class VariableExplorer(SpyderPluginWidget): """Variable Explorer plugin.""" CONF_SECTION = 'variable_explorer' CONFIGWIDGET_CLASS = VariableExplorerConfigPage DISABLE_ACTIONS_WHEN_HIDDEN = False INITIAL_FREE_MEMORY_TIME_TRIGGER = 60 * 1000 # ms SECONDARY_FREE_MEMORY_TIME_TRIGGER = 180 * 1000 # ms def __init__(self, parent): SpyderPluginWidget.__init__(self, parent) # Widgets self.stack = QStackedWidget(self) self.stack.setStyleSheet("QStackedWidget{padding: 0px; border: 0px}") self.shellwidgets = {} # Layout layout = QVBoxLayout() layout.addWidget(self.stack) self.setLayout(layout) def get_settings(self): """ Retrieve all Variable Explorer configuration settings. Specifically, return the settings in CONF_SECTION with keys in REMOTE_SETTINGS, and the setting 'dataframe_format'. Returns: dict: settings """ settings = {} for name in REMOTE_SETTINGS: settings[name] = self.get_option(name) # dataframe_format is stored without percent sign in config # to avoid interference with ConfigParser's interpolation name = 'dataframe_format' settings[name] = '%{0}'.format(self.get_option(name)) return settings @Slot(str, object) def change_option(self, option_name, new_value): """ Change a config option. This function is called if sig_option_changed is received. If the option changed is the dataframe format, then the leading '%' character is stripped (because it can't be stored in the user config). Then, the signal is emitted again, so that the new value is saved in the user config. """ if option_name == 'dataframe_format': assert new_value.startswith('%') new_value = new_value[1:] self.sig_option_changed.emit(option_name, new_value) @Slot() def free_memory(self): """Free memory signal.""" self.main.free_memory() QTimer.singleShot(self.INITIAL_FREE_MEMORY_TIME_TRIGGER, lambda: self.main.free_memory()) QTimer.singleShot(self.SECONDARY_FREE_MEMORY_TIME_TRIGGER, lambda: self.main.free_memory()) # ----- Stack accesors ---------------------------------------------------- def set_current_widget(self, nsb): self.stack.setCurrentWidget(nsb) # We update the actions of the options button (cog menu) and we move # it to the layout of the current widget. self._refresh_actions() nsb.setup_options_button() def current_widget(self): return self.stack.currentWidget() def count(self): return self.stack.count() def remove_widget(self, nsb): self.stack.removeWidget(nsb) def add_widget(self, nsb): self.stack.addWidget(nsb) # ----- Public API -------------------------------------------------------- def add_shellwidget(self, shellwidget): """ Register shell with variable explorer. This function opens a new NamespaceBrowser for browsing the variables in the shell. """ shellwidget_id = id(shellwidget) if shellwidget_id not in self.shellwidgets: self.options_button.setVisible(True) nsb = NamespaceBrowser(self, options_button=self.options_button) nsb.set_shellwidget(shellwidget) nsb.setup(**self.get_settings()) nsb.sig_option_changed.connect(self.change_option) nsb.sig_free_memory.connect(self.free_memory) self.add_widget(nsb) self.shellwidgets[shellwidget_id] = nsb self.set_shellwidget_from_id(shellwidget_id) return nsb def remove_shellwidget(self, shellwidget_id): # If shellwidget_id is not in self.shellwidgets, it simply means # that shell was not a Python-based console (it was a terminal) if shellwidget_id in self.shellwidgets: nsb = self.shellwidgets.pop(shellwidget_id) self.remove_widget(nsb) nsb.close() def set_shellwidget_from_id(self, shellwidget_id): if shellwidget_id in self.shellwidgets: nsb = self.shellwidgets[shellwidget_id] self.set_current_widget(nsb) def import_data(self, fname): """Import data in current namespace""" if self.count(): nsb = self.current_widget() nsb.refresh_table() nsb.import_data(filenames=fname) if self.dockwidget: self.switch_to_plugin() #------ SpyderPluginWidget API --------------------------------------------- def get_plugin_title(self): """Return widget title""" return _('Variable explorer') def get_plugin_icon(self): """Return plugin icon""" return ima.icon('dictedit') def get_focus_widget(self): """ Return the widget to give focus to when this plugin's dockwidget is raised on top-level """ return self.current_widget() def get_plugin_actions(self): """Return a list of actions related to plugin""" return self.current_widget().actions if self.current_widget() else [] def apply_plugin_settings(self, options): """Apply configuration file's plugin settings""" for nsb in list(self.shellwidgets.values()): nsb.setup(**self.get_settings())
class Plots(SpyderPluginWidget): """Plots plugin.""" CONF_SECTION = 'plots' CONF_FILE = False DISABLE_ACTIONS_WHEN_HIDDEN = False def __init__(self, parent): SpyderPluginWidget.__init__(self, parent) # Widgets self.stack = QStackedWidget(self) self.stack.setStyleSheet("QStackedWidget{padding: 0px; border: 0px}") self.shellwidgets = {} # Layout layout = QGridLayout(self) layout.addWidget(self.stack) def get_settings(self): """Retrieve all Plots configuration settings.""" return { name: self.get_option(name) for name in ['mute_inline_plotting', 'show_plot_outline', 'auto_fit_plotting'] } # ---- Stack accesors def set_current_widget(self, fig_browser): """ Set the currently visible fig_browser in the stack widget, refresh the actions of the cog menu button and move it to the layout of the new fig_browser. """ self.stack.setCurrentWidget(fig_browser) # We update the actions of the options button (cog menu) and # we move it to the layout of the current widget. self._refresh_actions() fig_browser.setup_options_button() def current_widget(self): return self.stack.currentWidget() def count(self): return self.stack.count() def remove_widget(self, fig_browser): self.stack.removeWidget(fig_browser) def add_widget(self, fig_browser): self.stack.addWidget(fig_browser) # ---- Public API def add_shellwidget(self, shellwidget): """ Register shell with figure explorer. This function opens a new FigureBrowser for browsing the figures in the shell. """ shellwidget_id = id(shellwidget) if shellwidget_id not in self.shellwidgets: self.options_button.setVisible(True) fig_browser = FigureBrowser(self, options_button=self.options_button, background_color=MAIN_BG_COLOR) fig_browser.set_shellwidget(shellwidget) fig_browser.setup(**self.get_settings()) fig_browser.sig_option_changed.connect( self.sig_option_changed.emit) fig_browser.thumbnails_sb.redirect_stdio.connect( self.main.redirect_internalshell_stdio) self.register_widget_shortcuts(fig_browser) self.add_widget(fig_browser) self.shellwidgets[shellwidget_id] = fig_browser self.set_shellwidget_from_id(shellwidget_id) return fig_browser def remove_shellwidget(self, shellwidget_id): # If shellwidget_id is not in self.shellwidgets, it simply means # that shell was not a Python-based console (it was a terminal) if shellwidget_id in self.shellwidgets: fig_browser = self.shellwidgets.pop(shellwidget_id) self.remove_widget(fig_browser) fig_browser.close() def set_shellwidget_from_id(self, shellwidget_id): if shellwidget_id in self.shellwidgets: fig_browser = self.shellwidgets[shellwidget_id] self.set_current_widget(fig_browser) # ---- SpyderPluginWidget API def get_plugin_title(self): """Return widget title""" return _('Plots') def get_plugin_icon(self): """Return plugin icon""" return ima.icon('hist') def get_focus_widget(self): """ Return the widget to give focus to when this plugin's dockwidget is raised on top-level """ return self.current_widget() def get_plugin_actions(self): """Return a list of actions related to plugin""" return self.current_widget().actions if self.current_widget() else [] def apply_plugin_settings(self, options): """Apply configuration file's plugin settings""" for fig_browser in list(self.shellwidgets.values()): fig_browser.setup(**self.get_settings()) def on_first_registration(self): """Action to be performed on first plugin registration""" self.tabify(self.main.variableexplorer)
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, **kwargs): super().__init__(*args, **kwargs) # Widgets self._stack = QStackedWidget(self) self._shellwidgets = {} # Layout layout = QVBoxLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._stack) self.setLayout(layout) def update_style(self): self._stack.setStyleSheet("QStackedWidget {padding: 0px; border: 0px}") # ---- Stack accesors # ------------------------------------------------------------------------ def count(self): """ Return the number of widgets in the stack. Returns ------- int The number of widgets in the stack. """ return self._stack.count() def current_widget(self): """ Return the current figure browser widget in the stack. Returns ------- QWidget The current widget. """ return self._stack.currentWidget() def get_focus_widget(self): return self.current_widget() # ---- Public API # ------------------------------------------------------------------------ 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) 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) self._stack.removeWidget(widget) self.close_widget(widget) self.update_actions() def set_shellwidget(self, shellwidget): """ Set widget associated with shellwidget as the current widget. """ shellwidget_id = id(shellwidget) old_widget = self.current_widget() if shellwidget_id in self._shellwidgets: widget = self._shellwidgets[shellwidget_id] self._stack.setCurrentWidget(widget) self.switch_widget(widget, old_widget) self.update_actions() def create_new_widget(self, shellwidget): """Create a widget to communicate with shellwidget.""" raise NotImplementedError def close_widget(self, widget): """Close the widget.""" raise NotImplementedError def switch_widget(self, widget, old_widget): """Switch the current widget.""" raise NotImplementedError def refresh(self): """Refresh widgets.""" if self.count(): widget = self.current_widget() widget.refresh()