Source code for schrodinger.ui.qt.appframework2.wizards

import enum

from schrodinger.Qt import QtCore
from schrodinger.Qt import QtWidgets
from schrodinger.ui.qt import basewidgets
from schrodinger.ui.qt.appframework2 import settings

ACTION = 'wizard_action'
Actions = enum.Enum('Action', ['Next', 'Back', 'Cancel'])


[docs]class BaseWizardPanel(settings.SettingsPanelMixin, basewidgets.BaseWidget): finished = QtCore.pyqtSignal()
[docs] def initSetOptions(self): super(BaseWizardPanel, self).initSetOptions() self.last_action = None self.panel_settings.append((ACTION, 'last_action'))
[docs] def initSetUp(self): super(BaseWizardPanel, self).initSetUp() self.prev_panel_state = None self.back_btn = QtWidgets.QPushButton('Back') self.next_btn = QtWidgets.QPushButton('Next') self.cancel_btn = QtWidgets.QPushButton('Cancel') self.back_btn.clicked.connect(self.onBackClicked) self.next_btn.clicked.connect(self.onNextClicked) self.cancel_btn.clicked.connect(self.onCancelClicked)
[docs] def initLayOut(self): super(BaseWizardPanel, self).initLayOut() self.button_layout = QtWidgets.QHBoxLayout() self.button_layout.addWidget(self.back_btn) self.button_layout.addStretch() self.button_layout.addWidget(self.next_btn) self.button_layout.addWidget(self.cancel_btn) self.bottom_layout.addLayout(self.button_layout)
[docs] def initSetDefaults(self): super(BaseWizardPanel, self).initSetDefaults() self._configurePanelSettings()
[docs] def onBackClicked(self): self.last_action = Actions.Back if self.processBack() is False: return self.close()
[docs] def onNextClicked(self): if self.processNext() is False: return # don't go to the next panel self.last_action = Actions.Next self.close()
[docs] def onCancelClicked(self): self.last_action = Actions.Cancel self.close()
[docs] def run(self, prev_panel_state=None): """ Runs the panel. If the panel is being invoked by clicking "Next" on the previous panel, pass in the previous panel's state so it can be processed. :param prev_panel_state: the previous panel's state :type prev_panel_state: settings.PanelState or None """ if prev_panel_state: self.processPrevPanel(prev_panel_state) self.prev_panel_state = prev_panel_state # If we exit the panel in any other way than hitting "Back" or "Next" # it will be treated as a "Cancel" by default. self.last_action = Actions.Cancel self.saved_settings = self.getSettings() super(BaseWizardPanel, self).run()
[docs] def closeEvent(self, event): super(BaseWizardPanel, self).closeEvent(event) if self.last_action == Actions.Cancel: self.applySettings(self.saved_settings) self.finished.emit()
#=========================================================================== # Main overrides - implement in child class #===========================================================================
[docs] def processPrevPanel(self, state): """ Override this method to receive the settings from the previous panel and processes them appropriately. :param state: the state from the previous panel :type state: settings.PanelState """
[docs] def processBack(self): """ Override this method to perform necessary actions when going back """
[docs] def processNext(self): """ Override this method to perform necessary actions when going to next A return value of False will stay in the current panel, any other value will continue to the next. """
[docs]class MultiPanelWizard(QtCore.QObject): """ The MultiPanelWizard allows a series of BaseWizardPanel instances to be chained together to create a wizard. It manages navigation between the panels as well as passing information from one panel to the next. This class can be instantiated and used directly, or it can be subclassed for more complex cases. """ finished = QtCore.pyqtSignal(object) _singleton = None
[docs] @classmethod def panel(cls, run=True): """ Launch a singleton instance of this class. If the wizard has already been instantiated, the existing wizard instance will be re-opened and brought to the front. :param run: Whether to launch the panel :type run: bool :return: The singleton panel instance :rtype: MultiPanelWizard """ if cls._singleton is None or not isinstance(cls._singleton, cls): # The isinstance check covers cases of panel inheritance cls._singleton = cls() if run: if cls._singleton.current_panel_index is None: cls._singleton.run() else: panel = cls._singleton.currentPanel() panel.show() panel.raise_() return cls._singleton
[docs] def __init__(self): super(MultiPanelWizard, self).__init__() self.panels = [] self.current_panel_index = None self.application = QtWidgets.QApplication.instance() if not self.application: self.application = QtWidgets.QApplication([]) self.setup()
[docs] def addPanel(self, panel): """ Add a panel to the wizard. Panels are chained in the order they are added. :param panel: the panel to be added to the wizard's chain :type panel: BaseWizardPanel """ self.panels.append(panel)
def _configurePanels(self): """ Adjusts the panels in the context of the chain - i.e. remove the "Back" button from the first panel and change the "Next" button on the last panel to "Finish". This should only be called after all the panels have been added. """ first_panel = self.panels[0] last_panel = self.panels[-1] first_panel.back_btn.setVisible(False) last_panel.next_btn.setText('Finish') for panel in self.panels: panel.finished.connect(self.processPanel)
[docs] def run(self): """ Run the wizard. """ self.current_panel_index = 0 self.last_panel_state = None self._configurePanels() self.startup() self.runCurrentPanel()
[docs] def currentPanel(self): """ Returns the current panel or None if the wizard has not been started. """ if self.current_panel_index is None: return None return self.panels[self.current_panel_index]
[docs] def runCurrentPanel(self): """ Runs the current panel. """ panel = self.currentPanel() panel.run(self.last_panel_state)
[docs] def processPanel(self): """ This method will get called whenever a panel is dismissed (via Next, Back, or Cancel) and depending on the action chosen, will update the current panel index to the appropriate value and run it or terminate the wizard. """ panel = self.currentPanel() if panel is None: return state = panel.getPanelState() action = panel.last_action if action == Actions.Next: self.current_panel_index += 1 self.last_panel_state = state elif action == Actions.Back: self.current_panel_index -= 1 self.last_panel_state = None if (action == Actions.Cancel or self.current_panel_index >= len(self.panels) or self.current_panel_index < 0): self.last_panel_state = None self.postProcess() self.finished.emit(self) return self.runCurrentPanel()
[docs] def quit(self): """ Exit the wizard """ self.currentPanel().onCancelClicked()
#=========================================================================== # Overrides #===========================================================================
[docs] def setup(self): """ If subclassing, override this method to add logic to be run during initialization, such as adding wizard panels. """
[docs] def startup(self): """ If subclassing, override this method to add logic to be run right before starting the wizard. """
[docs] def postProcess(self): """ If subclassing, override this method to add logic to be run after the wizard has run. This method will be called whether the user finishes the wizard or stops midway. To determine what to do, check the value of self.current_panel_index. """