Source code for spyder.api.shellconnect.main_widget

# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""
Main widget to use in plugins that show content that comes from the IPython
console, such as the Variable Explorer or Plots.
"""

# Third party imports
from qtpy.QtWidgets import QStackedWidget, QVBoxLayout

# Local imports
from spyder.api.translations import _
from spyder.api.widgets.main_widget import PluginMainWidget
from spyder.widgets.emptymessage import EmptyMessageWidget


class _ErroredMessageWidget(EmptyMessageWidget):
    """Widget to show when the kernel's shell failed to start."""

    def __init__(self, parent, shellwidget):
        # Initialize EmptyMessageWidget with the content we want to show for
        # errors
        super().__init__(
            parent,
            icon_filename=(
                "console-remote-off"
                if shellwidget.is_remote()
                else "console-off"
            ),
            text=_("No connected console"),
            description=_(
                "The current console has no active kernel, so there is no "
                "content to show here"
            ),
            adjust_on_resize=True,
        )

        # This is necessary to show this widget in case ShellConnectMainWidget
        # shows an empty message.
        self.is_empty = False


[docs] class ShellConnectMainWidget(PluginMainWidget): """ Main widget to use in a plugin that shows console-specific content. Notes ----- * This is composed of a QStackedWidget to stack widgets associated to each shell widget in the console and only show one of them at a time. * The current widget in the stack will display the content associated to the console with focus. """ def __init__(self, *args, set_layout=True, **kwargs): super().__init__(*args, **kwargs) # Widgets if not ( self.SHOW_MESSAGE_WHEN_EMPTY and self.get_conf( "show_message_when_panes_are_empty", section="main" ) ): self._stack = QStackedWidget(self) if set_layout: layout = QVBoxLayout() layout.addWidget(self._stack) self.setLayout(layout) self._shellwidgets = {} # ---- PluginMainWidget API # ------------------------------------------------------------------------
[docs] def current_widget(self): """ Return the current widget in the stack. Returns ------- QWidget The current widget. """ return self._content_widget
[docs] def get_focus_widget(self): return self._stack.currentWidget()
# ---- SpyderWidgetMixin API # ------------------------------------------------------------------------
[docs] def update_style(self): self._stack.setStyleSheet("QStackedWidget {padding: 0px; border: 0px}")
# ---- Stack accesors # ------------------------------------------------------------------------
[docs] def count(self): """ Return the number of widgets in the stack. Returns ------- int The number of widgets in the stack. """ return self._stack.count()
[docs] def get_widget_for_shellwidget(self, shellwidget): """return widget corresponding to shellwidget.""" shellwidget_id = id(shellwidget) if shellwidget_id in self._shellwidgets: return self._shellwidgets[shellwidget_id] return None
# ---- Public API # ------------------------------------------------------------------------
[docs] def add_shellwidget(self, shellwidget): """Create a new widget in the stack and associate it to shellwidget.""" shellwidget_id = id(shellwidget) if shellwidget_id not in self._shellwidgets: widget = self.create_new_widget(shellwidget) self._stack.addWidget(widget) self._shellwidgets[shellwidget_id] = widget # Add all actions to new widget for shortcuts to work. for __, action in self.get_actions().items(): if action: widget_actions = widget.actions() if action not in widget_actions: widget.addAction(action) self.set_shellwidget(shellwidget)
[docs] def remove_shellwidget(self, shellwidget): """Remove widget associated to shellwidget.""" shellwidget_id = id(shellwidget) if shellwidget_id in self._shellwidgets: widget = self._shellwidgets.pop(shellwidget_id) # If `widget` is an empty pane, we don't need to remove it from the # stack (because it's the one we need to show since the console is # showing an error) nor try to close it (because it makes no # sense). if not isinstance(widget, EmptyMessageWidget): self._stack.removeWidget(widget) self.close_widget(widget) self.update_actions()
[docs] def set_shellwidget(self, shellwidget): """Set widget associated with shellwidget as the current widget.""" old_widget = self.current_widget() widget = self.get_widget_for_shellwidget(shellwidget) if widget is None: return self.set_content_widget(widget, add_to_stack=False) if ( self.SHOW_MESSAGE_WHEN_EMPTY and self.get_conf( "show_message_when_panes_are_empty", section="main" ) and widget.is_empty ): self.show_empty_message() else: self.show_content_widget() self.switch_widget(widget, old_widget) self.update_actions()
[docs] def add_errored_shellwidget(self, shellwidget): """ Create a new _ErroredMessageWidget in the stack and associate it to shellwidget. This is necessary to show a meaningful message when switching to consoles with dead kernels. """ shellwidget_id = id(shellwidget) # This can happen if the kernel started without issues but something is # printed to its stderr stream, which we display as an error in the # console. In that case, we need to remove the current widget # associated to shellwidget and replace it by an empty one. if shellwidget_id in self._shellwidgets: self._shellwidgets.pop(shellwidget_id) widget = _ErroredMessageWidget(self, shellwidget) widget.set_visibility(self.is_visible) if self.dockwidget is not None: self.dockwidget.visibilityChanged.connect(widget.set_visibility) self.set_content_widget(widget) self._shellwidgets[shellwidget_id] = widget self.set_shellwidget(shellwidget)
[docs] def create_new_widget(self, shellwidget): """Create a widget to communicate with shellwidget.""" raise NotImplementedError
[docs] def close_widget(self, widget): """Close the widget.""" raise NotImplementedError
[docs] def switch_widget(self, widget, old_widget): """Switch the current widget.""" raise NotImplementedError
[docs] def refresh(self): """Refresh widgets.""" if self.count(): widget = self.current_widget() widget.refresh()
[docs] def is_current_widget_error_message(self): """Check if the current widget is showing an error message.""" return isinstance(self.current_widget(), _ErroredMessageWidget)
[docs] def switch_empty_message(self, value: bool): """Switch between the empty message widget or the one with content.""" if value: self.show_empty_message() else: self.show_content_widget()