class _OptionsSelector(QDialog): def __init__(self, list_options, parent=None): QDialog.__init__(self, parent) # Variables model = _AvailableOptionsListModel() for options in list_options: model.addOptions(options) # Widgets lbltext = QLabel('Select the options to import') self._combobox = QComboBox() self._combobox.setModel(model) buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) # Layouts layout = QVBoxLayout() layout.addWidget(lbltext) layout.addWidget(self._combobox) layout.addWidget(buttons) self.setLayout(layout) # Signals buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) def options(self): return self._combobox.model().options(self._combobox.currentIndex())
class LimitWizardPage(_ExpandableOptionsWizardPage): class _LimitComboBoxModel(QAbstractListModel): def __init__(self, limits_text=None): QAbstractListModel.__init__(self) if limits_text is None: limits_text = {} self._limits_text = limits_text.copy() self._limits = list(limits_text.keys()) def rowCount(self, *args, **kwargs): return len(self._limits) def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not (0 <= index.row() < self.rowCount()): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role != Qt.DisplayRole: return None limit_class = self._limits[index.row()] return self._limits_text[limit_class] def add(self, limit_class): if limit_class not in self._limits_text: raise ValueError('No text defined for limit: %s' % limit_class) self._limits.append(limit_class) self.reset() def remove(self, limit_class): self._limits.remove(limit_class) self.reset() def limitClass(self, index): return self._limits[index] class _LimitTableModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self._limits = [] def rowCount(self, *args, **kwargs): return len(self._limits) def columnCount(self, *args, **kwargs): return 1 def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not (0 <= index.row() < len(self._limits)): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role == Qt.DisplayRole or role == Qt.ToolTipRole: limit = self._limits[index.row()] return str(limit) if limit is not None else '' return None def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Vertical: return str(section + 1) def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable) def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or \ not (0 <= index.row() < len(self._limits)): return False row = index.row() self._limits[row] = value self.dataChanged.emit(index, index) return True def insertRows(self, row, count=1, parent=None): if parent is None: parent = QModelIndex() self.beginInsertRows(parent, row, row + count - 1) for _ in range(count): self._limits.insert(row, None) self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if parent is None: parent = QModelIndex() self.beginRemoveRows(parent, row, row + count - 1) for index in reversed(range(row, row + count)): self._limits.pop(index) self.endRemoveRows() return True def append(self, limit): self.insert(self.rowCount(), limit) def insert(self, index, limit): self.insertRows(index) self.setData(self.createIndex(index, 0), limit) def remove(self, limit): index = self._limits.index(limit) self.removeRows(index) def modify(self, index, limit): self.setData(self.createIndex(index, 0), limit) def clear(self): self.removeRows(0, self.rowCount()) def limits(self): limits = set(self._limits) limits.discard(None) return limits def limit(self, index): return self._limits[index.row()] class _LimitTableDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): return None def setEditorData(self, editor, index): pass def setModelData(self, editor, model, index): return None def __init__(self, options, parent=None): _ExpandableOptionsWizardPage.__init__(self, options, parent) self.setTitle('Limit') def _initUI(self): # Variables self._widgets = {} tbl_model = self._LimitTableModel() # Widgets self._cb_limit = QComboBox() self._cb_limit.setModel(self._LimitComboBoxModel()) btn_limit_add = QPushButton() btn_limit_add.setIcon(getIcon("list-add")) self._tbl_limit = QTableView() self._tbl_limit.setModel(tbl_model) self._tbl_limit.setItemDelegate(self._LimitTableDelegate()) header = self._tbl_limit.horizontalHeader() header.setResizeMode(QHeaderView.Stretch) header.hide() policy = self._tbl_limit.sizePolicy() policy.setVerticalStretch(True) self._tbl_limit.setSizePolicy(policy) tlb_limit = QToolBar() spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) tlb_limit.addWidget(spacer) act_remove = tlb_limit.addAction(getIcon("list-remove"), "Remove limit") act_clear = tlb_limit.addAction(getIcon("edit-clear"), "Clear") # Layouts layout = _ExpandableOptionsWizardPage._initUI(self) sublayout = QHBoxLayout() sublayout.addWidget(self._cb_limit, 1) sublayout.addWidget(btn_limit_add) layout.addRow("Select", sublayout) layout.addRow(self._tbl_limit) layout.addRow(tlb_limit) # Signals btn_limit_add.released.connect(self._onLimitAdd) act_remove.triggered.connect(self._onLimitRemove) act_clear.triggered.connect(self._onLimitClear) self._tbl_limit.doubleClicked.connect(self._onLimitDoubleClicked) tbl_model.dataChanged.connect(self.valueChanged) tbl_model.rowsInserted.connect(self.valueChanged) tbl_model.rowsRemoved.connect(self.valueChanged) return layout def _onLimitAdd(self): tbl_model = self._tbl_limit.model() cb_model = self._cb_limit.model() index = self._cb_limit.currentIndex() try: limit_class = cb_model.limitClass(index) except IndexError: return widget_class = self._widgets[limit_class] wdg_limit = widget_class() dialog = _LimitDialog(wdg_limit) if not dialog.exec_(): return limit = dialog.limit() tbl_model.append(limit) # Insert table row cb_model.remove(limit.__class__) # Remove limit from combo box def _onLimitRemove(self): selection = self._tbl_limit.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Limit", "Select a row") return tbl_model = self._tbl_limit.model() cb_model = self._cb_limit.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): limit = tbl_model.limit(tbl_model.createIndex(row, 0)) cb_model.add(limit.__class__) # Show limit to combo box tbl_model.removeRow(row) # Remove row if self._cb_limit.currentIndex() < 0: self._cb_limit.setCurrentIndex(0) def _onLimitClear(self): tbl_model = self._tbl_limit.model() cb_model = self._cb_limit.model() for row in reversed(range(tbl_model.rowCount())): limit = tbl_model.limit(tbl_model.createIndex(row, 0)) cb_model.add(limit.__class__) # Show limit to combo box tbl_model.removeRow(row) # Remove row if self._cb_limit.currentIndex() < 0: self._cb_limit.setCurrentIndex(0) def _onLimitDoubleClicked(self, index): tbl_model = self._tbl_limit.model() limit = tbl_model.limit(index) widget_class = self._widgets[limit.__class__] wdg_limit = widget_class() wdg_limit.setValue(limit) dialog = _LimitDialog(wdg_limit) if not dialog.exec_(): return tbl_model.modify(index.row(), dialog.limit()) def initializePage(self): _ExpandableOptionsWizardPage.initializePage(self) # Clear self._widgets.clear() limits_text = {} # Populate combo box it = self._iter_widgets('pymontecarlo.ui.gui.options.limit', 'LIMITS') for limit_class, widget_class, programs in it: widget = widget_class() self._widgets[limit_class] = widget_class program_text = ', '.join(map(attrgetter('name'), programs)) text = '{0} ({1})'.format(widget.accessibleName(), program_text) limits_text[limit_class] = text del widget cb_model = self._LimitComboBoxModel(limits_text) self._cb_limit.setModel(cb_model) self._cb_limit.setCurrentIndex(0) # Add limit(s) tbl_model = self._tbl_limit.model() tbl_model.clear() for limit in self.options().limits: tbl_model.append(limit) def validatePage(self): tbl_model = self._tbl_limit.model() self.options().limits.clear() for limit in tbl_model.limits(): if not limit.__class__ in self._widgets: continue self.options().limits.add(limit) return True def expandCount(self): if self._tbl_limit.model().rowCount() == 0: return 0 try: count = 1 for limit in self._tbl_limit.model().limits(): count *= len(expand(limit)) return count except: return 0
class _PhotonDistributionResultOptionsToolItem(_ResultToolItem): def _initUI(self): # Variables result = self.result() transitions = sorted(result.iter_transitions()) transition0 = transitions[0] model = _TransitionListModel(transitions) # Widgets self._chk_errorbar = QCheckBox("Show error bars") self._chk_errorbar.setChecked(True) self._cb_transition = QComboBox() self._cb_transition.setModel(model) self._cb_transition.setCurrentIndex(0) self._chk_pg = QCheckBox("No absorption, no fluorescence") state = result.exists(transition0, True, False, False, False) self._chk_pg.setEnabled(state) self._chk_pg.setChecked(state) self._chk_eg = QCheckBox("With absorption, no fluorescence") state = result.exists(transition0, True, True, False, False) self._chk_eg.setEnabled(state) self._chk_eg.setChecked(state) self._chk_pt = QCheckBox("No absorption, with fluorescence") state = result.exists(transition0, True, False, True, True) self._chk_pt.setEnabled(state) self._chk_pt.setChecked(state) self._chk_et = QCheckBox("With absorption, with fluorescence") state = result.exists(transition0, True, True, True, True) self._chk_et.setEnabled(state) self._chk_et.setChecked(state) # Layouts layout = _ResultToolItem._initUI(self) layout.addRow(self._chk_errorbar) layout.addRow("Transition", self._cb_transition) boxlayout = QVBoxLayout() boxlayout.addWidget(self._chk_pg) boxlayout.addWidget(self._chk_eg) boxlayout.addWidget(self._chk_pt) boxlayout.addWidget(self._chk_et) box_generated = QGroupBox("Curves") box_generated.setLayout(boxlayout) layout.addRow(box_generated) # Signals self._cb_transition.currentIndexChanged.connect(self._onTransitionChanged) self._chk_pg.stateChanged.connect(self.stateChanged) self._chk_eg.stateChanged.connect(self.stateChanged) self._chk_pt.stateChanged.connect(self.stateChanged) self._chk_et.stateChanged.connect(self.stateChanged) self._chk_errorbar.stateChanged.connect(self.stateChanged) return layout def _onTransitionChanged(self): result = self.result() index = self._cb_transition.currentIndex() transition = self._cb_transition.model().transition(index) self._chk_pg.setEnabled(result.exists(transition, True, False, False, False)) self._chk_eg.setEnabled(result.exists(transition, True, True, False, False)) self._chk_pt.setEnabled(result.exists(transition, True, False, True, True)) self._chk_et.setEnabled(result.exists(transition, True, True, True, True)) self.stateChanged.emit() def transition(self): index = self._cb_transition.currentIndex() return self._cb_transition.model().transition(index) def showConditions(self): return ( self._chk_pg.isChecked() and self._chk_pg.isEnabled(), self._chk_eg.isChecked() and self._chk_eg.isEnabled(), self._chk_pt.isChecked() and self._chk_pt.isEnabled(), self._chk_et.isChecked() and self._chk_et.isEnabled(), ) def showErrorbar(self): return self._chk_errorbar.isChecked()
def __init__(self, results, result_key, parameter_getters, x_parameter_name, series=None, parent=None): QDialog.__init__(self, parent) # Variables self._results = results self._result_key = result_key self._parameter_getters = parameter_getters options = results.options # Widgets self._txt_name = QLineEdit() self._cb_parameters = {} for name, getter in parameter_getters.items(): if name == x_parameter_name: continue combobox = QComboBox() values = np.array(getter(options), ndmin=1) combobox.setModel(_ValuesModel(values)) self._cb_parameters[name] = combobox self._cb_summary_key = QComboBox() self._cb_summary_key.setModel(_ValuesModel([])) buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) # Layouts layout = QFormLayout() if sys.platform == 'darwin': # Fix for Mac OS layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow) layout.addRow('Name', self._txt_name) for name, combobox in self._cb_parameters.items(): layout.addRow(name, combobox) layout.addRow('Summary variable', self._cb_summary_key) layout.addRow(buttons) self.setLayout(layout) # Signals buttons.accepted.connect(self._onOk) buttons.rejected.connect(self.reject) for combobox in self._cb_parameters.values(): combobox.currentIndexChanged.connect(self._onParameterChanged) # Defaults if series is not None: self._txt_name.setText(series.name) for name, _, value in series.conditions: combobox = self._cb_parameters[name] index = combobox.model().valueIndex(value) combobox.setCurrentIndex(index) self._onParameterChanged() index = self._cb_summary_key.model().valueIndex(series.summary_key) self._cb_summary_key.setCurrentIndex(index) else: self._onParameterChanged()
class _SeriesDialog(QDialog): def __init__(self, results, result_key, parameter_getters, x_parameter_name, series=None, parent=None): QDialog.__init__(self, parent) # Variables self._results = results self._result_key = result_key self._parameter_getters = parameter_getters options = results.options # Widgets self._txt_name = QLineEdit() self._cb_parameters = {} for name, getter in parameter_getters.items(): if name == x_parameter_name: continue combobox = QComboBox() values = np.array(getter(options), ndmin=1) combobox.setModel(_ValuesModel(values)) self._cb_parameters[name] = combobox self._cb_summary_key = QComboBox() self._cb_summary_key.setModel(_ValuesModel([])) buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) # Layouts layout = QFormLayout() if sys.platform == 'darwin': # Fix for Mac OS layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow) layout.addRow('Name', self._txt_name) for name, combobox in self._cb_parameters.items(): layout.addRow(name, combobox) layout.addRow('Summary variable', self._cb_summary_key) layout.addRow(buttons) self.setLayout(layout) # Signals buttons.accepted.connect(self._onOk) buttons.rejected.connect(self.reject) for combobox in self._cb_parameters.values(): combobox.currentIndexChanged.connect(self._onParameterChanged) # Defaults if series is not None: self._txt_name.setText(series.name) for name, _, value in series.conditions: combobox = self._cb_parameters[name] index = combobox.model().valueIndex(value) combobox.setCurrentIndex(index) self._onParameterChanged() index = self._cb_summary_key.model().valueIndex(series.summary_key) self._cb_summary_key.setCurrentIndex(index) else: self._onParameterChanged() def _onParameterChanged(self): summary_keys = set() for container in self._results: match = True for name, expected in self.parameterValue().items(): getter = self._parameter_getters[name] actual = getter(container.options) if actual != expected: match = False break if not match: continue try: result = container[self._result_key] except KeyError: continue summary_keys.update(result.get_summary().keys()) self._cb_summary_key.setModel(_ValuesModel(summary_keys)) def _onOk(self): if self._cb_summary_key.currentIndex() < 0: return self.accept() def name(self): name = self._txt_name.text().strip() if not name: parts = [] for param_name, value in self.parameterValue().items(): parts.append('%s=%s' % (param_name, value)) parts.append('summary=%s' % self.summaryKey()) name = '+'.join(parts) return name def parameterValue(self): parameter_value = {} for name, combobox in self._cb_parameters.items(): try: value = combobox.model().value(combobox.currentIndex()) parameter_value[name] = value except IndexError: continue return parameter_value def summaryKey(self): return self._cb_summary_key.model().value(self._cb_summary_key.currentIndex())
class TransferPanel(QWidget): ''' Transfer Panel This Panel is the main dialog box for the Dive Computer Transfer GUI ''' def __init__(self, parent=None): super(TransferPanel, self).__init__(parent) self._logbook = None self._logbookName = 'None' self._logbookPath = None self._createLayout() self._readSettings() self.setWindowTitle(self.tr('DC Transfer - %s') % self._logbookName) def _createLayout(self): 'Create the Widget Layout' self._txtLogbook = QLineEdit() self._txtLogbook.setReadOnly(True) self._lblLogbook = QLabel(self.tr('&Logbook File:')) self._lblLogbook.setBuddy(self._txtLogbook) self._btnBrowse = QPushButton('...') self._btnBrowse.clicked.connect(self._btnBrowseClicked) self._btnBrowse.setStyleSheet('QPushButton { min-width: 24px; max-width: 24px; }') self._btnBrowse.setToolTip(self.tr('Browse for a Logbook')) self._cbxComputer = QComboBox() self._lblComputer = QLabel(self.tr('Dive &Computer:')) self._lblComputer.setBuddy(self._cbxComputer) self._btnAddComputer = QPushButton(QPixmap(':/icons/list-add.png'), self.tr('')) self._btnAddComputer.setStyleSheet('QPushButton { min-width: 24px; min-height: 24; max-width: 24px; max-height: 24; }') self._btnAddComputer.clicked.connect(self._btnAddComputerClicked) self._btnRemoveComputer = QPushButton(QPixmap(':/icons/list-remove.png'), self.tr('')) self._btnRemoveComputer.setStyleSheet('QPushButton { min-width: 24px; min-height: 24; max-width: 24px; max-height: 24; }') self._btnRemoveComputer.clicked.connect(self._btnRemoveComputerClicked) hbox = QHBoxLayout() hbox.addWidget(self._btnAddComputer) hbox.addWidget(self._btnRemoveComputer) gbox = QGridLayout() gbox.addWidget(self._lblLogbook, 0, 0) gbox.addWidget(self._txtLogbook, 0, 1) gbox.addWidget(self._btnBrowse, 0, 2) gbox.addWidget(self._lblComputer, 1, 0) gbox.addWidget(self._cbxComputer, 1, 1) gbox.addLayout(hbox, 1, 2) gbox.setColumnStretch(1, 1) self._pbTransfer = QProgressBar() self._pbTransfer.reset() self._txtStatus = QTextEdit() self._txtStatus.setReadOnly(True) self._btnTransfer = QPushButton(self.tr('&Transfer Dives')) self._btnTransfer.clicked.connect(self._btnTransferClicked) self._btnExit = QPushButton(self.tr('E&xit')) self._btnExit.clicked.connect(self.close) hbox = QHBoxLayout() hbox.addWidget(self._btnTransfer) hbox.addStretch() hbox.addWidget(self._btnExit) vbox = QVBoxLayout() vbox.addLayout(gbox) vbox.addWidget(self._pbTransfer) vbox.addWidget(self._txtStatus) vbox.addLayout(hbox) self.setLayout(vbox) def _closeLogbook(self): 'Close the current Logbook' if self._logbook is None: return self._logbook = None self._logbookName = 'None' self._logbookPath = None self._txtLogbook.clear() self._cbxComputer.setModel(None) self._writeSettings() self.setWindowTitle(self.tr('DC Transfer - %s') % self._logbookName) def _openLogbook(self, path): 'Open an existing Logbook' if self._logbook is not None: self._closeLogbook() if not os.path.exists(path): QMessageBox.critical(self, self.tr('Missing Logbook'), self.tr('Logbook File "%s" does not exist.') % path) return #TODO: Handle a Schema Upgrade in a user-friendly manner self._logbook = Logbook(path, auto_update=False) self._logbookName = os.path.basename(path) self._logbookPath = path self._txtLogbook.setText(self._logbookPath) self._cbxComputer.setModel(DiveComputersModel(self._logbook)) self._writeSettings() self.setWindowTitle(self.tr('DC Transfer - %s') % self._logbookName) def _readSettings(self): 'Read main window settings from the configuration' settings = QSettings() settings.beginGroup('MainWindow') max = settings.value('max') size = settings.value('size') pos = settings.value('pos') file = settings.value('file') settings.endGroup() # Size and Position the Main Window if size is not None: self.resize(size) if pos is not None: self.move(pos) # HAX because QVariant is not exposed in PySide and the default # coercion to string is just stupid if max is not None and (max == 'true'): self.showMaximized() # Open the Logbook if file is not None: self._openLogbook(file) def _writeSettings(self): 'Write settings to the configuration' settings = QSettings() settings.beginGroup('MainWindow') settings.setValue('pos', self.pos()) settings.setValue('size', self.size()) settings.setValue('max', self.isMaximized()) settings.setValue('file', self._logbookPath) settings.endGroup() def closeEvent(self, e): 'Intercept an OnClose event' self._writeSettings() e.accept() #-------------------------------------------------------------------------- # Slots @QtCore.Slot() def _btnAddComputerClicked(self): 'Add a Dive Computer' dc = AddDiveComputerWizard.RunWizard(self) if dc is not None: self._logbook.session.add(dc) self._logbook.session.commit() self._cbxComputer.model().reload() self._cbxComputer.setCurrentIndex(self._cbxComputer.findText(dc.name)) @QtCore.Slot() def _btnRemoveComputerClicked(self): 'Remove a Dive Computer' idx = self._cbxComputer.currentIndex() dc = self._cbxComputer.itemData(idx, Qt.UserRole+0) if QMessageBox.question(self, self.tr('Delete Dive Computer?'), self.tr('Are you sure you want to delete "%s"?') % dc.name, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) == QMessageBox.Yes: self._logbook.session.delete(dc) self._logbook.session.commit() self._cbxComputer.model().reload() @QtCore.Slot() def _btnBrowseClicked(self): 'Browse for a Logbook File' if self._logbook is not None: dir = os.path.dirname(self._logbookPath) else: dir = os.path.expanduser('~') fn = QFileDialog.getOpenFileName(self, caption=self.tr('Select a Logbook file'), dir=dir, filter='Logbook Files (*.lbk);;All Files(*.*)')[0] if fn == '': return if not os.path.exists(fn): if QMessageBox.question(self, self.tr('Create new Logbook?'), self.tr('Logbook "%s" does not exist. Would you like to create it?') % os.path.basename(fn), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) != QMessageBox.Yes: return Logbook.Create(fn) self._openLogbook(fn) @QtCore.Slot() def _btnTransferClicked(self): 'Transfer Dives' idx = self._cbxComputer.currentIndex() dc = self._cbxComputer.itemData(idx, Qt.UserRole+0) if self._logbook.session.dirty: print "Flushing dirty session" self._logbook.rollback() self._txtLogbook.setEnabled(False) self._btnBrowse.setEnabled(False) self._cbxComputer.setEnabled(False) self._btnAddComputer.setEnabled(False) self._btnRemoveComputer.setEnabled(False) self._btnTransfer.setEnabled(False) self._btnExit.setEnabled(False) self._txtStatus.clear() thread = QThread(self) #FIXME: ZOMG HAX: Garbage Collector will eat TransferWorker when moveToThread is called #NOTE: Qt.QueuedConnection is important... self.worker = None self.worker = TransferWorker(dc) thread.started.connect(self.worker.start, Qt.QueuedConnection) self.worker.moveToThread(thread) self.worker.finished.connect(self._transferFinished, Qt.QueuedConnection) self.worker.finished.connect(self.worker.deleteLater, Qt.QueuedConnection) self.worker.finished.connect(thread.deleteLater, Qt.QueuedConnection) self.worker.progress.connect(self._transferProgress, Qt.QueuedConnection) self.worker.started.connect(self._transferStart, Qt.QueuedConnection) self.worker.status.connect(self._transferStatus, Qt.QueuedConnection) thread.start() @QtCore.Slot(str) def _transferStatus(self, msg): 'Transfer Status Message' self._txtStatus.append(msg) @QtCore.Slot(int) def _transferStart(self, nBytes): 'Transfer Thread Stated' if nBytes > 0: self._pbTransfer.setMaximum(nBytes) else: self._pbTransfer.setMaximum(100) self._pbTransfer.reset() @QtCore.Slot(int) def _transferProgress(self, nTransferred): 'Transfer Thread Progress Event' self._pbTransfer.setValue(nTransferred) @QtCore.Slot(models.Dive) def _transferParsed(self, dive): 'Transfer Thread Parsed Dive' self._logbook.session.add(dive) @QtCore.Slot() def _transferFinished(self): 'Transfer Thread Finished' self._logbook.session.commit() self._txtLogbook.setEnabled(True) self._btnBrowse.setEnabled(True) self._cbxComputer.setEnabled(True) self._btnAddComputer.setEnabled(True) self._btnRemoveComputer.setEnabled(True) self._btnTransfer.setEnabled(True) self._btnExit.setEnabled(True)
class DetectorWizardPage(_ExpandableOptionsWizardPage): class _DetectorComboBoxModel(QAbstractListModel): def __init__(self): QAbstractListModel.__init__(self) self._detectors = [] def rowCount(self, *args, **kwargs): return len(self._detectors) def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not (0 <= index.row() < self.rowCount()): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role != Qt.DisplayRole: return None return self._detectors[index.row()]['text'] def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or \ not (0 <= index.row() < len(self._detectors)): return False row = index.row() self._detectors[row] = value self.dataChanged.emit(index, index) return True def insertRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginInsertRows(QModelIndex(), row, row + count - 1) for _ in range(count): value = {'text': '', 'class': None, 'widget_class': None} self._detectors.insert(row, value) self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginRemoveRows(QModelIndex(), row, row + count - 1) for index in reversed(range(row, row + count)): self._detectors.pop(index) self.endRemoveRows() return True def append(self, text, clasz, widget_class): self.insert(self.rowCount(), text, clasz, widget_class) def insert(self, row, text, clasz, widget_class): self.insertRows(row) value = {'text': text, 'class': clasz, 'widget_class': widget_class} self.setData(self.createIndex(row, 0), value) def clear(self): self.removeRows(0, self.rowCount()) def widget_class(self, index): return self._detectors[index]['widget_class'] class _DetectorTableModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self._detectors = [] def rowCount(self, *args, **kwargs): return len(self._detectors) def columnCount(self, *args, **kwargs): return 2 def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not (0 <= index.row() < len(self._detectors)): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role == Qt.DisplayRole or role == Qt.ToolTipRole: key, detector = self._detectors[index.row()] column = index.column() if column == 0: return key elif column == 1: return str(detector) if detector is not None else '' return None def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'Key' elif section == 1: return 'Detector' elif orientation == Qt.Vertical: return str(section + 1) def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable) def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or \ not (0 <= index.row() < len(self._detectors)): return False row = index.row() column = index.column() self._detectors[row][column] = value self.dataChanged.emit(index, index) return True def insertRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginInsertRows(parent, row, row + count - 1) for _ in range(count): self._detectors.insert(row, ['untitled', None]) self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginRemoveRows(parent, row, row + count - 1) for index in reversed(range(row, row + count)): self._detectors.pop(index) self.endRemoveRows() return True def append(self, key, detector): self.insert(self.rowCount(), key, detector) def insert(self, index, key, detector): self.insertRows(index) self.setData(self.createIndex(index, 0), key) self.setData(self.createIndex(index, 1), detector) def modify(self, index, key, detector): self.setData(self.createIndex(index, 0), key) self.setData(self.createIndex(index, 1), detector) def clear(self): self.removeRows(0, self.rowCount()) def detectors(self): detectors = {} for key, detector in self._detectors: if detector is not None: detectors.setdefault(key, []).append(detector) return detectors def detector(self, index): return self._detectors[index.row()][1] def key(self, index): return self._detectors[index.row()][0] class _DetectorTableDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): column = index.column() if column == 0: editor = QLineEdit(parent) editor.setValidator(QRegExpValidator(QRegExp(r"^(?!\s*$).+"))) return editor elif column == 1: return None def setEditorData(self, editor, index): column = index.column() if column == 0: key = index.model().data(index, Qt.DisplayRole) editor.setText(key) def setModelData(self, editor, model, index): column = index.column() if column == 0: if not editor.hasAcceptableInput(): return model.setData(index, editor.text()) def __init__(self, options, parent=None): _ExpandableOptionsWizardPage.__init__(self, options, parent) self.setTitle('Detector') def _initUI(self): # Variables self._widgets = {} tbl_model = self._DetectorTableModel() # Widgets self._cb_detector = QComboBox() self._cb_detector.setModel(self._DetectorComboBoxModel()) btn_detector_add = QPushButton() btn_detector_add.setIcon(getIcon("list-add")) self._tbl_detector = QTableView() self._tbl_detector.setModel(tbl_model) self._tbl_detector.setItemDelegate(self._DetectorTableDelegate()) header = self._tbl_detector.horizontalHeader() header.setResizeMode(1, QHeaderView.Stretch) policy = self._tbl_detector.sizePolicy() policy.setVerticalStretch(True) self._tbl_detector.setSizePolicy(policy) tlb_detector = QToolBar() spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) tlb_detector.addWidget(spacer) act_remove = tlb_detector.addAction(getIcon("list-remove"), "Remove detector") act_clear = tlb_detector.addAction(getIcon("edit-clear"), "Clear") # Layouts layout = _ExpandableOptionsWizardPage._initUI(self) sublayout = QHBoxLayout() sublayout.addWidget(self._cb_detector, 1) sublayout.addWidget(btn_detector_add) layout.addRow("Select", sublayout) layout.addRow(self._tbl_detector) layout.addRow(tlb_detector) # Signals btn_detector_add.released.connect(self._onDetectorAdd) act_remove.triggered.connect(self._onDetectorRemove) act_clear.triggered.connect(self._onDetectorClear) self._tbl_detector.doubleClicked.connect(self._onDetectorDoubleClicked) tbl_model.dataChanged.connect(self.valueChanged) tbl_model.rowsInserted.connect(self.valueChanged) tbl_model.rowsRemoved.connect(self.valueChanged) return layout def _onDetectorAdd(self): index = self._tbl_detector.selectionModel().currentIndex() tbl_model = self._tbl_detector.model() cb_model = self._cb_detector.model() widget_class = cb_model.widget_class(self._cb_detector.currentIndex()) wdg_detector = widget_class() dialog = _DetectorDialog(wdg_detector) if not dialog.exec_(): return tbl_model.insert(index.row() + 1, dialog.key(), dialog.detector()) def _onDetectorRemove(self): selection = self._tbl_detector.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Detector", "Select a row") return tbl_model = self._tbl_detector.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): tbl_model.removeRow(row) def _onDetectorClear(self): model = self._tbl_detector.model() for row in reversed(range(model.rowCount())): model.removeRow(row) def _onDetectorDoubleClicked(self, index): if index.column() != 1: return tbl_model = self._tbl_detector.model() key = tbl_model.key(index) detector = tbl_model.detector(index) widget_class = self._widgets[detector.__class__] wdg_detector = widget_class() wdg_detector.setValue(detector) dialog = _DetectorDialog(wdg_detector, key) if not dialog.exec_(): return tbl_model.modify(index.row(), dialog.key(), dialog.detector()) def initializePage(self): _ExpandableOptionsWizardPage.initializePage(self) tbl_model = self._tbl_detector.model() cb_model = self._cb_detector.model() # Clear self._widgets.clear() tbl_model.clear() cb_model.clear() # Populate combo box it = self._iter_widgets('pymontecarlo.ui.gui.options.detector', 'DETECTORS') for clasz, widget_class, programs in it: widget = widget_class() self._widgets[clasz] = widget_class program_text = ', '.join(map(attrgetter('name'), programs)) text = '{0} ({1})'.format(widget.accessibleName(), program_text) cb_model.append(text, clasz, widget_class) del widget self._cb_detector.setCurrentIndex(0) # Add detector(s) for key, detectors in self.options().detectors.items(): detectors = np.array(detectors, ndmin=1) for detector in detectors: if not detector.__class__ in self._widgets: continue tbl_model.append(key, detector) def validatePage(self): tbl_model = self._tbl_detector.model() if tbl_model.rowCount() == 0: return False self.options().detectors.clear() self.options().detectors.update(tbl_model.detectors()) return True def expandCount(self): if self._tbl_detector.model().rowCount() == 0: return 0 try: count = 1 for detectors in self._tbl_detector.model().detectors().values(): count *= len(detectors) for detector in detectors: count *= len(expand(detector)) return count except: return 0
class ModelWizardPage(_ExpandableOptionsWizardPage): class _ModelTypeComboBoxModel(QAbstractListModel): def __init__(self, model_types=None): QAbstractListModel.__init__(self) if model_types is None: model_types = [] self._model_types = list(model_types) def rowCount(self, *args, **kwargs): return len(self._model_types) def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not (0 <= index.row() < self.rowCount()): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role != Qt.DisplayRole: return None return str(self._model_types[index.row()]) def modelType(self, index): return self._model_types[index] class _ModelComboBoxModel(QAbstractListModel): def __init__(self, models_text=None): QAbstractListModel.__init__(self) if models_text is None: models_text = {} self._models_text = models_text.copy() self._models = {} for model in models_text.keys(): self._models.setdefault(model.type, []).append(model) self._model_type = None def rowCount(self, *args, **kwargs): return len(self._models.get(self._model_type, [])) def data(self, index, role=Qt.DisplayRole): if not index.isValid() or \ not (0 <= index.row() < self.rowCount()): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role != Qt.DisplayRole: return None model = self._models[self._model_type][index.row()] return self._models_text[model] def setModelType(self, model_type): self._model_type = model_type self.reset() def model(self, index): return self._models[self._model_type][index] def add(self, model): if model not in self._models_text: raise ValueError('No text defined for model: %s' % model) self._models[model.type].append(model) self.reset() def remove(self, model): self._models[model.type].remove(model) self.reset() def __init__(self, options, parent=None): _ExpandableOptionsWizardPage.__init__(self, options, parent) self.setTitle('Model') def _initUI(self): # Widgets self._cb_model_type = QComboBox() self._cb_model_type.setModel(self._ModelTypeComboBoxModel()) self._cb_model = QComboBox() self._cb_model.setModel(self._ModelComboBoxModel()) self._cb_model.setMaxVisibleItems(10) btn_model_add = QPushButton() btn_model_add.setIcon(getIcon("list-add")) self._tbl_model = ModelTableWidget() tlb_model = QToolBar() spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) tlb_model.addWidget(spacer) act_remove = tlb_model.addAction(getIcon("list-remove"), "Remove model") act_clear = tlb_model.addAction(getIcon("edit-clear"), "Clear") # Layouts layout = _ExpandableOptionsWizardPage._initUI(self) sublayout = QHBoxLayout() sublayout.addWidget(self._cb_model_type, 1) sublayout.addWidget(self._cb_model, 1) sublayout.addWidget(btn_model_add) layout.addRow("Select", sublayout) layout.addRow(self._tbl_model) layout.addRow(tlb_model) # Signals btn_model_add.released.connect(self._onModelAdd) act_remove.triggered.connect(self._onModelRemove) act_clear.triggered.connect(self._onModelClear) self._cb_model_type.currentIndexChanged.connect(self._onModelTypeChanged) self._tbl_model.dataChanged.connect(self.valueChanged) return layout def _onModelTypeChanged(self): cb_model = self._cb_model.model() index = self._cb_model_type.currentIndex() model_type = self._cb_model_type.model().modelType(index) cb_model.setModelType(model_type) self._cb_model.setCurrentIndex(0) def _onModelAdd(self): cb_model = self._cb_model.model() index = self._cb_model.currentIndex() try: model = cb_model.model(index) except IndexError: # No entry return self._tbl_model.addModel(model) cb_model.remove(model) # Remove model from combo box self._cb_model.setCurrentIndex(index) def _onModelRemove(self): models = self._tbl_model.currentModels() if len(models) == 0: QMessageBox.warning(self, "Model", "Select a row") return cb_model = self._cb_model.model() for model in models: cb_model.add(model) # Show model in combo box self._tbl_model.removeModel(model) if self._cb_model.currentIndex() < 0: self._cb_model.setCurrentIndex(0) def _onModelClear(self): models = self._tbl_model.models() cb_model = self._cb_model.model() for model in models: cb_model.add(model) # Show model in combo box self._tbl_model.removeModel(model) if self._cb_model.currentIndex() < 0: self._cb_model.setCurrentIndex(0) def _iter_models(self): allmodels = {} default_models = {} for program in self.options().programs: converter = program.converter_class for models in converter.MODELS.values(): for model in models: allmodels.setdefault(model, set()).add(program) if model == converter.DEFAULT_MODELS[model.type]: default_models.setdefault(model, set()).add(program) for model, programs in allmodels.items(): defaults = default_models.get(model, []) yield model, programs, defaults def initializePage(self): _ExpandableOptionsWizardPage.initializePage(self) # Populate combo boxes model_types = set() models_text = {} for model, programs, defaults in self._iter_models(): programs_text = [] for program in programs: program_text = program.name if program in defaults: program_text += '*' programs_text.append(program_text) program_text = ', '.join(programs_text) text = '{0} ({1})'.format(str(model), program_text) model_types.add(model.type) models_text[model] = text model_types = sorted(model_types, key=attrgetter('name')) cb_model_type = self._ModelTypeComboBoxModel(model_types) self._cb_model_type.setModel(cb_model_type) self._cb_model_type.setCurrentIndex(0) cb_model = self._ModelComboBoxModel(models_text) self._cb_model.setModel(cb_model) cb_model.setModelType(cb_model_type.modelType(0)) self._cb_model.setCurrentIndex(0) # Add model(s) self._tbl_model.clear() self._tbl_model.addModels(self.options().models) def validatePage(self): self.options().models.clear() for model in self._tbl_model.models(): self.options().models.add(model) return True def expandCount(self): try: count = 1 models = {} for model in self._tbl_model.models(): models.setdefault(model.type, []).append(model) for values in models.values(): count *= len(values) return count except: return 0