class StackedPage(QWidget): def __init__(self, parent = None): super(StackedPage, self).__init__(parent) layout = QHBoxLayout(self) leftpane = QVBoxLayout() self.buttongroup = QButtonGroup(self) self.buttongroup.setExclusive(True) self.groupbox = QGroupBox(self) self.groupbox.setMinimumWidth(200) QVBoxLayout(self.groupbox) leftpane.addWidget(self.groupbox) leftpane.addStretch(1) layout.addLayout(leftpane) self.rightpane = QStackedWidget(self) layout.addWidget(self.rightpane) self.buttongroup.buttonClicked[int].connect(self.rightpane.setCurrentIndex) self.rightpane.currentChanged[int].connect(self.activate) def addPage(self, buttontext, widget): button = QPushButton(buttontext) button.setCheckable(True) button.setChecked(self.rightpane.count() == 0) self.buttongroup.addButton(button, self.rightpane.count()) self.groupbox.layout().addWidget(button) self.rightpane.addWidget(widget) def pages(self): return [ self.rightpane.widget(i) for i in range(self.rightpane.count()) ] def activate(self, ix): page = self.rightpane.currentWidget() if hasattr(page, "activate") and callable(page.activate): page.activate() def showEvent(self, *args, **kwargs): self.activate(0) return QWidget.showEvent(self, *args, **kwargs)
class GeometryWizardPage(_ExpandableOptionsWizardPage): def __init__(self, options, parent=None): _ExpandableOptionsWizardPage.__init__(self, options, parent) self.setTitle('Geometry') def _initUI(self): # Variables self._widgets = {} # Widgets self._cb_geometry = QComboBox() self._wdg_geometry = QStackedWidget() policy = self._wdg_geometry.sizePolicy() policy.setVerticalStretch(True) self._wdg_geometry.setSizePolicy(policy) # Layouts layout = _ExpandableOptionsWizardPage._initUI(self) layout.addRow("Type of geometry", self._cb_geometry) layout.addRow(self._wdg_geometry) # Signals self._cb_geometry.currentIndexChanged.connect(self._onGeometryChanged) self._cb_geometry.currentIndexChanged.connect(self.valueChanged) return layout def _onGeometryChanged(self): newindex = self._cb_geometry.currentIndex() oldwidget = self._wdg_geometry.currentWidget() newwidget = self._wdg_geometry.widget(newindex) if newwidget is None: return try: newwidget.setValue(oldwidget.value()) except: newwidget.setValue(self.options().geometry) self._wdg_geometry.setCurrentIndex(newindex) # See https://qt-project.org/faq/answer/how_can_i_get_a_qstackedwidget_to_automatically_switch_size_depending_on_th oldwidget.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) newwidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._wdg_geometry.adjustSize() def _find_material_class(self, programs): highest_class = Material for program in programs: converter = program.converter_class for clasz in converter.MATERIALS: if issubclass(clasz, highest_class): highest_class = clasz return highest_class def initializePage(self): _ExpandableOptionsWizardPage.initializePage(self) # Clear self._widgets.clear() for i in reversed(range(self._cb_geometry.count())): self._cb_geometry.removeItem(i) self._wdg_geometry.removeWidget(self._wdg_geometry.widget(i)) # Populate combo box it = self._iter_widgets('pymontecarlo.ui.gui.options.geometry', 'GEOMETRIES') for clasz, widget_class, programs in it: widget = widget_class() widget.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) widget.setMaterialClass(self._find_material_class(programs)) self._widgets[clasz] = widget program_text = ', '.join(map(attrgetter('name'), programs)) text = '{0} ({1})'.format(widget.accessibleName(), program_text) self._cb_geometry.addItem(text) self._wdg_geometry.addWidget(widget) widget.valueChanged.connect(self.valueChanged) # Select geometry geometry = self.options().geometry widget = self._widgets.get(geometry.__class__) if widget is None: widget = next(iter(self._widgets.values())) widget.setValue(geometry) self._wdg_geometry.setCurrentWidget(widget) self._cb_geometry.setCurrentIndex(self._wdg_geometry.currentIndex()) def validatePage(self): if not self._wdg_geometry.currentWidget().hasAcceptableInput(): return False self.options().geometry = self._wdg_geometry.currentWidget().value() return True def expandCount(self): try: return len(expand(self._wdg_geometry.currentWidget().value())) except: return 0
class BeamWizardPage(_ExpandableOptionsWizardPage): def __init__(self, options, parent=None): _ExpandableOptionsWizardPage.__init__(self, options, parent) self.setTitle("Beam") def _initUI(self): # Variables self._widgets = {} # Widgets self._cb_beam = QComboBox() self._wdg_beam = QStackedWidget() # Layouts layout = _ExpandableOptionsWizardPage._initUI(self) layout.addRow("Type of beam", self._cb_beam) layout.addRow(self._wdg_beam) # Signals self._cb_beam.currentIndexChanged.connect(self._onBeamChanged) self._cb_beam.currentIndexChanged.connect(self.valueChanged) return layout def _onBeamChanged(self): newindex = self._cb_beam.currentIndex() oldwidget = self._wdg_beam.currentWidget() newwidget = self._wdg_beam.widget(newindex) if newwidget is None: return try: newwidget.setValue(oldwidget.value()) except: newwidget.setValue(self.options().beam) self._wdg_beam.setCurrentIndex(newindex) def initializePage(self): _ExpandableOptionsWizardPage.initializePage(self) # Clear self._widgets.clear() for i in reversed(range(self._cb_beam.count())): self._cb_beam.removeItem(i) self._wdg_beam.removeWidget(self._wdg_beam.widget(i)) # Populate combo box it = self._iter_widgets("pymontecarlo.ui.gui.options.beam", "BEAMS") for clasz, widget_class, programs in it: widget = widget_class() self._widgets[clasz] = widget program_text = ", ".join(map(attrgetter("name"), programs)) text = "{0} ({1})".format(widget.accessibleName(), program_text) self._cb_beam.addItem(text) self._wdg_beam.addWidget(widget) widget.setParticlesEnabled(False) for program in programs: converter = program.converter_class for particle in converter.PARTICLES: widget.setParticleEnabled(particle, True) widget.valueChanged.connect(self.valueChanged) # Select beam beam = self.options().beam widget = self._widgets.get(beam.__class__) if widget is None: widget = next(iter(self._widgets.values())) widget.setValue(beam) self._wdg_beam.setCurrentWidget(widget) self._cb_beam.setCurrentIndex(self._wdg_beam.currentIndex()) def validatePage(self): if not self._wdg_beam.currentWidget().hasAcceptableInput(): return False self.options().beam = self._wdg_beam.currentWidget().value() return True def expandCount(self): try: return len(expand(self._wdg_beam.currentWidget().value())) except: return 0
class MainWindow(QMainWindow, Ui_MainWindow): """Main interface""" # Custom Signals dock_destroyed = Signal(str) def __init__(self, user_data, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.set_username(user_data[0]) self._docks = [] self._stackedWidget = QStackedWidget() self.setCentralWidget(self._stackedWidget) self._overview = OverviewDock() self._stackedWidget.addWidget(self._overview) self._edit_mode = False self._stackedWidget.currentChanged.connect(self.dock_changed) # permission stuff self.set_access(user_data[1]) def dock_changed(self, new_index): new_dock = self._stackedWidget.widget(new_index).objectName() if "Edit" in new_dock: # it's an editing dock self.set_editing(True) def set_editing(self, state): self._edit_mode = state def is_editing(self): return self._edit_mode def set_username(self, username): statics.username = username message = QLabel(unicode("Usuário: ".decode('utf-8')) + username) self.statusbar.addWidget(message) def set_access(self, access_level): statics.access_level = access_level # set availability for QAction objects for action in self.findChildren(QAction): action.setVisible(check_access(access_level, action.objectName())) for menu in self.findChildren(QMenu): if menu.isEmpty(): # no visible actions under this menu menu.menuAction().setVisible(False) @QtCore.Slot() def on_actionExit_activated(self): self.close() @QtCore.Slot() def on_actionAddBook_activated(self): self.show_on_top(AddBookDock, self.actionAddBook) @QtCore.Slot() def on_actionAddAssociate_activated(self): self.show_on_top(AddAssociateDock, self.actionAddAssociate) @QtCore.Slot() def on_actionAddActivity_activated(self): self.show_on_top(AddActivityDock, self.actionAddActivity) @QtCore.Slot() def on_actionPendencies_activated(self): self.show_on_top(PendenciesDock, self.actionPendencies) @QtCore.Slot() def on_actionSellProduct_activated(self): self.show_on_top(OrderProductDock, self.actionSellProduct) @QtCore.Slot() def on_actionSellBook_activated(self): self.show_on_top(OrderBookDock, self.actionSellBook) def show_on_top(self, widget_type, related_action=None, param=None): """ makes the dock related to 'widget_type' the central widget of the mainwindow (by stacking it on front of other widgets), grays out respective action while visible """ if self.is_editing(): # edit dock is being swapped out message = unicode("Todos os dados não salvos serão perdidos".decode('utf-8')) reply = QMessageBox.warning(self, unicode("Atenção".decode('utf-8')), message, QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.No: return else: self.remove_instance(self._stackedWidget.currentWidget()) # there's a need to gray out the corresponding menu option if related_action: related_action.setDisabled(True) widget = self.get_instance(widget_type) if widget is None: # create a new instance of referenced widget if it doesn't exist yet if param: # when a parameter is passed widget = widget_type(param) else: widget = widget_type() self._docks.append(widget) self._stackedWidget.addWidget(widget) # else: # widget.clear() # connects a connection cleanup method when needed # if hasattr(widget, 'cleanup_conn'): # widget.cleanup_conn.connect(self.clean_connection) self._stackedWidget.setCurrentWidget(widget) def get_instance(self, type): for inst in self._docks: if isinstance(inst, type): return inst return None def remove_instance(self, inst): if self.is_editing(): self.set_editing(False) self._docks.remove(inst) self._stackedWidget.removeWidget(inst) self._stackedWidget.setCurrentWidget(self._overview) self._overview.refresh() def closeEvent(self, event): message = unicode("Deseja mesmo sair?".decode('utf-8')) reply = QMessageBox.question(self, 'Seareiros', message, QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore()