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 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 ModelTableWidget(QWidget): dataChanged = Signal(QModelIndex, QModelIndex) class _ModelTableModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self._models = [] def rowCount(self, *args, **kwargs): return len(self._models) 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._models)): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter model = self._models[index.row()] if model is None: return "" if role == Qt.DisplayRole or role == Qt.ToolTipRole: column = index.column() if column == 0: return str(model.type) elif column == 1: return str(model) return None def headerData(self, section, orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return "Type" elif section == 1: return "Model" 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)) def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or not (0 <= index.row() < len(self._models)): return False row = index.row() self._models[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._models.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._models.pop(index) self.endRemoveRows() return True def append(self, model): self.insert(self.rowCount(), model) def insert(self, index, model): self.insertRows(index) self.setData(self.createIndex(index, 0), model) def remove(self, model): index = self._models.index(model) self.removeRows(index) def clear(self): self.removeRows(0, self.rowCount()) def models(self): models = set(self._models) models.discard(None) return models def model(self, index): return self._models[index.row()] def __init__(self, parent=None): QWidget.__init__(self, parent) # Variables model = self._ModelTableModel() # Widgets self._tbl_models = QTableView() self._tbl_models.setModel(model) header = self._tbl_models.horizontalHeader() header.setResizeMode(QHeaderView.Stretch) policy = self._tbl_models.sizePolicy() policy.setVerticalStretch(True) self._tbl_models.setSizePolicy(policy) self._tbl_models.setSelectionMode(QTableView.SelectionMode.MultiSelection) # Layouts layout = QVBoxLayout() layout.addWidget(self._tbl_models) self.setLayout(layout) # Signals model.dataChanged.connect(self.dataChanged) def addModel(self, model): self._tbl_models.model().append(model) def addModels(self, models): for model in models: self.addModel(model) def removeModel(self, model): self._tbl_models.model().remove(model) def clear(self): self._tbl_models.model().clear() def models(self): return self._tbl_models.model().models() def currentModels(self): tbl_model = self._tbl_models.model() models = [] for index in self._tbl_models.selectionModel().selection().indexes(): models.append(tbl_model.model(index)) return models
class WarningWizardPage(_OptionsWizardPage): class _WarningTableModel(QAbstractTableModel): def __init__(self, warns=None): QAbstractTableModel.__init__(self) if warns is None: warns = [] self._warns = warns def rowCount(self, *args, **kwargs): return len(self._warns) 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._warns)): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role != Qt.DisplayRole: return None return str(self._warns[index.row()][index.column()]) def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'Program' elif section == 1: return 'Warning' 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)) def __init__(self, options, parent=None): _OptionsWizardPage.__init__(self, options, parent=parent) self.setTitle("Conversion warnings") # Widgets self._lbl_count = SimulationCountLabel() self._lbl_count.setAlignment(Qt.AlignCenter) # Layouts layout = self.layout() frm_line = QFrame() frm_line.setFrameStyle(QFrame.HLine) frm_line.setFrameShadow(QFrame.Sunken) layout.addWidget(frm_line) sublayout = QHBoxLayout() sublayout.setContentsMargins(10, 0, 10, 0) sublayout.addWidget(self._lbl_count) layout.addLayout(sublayout) def _initUI(self): # Widgets self._tbl_warnings = QTableView() self._tbl_warnings.setModel(self._WarningTableModel()) header = self._tbl_warnings.horizontalHeader() header.setResizeMode(1, QHeaderView.Stretch) policy = self._tbl_warnings.sizePolicy() policy.setVerticalStretch(True) self._tbl_warnings.setSizePolicy(policy) # Layouts layout = _OptionsWizardPage._initUI(self) layout.addRow(self._tbl_warnings) return layout def initializePage(self): count = 0 warns = [] for program in self.options().programs: converter = program.converter_class() warnings.simplefilter("always") with warnings.catch_warnings(record=True) as ws: list_options = converter.convert(self.options()) count += len(list_options) for w in ws: warns.append((program, w.message)) model = self._WarningTableModel(warns) self._tbl_warnings.setModel(model) self._lbl_count.setValue(count) def validatePage(self): return self._lbl_count.value() > 0