def __init__(self, fileName, parent=None): QObject.__init__(self) sweepCollection = IvSweepCollection(str(fileName)) self.sweepCollection = sweepCollection tableModel = SweepTableModel(sweepCollection) tableView = QTableView() tableView.setModel(tableModel) tableView.setItemDelegateForColumn(0, CheckBoxDelegate(parent=tableView)) tableView.setItemDelegateForColumn(1, CheckBoxDelegate(parent=tableView)) self.tableModel = tableModel tableView.resizeColumnsToContents() tableView.setSelectionMode(QAbstractItemView.SingleSelection) tableView.setSelectionBehavior(QAbstractItemView.SelectRows) sm = tableView.selectionModel() sm.currentRowChanged.connect(self.selectedRowChanged) self.tableView = tableView dockWidget = QDockWidget('Sweeps') dockWidget.setWidget(tableView) self.sweepTableDock = dockWidget hkDock = HkDockWidget(sweepCollection.hk) hkDock.setWindowTitle('HK - %s' % str(fileName)) self.hkDock = hkDock ivGraphWidget = IvGraphWidget(sweepCollection) tableModel.plotToggled.connect(ivGraphWidget.showSweep) #tableModel.badToggled.connect(self.toggleBad) self.ivGraphWidget = ivGraphWidget
class SpecimenPositionListWidget(ParameterWidget): class _SpecimenPositionModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self.positions = [] def rowCount(self, *args, **kwargs): return len(self.positions) def columnCount(self, *args, **kwargs): return 5 def data(self, index, role): if not index.isValid() or not (0 <= index.row() < len(self.positions)): return None if role != Qt.DisplayRole: return None position = self.positions[index.row()] column = index.column() if column == 0: return str(position.x) if position.x is not None else '' elif column == 1: return str(position.y) if position.y is not None else '' elif column == 2: return str(position.z) if position.z is not None else '' elif column == 3: return str(position.r) if position.r is not None else '' elif column == 4: return str(position.t) if position.t is not None else '' def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'X' elif section == 1: return 'Y' elif section == 2: return 'Z' elif section == 3: return 'R' elif section == 4: return 'T' 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.positions)): return False position = self.positions[index.row()] column = index.column() if column == 0: position.x = value elif column == 1: position.y = value elif column == 2: position.z = value elif column == 3: position.r = value elif column == 4: position.t = value 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 i in range(count): self.positions.insert(row + i, SpecimenPosition()) 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) self.positions = self.positions[:row] + self.positions[row + count:] self.endRemoveRows() return True class _SpecimenPositionDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): column = index.column() if column == 0: return NumericalAttributeLineEdit(SpecimenPosition.x, parent) elif column == 1: return NumericalAttributeLineEdit(SpecimenPosition.y, parent) elif column == 2: return NumericalAttributeLineEdit(SpecimenPosition.y, parent) elif column == 3: return NumericalAttributeLineEdit(SpecimenPosition.y, parent) elif column == 4: return NumericalAttributeLineEdit(SpecimenPosition.y, parent) else: return QItemDelegate.createEditor(self, parent, option, index) def setEditorData(self, editor, index): text = index.model().data(index, Qt.DisplayRole) column = index.column() if column == 0: editor.setText(text) elif column == 1: editor.setText(text) elif column == 2: editor.setText(text) elif column == 3: editor.setText(text) elif column == 4: editor.setText(text) else: QItemDelegate.setEditorData(self, editor, index) def setModelData(self, editor, model, index): column = index.column() if column == 0: model.setData(index, editor.text()) elif column == 1: model.setData(index, editor.text()) elif column == 2: model.setData(index, editor.text()) elif column == 3: model.setData(index, editor.text()) elif column == 4: model.setData(index, editor.text()) else: return QItemDelegate.setModelData(self, editor, model, index) def __init__(self, parent=None): ParameterWidget.__init__(self, object, parent) def _init_ui(self): # Widgets self._table = QTableView() self._table.setModel(self._SpecimenPositionModel()) self._table.setItemDelegate(self._SpecimenPositionDelegate(self)) self._table.horizontalHeader().setStretchLastSection(True) self._toolbar = QToolBar() action_add = self._toolbar.addAction(getIcon("list-add"), "Add layer") action_remove = self._toolbar.addAction(getIcon("list-remove"), "Remove layer") # Layouts layout = ParameterWidget._init_ui(self) layout.addRow(self._table) layout.addRow(self._toolbar) # Signals action_add.triggered.connect(self._on_add) action_remove.triggered.connect(self._on_remove) return layout def _on_add(self): index = self._table.selectionModel().currentIndex() model = self._table.model() model.insertRows(index.row() + 1) def _on_remove(self): selection = self._table.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Specimen position", "Select a position") return model = self._table.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): model.removeRow(row) def parameter(self): positions = [] for position in self._table.model().positions: positions.append(SpecimenPosition(position.x, position.y, position.z, position.r, position.t)) return positions def setParameter(self, positions): model = self._table.model() model.positions = positions model.reset() def positions(self): return self.parameter() def setPositions(self, positions): self.setParameter(positions) def setReadOnly(self, state): ParameterWidget.setReadOnly(self, state) if state: trigger = QTableView.EditTrigger.NoEditTriggers else: trigger = QTableView.EditTrigger.AllEditTriggers self._table.setEditTriggers(trigger) self._toolbar.setEnabled(not state) def isReadOnly(self): return ParameterWidget.isReadOnly(self) and \ self._table.editTriggers() == QTableView.EditTrigger.NoEditTriggers and \ not self._toolbar.isEnabled()
class ConfigurationManager(SiriusMainWindow): """.""" NAME_COL = None CONFIG_TYPE_COL = None def __init__(self, model, parent=None): """Constructor.""" super().__init__(parent) self._model = model self._logger = logging.getLogger(__name__) self._logger.setLevel(logging.INFO) self._setup_ui() self.setWindowTitle("Configuration Manager") def _setup_ui(self): # self.setGeometry(0, 0, 1600, 900) self.main_widget = QFrame() self.main_widget.setObjectName('ServConf') self.setCentralWidget(self.main_widget) self.layout = QGridLayout() self.main_widget.setLayout(self.layout) # Basic widgets self.editor = QTableView() self.delete_button = QPushButton('Delete', self) self.delete_button.setObjectName('DeleteButton') self.rename_button = QPushButton('Rename', self) self.rename_button.setObjectName('RenameButton') self.d_editor = QTableView() self.retrieve_button = QPushButton('Retrieve', self) self.retrieve_button.setObjectName('RetrieveButton') self.tree = QTreeView(self) self.config_type = QComboBox(parent=self) self.config_type.setModel( ConfigTypeModel(self._model, self.config_type)) # Tab widgets self.tab1 = QWidget() self.tab1.layout = QVBoxLayout(self.tab1) self.tab2 = QWidget() self.tab2.layout = QVBoxLayout(self.tab2) self.tab1.layout.addWidget(self.editor) hlay = QHBoxLayout() hlay.addWidget(self.rename_button) hlay.addWidget(self.delete_button) self.tab1.layout.addLayout(hlay) self.tab2.layout.addWidget(self.d_editor) self.tab2.layout.addWidget(self.retrieve_button) self.editor_tab = QTabWidget(self) self.editor_tab.addTab(self.tab1, 'Configurations') self.editor_tab.addTab(self.tab2, 'Discarded Configurations') self.config_viewer = QWidget(self) self.config_viewer.layout = QVBoxLayout(self.config_viewer) self.config_viewer.layout.addWidget(self.editor_tab) self.config_viewer.layout.addWidget(self.tree) # Header widget self.header = QFrame(self) self.header.setObjectName('Header') self.header.layout = QHBoxLayout(self.header) self.header.layout.addStretch() self.header.layout.addWidget( QLabel('Configuration Database Manager', self.header)) self.header.layout.addStretch() # Sub header with database genral information self.sub_header = QFrame(self) self.sub_header.setObjectName('SubHeader') self.sub_header.layout = QVBoxLayout(self.sub_header) self.server_layout = QHBoxLayout() self.server_layout.addWidget(QLabel('<b>Server:</b>', self.sub_header)) self.server_layout.addWidget(QLabel(self._model.url, self.sub_header)) self.server_layout.addStretch() self.size_layout = QHBoxLayout() self.size_layout.addWidget(QLabel('<b>DB Size:</b>', self.sub_header)) try: dbsize = self._model.get_dbsize() dbsize = '{:.2f} MB'.format(dbsize/(1024*1024)) except ConfigDBException: dbsize = 'Failed to retrieve information' self.size_layout.addWidget(QLabel(dbsize, self.sub_header)) self.size_layout.addStretch() self.sub_header.layout.addLayout(self.server_layout) self.sub_header.layout.addLayout(self.size_layout) # Query form self.query_form = QFrame() self.query_form.setObjectName("QueryForm") self.query_form.layout = QVBoxLayout() self.query_form.setLayout(self.query_form.layout) self.configs_layout = QGridLayout() self.configs_layout.addWidget(QLabel('Configurations:', self), 0, 0) self.nr_configs = QLabel(self) self.configs_layout.addWidget(self.nr_configs, 0, 1) self.configs_layout.addWidget(QLabel('Discarded:', self), 0, 2) self.nr_discarded = QLabel(self) self.configs_layout.addWidget(self.nr_discarded, 0, 3) self.query_form.layout.addWidget(self.config_type) self.query_form.layout.addLayout(self.configs_layout) # Main widget layout setup self.layout.addWidget(self.header, 0, 0, 1, 3) self.layout.addWidget(self.sub_header, 1, 0, 1, 2) self.layout.addWidget(self.query_form, 2, 0, 1, 2) self.layout.addWidget(self.config_viewer, 3, 0, 1, 2) self.layout.addWidget(self.tree, 1, 2, 4, 1) # self.layout.addWidget(self.delete_button, 4, 0, 1, 2) self.layout.setColumnStretch(0, 1) self.layout.setColumnStretch(1, 2) self.layout.setColumnStretch(2, 2) # Set table models and options self.editor_model = ConfigDbTableModel('notexist', self._model) self.d_editor_model = ConfigDbTableModel('notexist', self._model, True) self.editor.setModel(self.editor_model) self.editor.setSelectionBehavior(self.editor.SelectRows) self.editor.setSortingEnabled(True) self.editor.horizontalHeader().setResizeMode(QHeaderView.Stretch) self.d_editor.setModel(self.d_editor_model) self.d_editor.setSelectionBehavior(self.editor.SelectRows) self.d_editor.setSortingEnabled(True) self.d_editor.horizontalHeader().setResizeMode(QHeaderView.Stretch) self.d_editor.setSelectionMode(self.d_editor.SingleSelection) # Set tree model and options self.tree_model = JsonTreeModel(None, None, self._model) self.tree.setModel(self.tree_model) # Delete button self.delete_button.setEnabled(False) self.rename_button.setEnabled(True) self.retrieve_button.setEnabled(False) # Signals and slots # Tab self.editor_tab.currentChanged.connect(self._tab_changed) # Fill tables when configuration is selected self.config_type.currentTextChanged.connect(self._fill_table) # Fill tree when a configuration is selected self.editor.selectionModel().selectionChanged.connect( lambda x, y: self._fill_tree()) self.d_editor.selectionModel().selectionChanged.connect( lambda x, y: self._fill_tree()) # Connect database error to slot that show messages self.editor_model.connectionError.connect(self._database_error) self.d_editor_model.connectionError.connect(self._database_error) # Makes tree column extend automatically to show content self.tree.expanded.connect( lambda idx: self.tree.resizeColumnToContents(idx.column())) # Button action self.delete_button.pressed.connect(self._remove_configuration) self.rename_button.pressed.connect(self._rename_configuration) self.retrieve_button.pressed.connect(self._retrieve_configuration) # Set constants ConfigurationManager.NAME_COL = \ self.editor_model.horizontalHeader.index('name') ConfigurationManager.CONFIG_TYPE_COL = \ self.editor_model.horizontalHeader.index('config_type') self.editor.resizeColumnsToContents() self.d_editor.resizeColumnsToContents() @Slot(str) def _fill_table(self, config_type): """Fill table with configuration of `config_type`.""" leng = len(self._model.find_configs( config_type=config_type, discarded=False)) self.nr_configs.setText(str(leng)) leng = len(self._model.find_configs( config_type=config_type, discarded=True)) self.nr_discarded.setText(str(leng)) self.editor_model.setupModelData(config_type) self.d_editor_model.setupModelData(config_type) self.editor.resizeColumnsToContents() self.d_editor.resizeColumnsToContents() self.editor_model.sort(2, Qt.DescendingOrder) self.d_editor_model.sort(2, Qt.DescendingOrder) @Slot() def _fill_tree(self): if self.editor_tab.currentIndex() == 0: configs = list() rows = self._get_selected_rows(self.editor) # Get selected rows for row in rows: # Get name and configuration type configs.append(self._type_name(row, self.editor_model)) # Set tree data self.tree_model.setupModelData(configs) if len(configs) == 1: self.delete_button.setEnabled(True) self.delete_button.setText( 'Delete {} ({})'.format(configs[0][1], configs[0][0])) self.rename_button.setEnabled(True) self.rename_button.setText( 'Rename {} ({})'.format(configs[0][1], configs[0][0])) elif len(configs) > 1: self.rename_button.setEnabled(False) self.rename_button.setText('Rename') self.delete_button.setEnabled(True) self.delete_button.setText( 'Delete {} configurations'.format(len(configs))) else: self.rename_button.setEnabled(False) self.rename_button.setText('Rename') self.delete_button.setEnabled(False) self.delete_button.style().polish(self.delete_button) self.rename_button.style().polish(self.rename_button) else: try: row = self._get_selected_rows(self.d_editor).pop() except KeyError: self.retrieve_button.setEnabled(False) self.retrieve_button.style().polish(self.retrieve_button) else: config_type, name = self._type_name(row, self.d_editor_model) self.tree_model.setupModelData([(config_type, name)]) self.retrieve_button.setEnabled(True) self.retrieve_button.style().polish(self.retrieve_button) # self.tree.resizeColumnsToContents() @Slot() def _remove_configuration(self): type = QMessageBox.Question title = 'Remove configuration?' buttons = QMessageBox.Ok | QMessageBox.Cancel # self.editor.selectRow(index.row()) rows = list(self._get_selected_rows(self.editor)) message = 'Remove configurations:\n' for row in rows: config_type = self.editor_model.createIndex(row, 0).data() name = self.editor_model.createIndex(row, 1).data() message += '- {} ({})\n'.format(name, config_type) msg = QMessageBox(type, title, message, buttons).exec_() if msg == QMessageBox.Ok: rows.sort(reverse=True) for row in rows: self.editor_model.removeRows(row) self.editor.selectionModel().clearSelection() self._fill_table(self.config_type.currentText()) @Slot() def _rename_configuration(self): # self.editor.selectRow(index.row()) rows = list(self._get_selected_rows(self.editor)) if not rows: return config_type = self.editor_model.createIndex(rows[0], 0).data() name = self.editor_model.createIndex(rows[0], 1).data() wid = RenameConfigDialog(config_type, self) wid.setWindowTitle('Rename: {}'.format(name)) wid.search_le.setText(name) newname, status = wid.exec_() if not newname or not status: return self._model.rename_config( name, newname, config_type=config_type) self.editor.selectionModel().clearSelection() self._fill_table(self.config_type.currentText()) @Slot() def _retrieve_configuration(self): type = QMessageBox.Question title = 'Retrieve configuration?' buttons = QMessageBox.Ok | QMessageBox.Cancel try: row = self._get_selected_rows(self.d_editor).pop() except KeyError: pass else: config_type, name = self._type_name(row, self.d_editor_model) name = name[:-37] message = \ 'Retrieve configuration {} ({})?'.format(config_type, name) msg = QMessageBox(type, title, message, buttons).exec_() if msg == QMessageBox.Ok: try: self.d_editor_model.removeRows(row) except TypeError: self._database_error( 'Exception', 'Configuration no longer is in the correct format', 'retrieve configuration') self.editor.selectionModel().clearSelection() self._fill_table(self.config_type.currentText()) @Slot(int) def _tab_changed(self, index): if index == 0: self.editor.selectionModel().clearSelection() self.delete_button.setText('Delete') self.delete_button.setEnabled(False) self.delete_button.style().polish(self.delete_button) self.rename_button.setText('Rename') self.rename_button.setEnabled(False) self.rename_button.style().polish(self.rename_button) else: self.d_editor.selectionModel().clearSelection() self.retrieve_button.setEnabled(False) self.retrieve_button.style().polish(self.retrieve_button) self.tree_model.setupModelData([]) @Slot(int, str, str) def _database_error(self, code, message, operation): type = QMessageBox.Warning title = 'Something went wrong' msg = '{}: {}, while trying to {}'.format(code, message, operation) QMessageBox(type, title, msg).exec_() def _get_selected_rows(self, table): index_list = table.selectionModel().selectedIndexes() return {idx.row() for idx in index_list} def _type_name(self, row, model): # Return config_type and name given a row and a table model return (model.createIndex(row, self.CONFIG_TYPE_COL).data(), model.createIndex(row, self.NAME_COL).data())
class LayoutSettingsDialog(QDialog): """Layout settings dialog""" def __init__(self, parent, names, order, active): super(LayoutSettingsDialog, self).__init__(parent) # variables self._parent = parent self._selection_model = None self.names = names self.order = order self.active = active # widgets self.button_move_up = QPushButton(_('Move Up')) self.button_move_down = QPushButton(_('Move Down')) self.button_delete = QPushButton(_('Delete Layout')) self.button_box = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.group_box = QGroupBox(_("Layout Display and Order")) self.table = QTableView(self) self.ok_button = self.button_box.button(QDialogButtonBox.Ok) self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel) self.cancel_button.setDefault(True) self.cancel_button.setAutoDefault(True) # widget setup self.dialog_size = QSize(300, 200) self.setMinimumSize(self.dialog_size) self.setFixedSize(self.dialog_size) self.setWindowTitle('Layout Settings') self.table.setModel(LayoutModel(self.table, order, active)) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setSelectionMode(QAbstractItemView.SingleSelection) self.table.verticalHeader().hide() self.table.horizontalHeader().hide() self.table.setAlternatingRowColors(True) self.table.setShowGrid(False) self.table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table.horizontalHeader().setStretchLastSection(True) self.table.setColumnHidden(1, True) # need to keep a reference for pyside not to segfault! self._selection_model = self.table.selectionModel() # layout buttons_layout = QVBoxLayout() buttons_layout.addWidget(self.button_move_up) buttons_layout.addWidget(self.button_move_down) buttons_layout.addStretch() buttons_layout.addWidget(self.button_delete) group_layout = QHBoxLayout() group_layout.addWidget(self.table) group_layout.addLayout(buttons_layout) self.group_box.setLayout(group_layout) layout = QVBoxLayout() layout.addWidget(self.group_box) layout.addWidget(self.button_box) self.setLayout(layout) # signals and slots self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.close) self.button_delete.clicked.connect(self.delete_layout) self.button_move_up.clicked.connect(lambda: self.move_layout(True)) self.button_move_down.clicked.connect(lambda: self.move_layout(False)) self.table.model().dataChanged.connect( lambda: self.selection_changed(None, None)) self._selection_model.selectionChanged.connect( lambda: self.selection_changed(None, None)) # focus table index = self.table.model().index(0, 0) self.table.setCurrentIndex(index) self.table.setFocus() def delete_layout(self): """ """ names, order, active = self.names, self.order, self.order name = from_qvariant(self.table.selectionModel().currentIndex().data(), to_text_string) if name in names: index = names.index(name) # In case nothing has focus in the table if index != -1: order.remove(name) names[index] = None if name in active: active.remove(name) self.names, self.order, self.active = names, order, active self.table.model().set_data(order, active) index = self.table.model().index(0, 0) self.table.setCurrentIndex(index) self.table.setFocus() self.selection_changed(None, None) if len(order) == 0: self.button_move_up.setDisabled(True) self.button_move_down.setDisabled(True) self.button_delete.setDisabled(True) def move_layout(self, up=True): """ """ names, order, active = self.names, self.order, self.active row = self.table.selectionModel().currentIndex().row() row_new = row if up: row_new -= 1 else: row_new += 1 order[row], order[row_new] = order[row_new], order[row] self.order = order self.table.model().set_data(order, active) index = self.table.model().index(row_new, 0) self.table.setCurrentIndex(index) self.table.setFocus() self.selection_changed(None, None) def selection_changed(self, selection, deselection): """ """ model = self.table.model() index = self.table.currentIndex() row = index.row() order, names, active = self.order, self.names, self.active state = model.row(row)[1] name = model.row(row)[0] # Check if name changed if name not in names: # Did changed if row != -1: # row == -1, means no items left to delete old_name = order[row] order[row] = name names[names.index(old_name)] = name if old_name in active: active[active.index(old_name)] = name # Check if checbox clicked if state: if name not in active: active.append(name) else: if name in active: active.remove(name) self.active = active self.button_move_up.setDisabled(False) self.button_move_down.setDisabled(False) if row == 0: self.button_move_up.setDisabled(True) if row == len(names) - 1: self.button_move_down.setDisabled(True) if len(names) == 0: self.button_move_up.setDisabled(True) self.button_move_down.setDisabled(True)
class BasePlotCurveEditorDialog(QDialog): """QDialog that is used in Qt Designer to edit the properties of the curves in a waveform plot. This dialog is shown when you double-click the plot, or when you right click it and choose 'edit curves'. This thing is mostly just a wrapper for a table view, with a couple buttons to add and remove curves, and a button to save the changes.""" TABLE_MODEL_CLASS = BasePlotCurvesModel def __init__(self, plot, parent=None): super(BasePlotCurveEditorDialog, self).__init__(parent) self.plot = plot self.setup_ui() self.table_model = self.TABLE_MODEL_CLASS(self.plot) self.table_view.setModel(self.table_model) self.table_model.plot = plot # self.table_view.resizeColumnsToContents() self.add_button.clicked.connect(self.addCurve) self.remove_button.clicked.connect(self.removeSelectedCurve) self.remove_button.setEnabled(False) self.table_view.selectionModel().selectionChanged.connect( self.handleSelectionChange) self.table_view.doubleClicked.connect(self.handleDoubleClick) self.resize(800, 300) def setup_ui(self): self.vertical_layout = QVBoxLayout(self) self.table_view = QTableView(self) self.table_view.setEditTriggers(QAbstractItemView.DoubleClicked) self.table_view.setProperty("showDropIndicator", False) self.table_view.setDragDropOverwriteMode(False) self.table_view.setSelectionMode(QAbstractItemView.SingleSelection) self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.table_view.setSortingEnabled(False) self.table_view.horizontalHeader().setStretchLastSection(True) self.table_view.verticalHeader().setVisible(False) self.table_view.setColumnWidth(0, 160) self.table_view.setColumnWidth(1, 160) self.table_view.setColumnWidth(2, 160) self.vertical_layout.addWidget(self.table_view) self.add_remove_layout = QHBoxLayout() spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.add_remove_layout.addItem(spacer) self.add_button = QPushButton("Add Curve", self) self.add_remove_layout.addWidget(self.add_button) self.remove_button = QPushButton("Remove Curve", self) self.add_remove_layout.addWidget(self.remove_button) self.vertical_layout.addLayout(self.add_remove_layout) self.button_box = QDialogButtonBox(self) self.button_box.setOrientation(Qt.Horizontal) self.button_box.addButton("Done", QDialogButtonBox.AcceptRole) self.vertical_layout.addWidget(self.button_box) self.button_box.accepted.connect(self.saveChanges) self.button_box.rejected.connect(self.reject) self.setWindowTitle("Waveform Curve Editor") def setup_delegate_columns(self, index=2): symbol_delegate = SymbolColumnDelegate(self) self.table_view.setItemDelegateForColumn(index+3, symbol_delegate) line_delegate = LineColumnDelegate(self) self.table_view.setItemDelegateForColumn(index+1, line_delegate) color_delegate = ColorColumnDelegate(self) self.table_view.setItemDelegateForColumn(index, color_delegate) @Slot() def addCurve(self): self.table_model.append() @Slot() def removeSelectedCurve(self): self.table_model.removeAtIndex(self.table_view.currentIndex()) @Slot(QItemSelection, QItemSelection) def handleSelectionChange(self, selected, deselected): self.remove_button.setEnabled( self.table_view.selectionModel().hasSelection()) @Slot(QModelIndex) def handleDoubleClick(self, index): if self.table_model.needsColorDialog(index): # The table model returns a QBrush for BackgroundRole, not a QColor init_color = self.table_model.data(index, Qt.BackgroundRole).color() color = QColorDialog.getColor(init_color, self) if color.isValid(): self.table_model.setData(index, color, role=Qt.EditRole) @Slot() def saveChanges(self): formWindow = QDesignerFormWindowInterface.findFormWindow(self.plot) if formWindow: formWindow.cursor().setProperty("curves", self.plot.curves) self.accept()
class LiveFilterDialog(QDialog): JS_HIDE = """ count = 0; searchResultCount = document.getElementById("searchResultCount"); divs = document.querySelectorAll("div"); for (var i = 0, len = divs.length; i < len; i++) {{ div = divs[i]; div.hidden = {0}; count++; }}; if (searchResultCount) {{ searchResultCount.innerHTML = count; }} """ JS_SHOW = """ wordSets = [{0}]; count = 0; searchResultCount = document.getElementById("searchResultCount"); divs = document.querySelectorAll("div"); for (var i=0, len=divs.length; i < len; i++) {{ div = divs[i]; var found = true; for (var j=0, len2=wordSets.length; j < len2; j++) {{ wordSet = wordSets[j]; var regex; if (wordSet.startsWith("'")) {{ wordSet = wordSet.replace("'", ""); wordSet = wordSet.replace("'", ""); regex = new RegExp(wordSet); }} else {{ regex = new RegExp(wordSet, "i"); }} found &= regex.test(div.innerHTML); }} if (found) {{ div.hidden = false; count++; }} }}; if (searchResultCount) {{ searchResultCount.innerHTML = count; }} """ def __init__(self, parent): super().__init__() self.parent = parent self.setWindowTitle(config.thisTranslation["liveFilter"]) self.setMinimumSize(400, 400) self.selectedFilter = None self.selectedPattern = None self.settingBibles = False self.db = LiveFilterSqlite() self.filters = None self.saveReadFormattedBibles = config.readFormattedBibles if config.readFormattedBibles: self.parent.disableBiblesInParagraphs() self.setupUI() def setupUI(self): mainLayout = QVBoxLayout() title = QLabel(config.thisTranslation["liveFilter"]) mainLayout.addWidget(title) self.filtersTable = QTableView() self.filtersTable.setEnabled(True) self.filtersTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.filtersTable.setSortingEnabled(True) self.dataViewModel = QStandardItemModel(self.filtersTable) self.filtersTable.setModel(self.dataViewModel) self.dataViewModel.itemChanged.connect(self.filterSelectionChanged) self.selectionModel = self.filtersTable.selectionModel() self.selectionModel.selectionChanged.connect(self.handleSelection) mainLayout.addWidget(self.filtersTable) self.reloadFilters() buttonsLayout = QHBoxLayout() addButton = QPushButton(config.thisTranslation["add"]) addButton.clicked.connect(self.addNewFilter) buttonsLayout.addWidget(addButton) removeButton = QPushButton(config.thisTranslation["remove"]) removeButton.clicked.connect(self.removeFilter) buttonsLayout.addWidget(removeButton) editButton = QPushButton(config.thisTranslation["edit"]) editButton.clicked.connect(self.editFilter) buttonsLayout.addWidget(editButton) importButton = QPushButton(config.thisTranslation["import"]) importButton.clicked.connect(self.importFile) buttonsLayout.addWidget(importButton) buttonsLayout.addStretch() mainLayout.addLayout(buttonsLayout) buttons = QDialogButtonBox.Ok self.buttonBox = QDialogButtonBox(buttons) self.buttonBox.accepted.connect(self.accept) self.buttonBox.accepted.connect(self.close) self.buttonBox.rejected.connect(self.reject) mainLayout.addWidget(self.buttonBox) self.setLayout(mainLayout) def close(self): pass def reloadFilters(self): self.filters = self.db.getAll() self.dataViewModel.clear() rowCount = 0 for bible, description in self.filters: item = QStandardItem(bible) item.setToolTip(bible) item.setCheckable(True) self.dataViewModel.setItem(rowCount, 0, item) item = QStandardItem(description) self.dataViewModel.setItem(rowCount, 1, item) rowCount += 1 self.dataViewModel.setHorizontalHeaderLabels([ config.thisTranslation["filter2"], config.thisTranslation["pattern"] ]) self.filtersTable.resizeColumnsToContents() def handleSelection(self, selected, deselected): for item in selected: row = item.indexes()[0].row() filter = self.dataViewModel.item(row, 0) self.selectedFilter = filter.text() pattern = self.dataViewModel.item(row, 1) self.selectedPattern = pattern.text() def filterSelectionChanged(self, item): try: numChecked = 0 for index in range(self.dataViewModel.rowCount()): item = self.dataViewModel.item(index) if item.checkState() == Qt.Checked: numChecked += 1 if numChecked == 0: config.mainWindow.studyPage.runJavaScript( self.JS_HIDE.format("false")) else: sets = [] config.mainWindow.studyPage.runJavaScript( self.JS_HIDE.format("true")) for index in range(self.dataViewModel.rowCount()): item = self.dataViewModel.item(index) if item.checkState() == Qt.Checked: sets.append('"{0}"'.format(self.filters[index][1])) wordSets = ",".join(sets) js = self.JS_SHOW.format(wordSets) config.mainWindow.studyPage.runJavaScript(js) except Exception as e: print(str(e)) def addNewFilter(self): fields = [(config.thisTranslation["filter2"], ""), (config.thisTranslation["pattern"], "")] dialog = MultiLineInputDialog("New Filter", fields) if dialog.exec(): data = dialog.getInputs() self.db.insert(data[0], data[1]) self.reloadFilters() def removeFilter(self): reply = QMessageBox.question( self, "Delete", 'Delete {0} {1}'.format(self.selectedFilter, config.thisTranslation["filter2"]), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.db.delete(self.selectedFilter) self.reloadFilters() def editFilter(self): fields = [(config.thisTranslation["filter2"], self.selectedFilter), (config.thisTranslation["pattern"], self.selectedPattern)] dialog = MultiLineInputDialog("Edit Filter", fields) if dialog.exec(): data = dialog.getInputs() self.db.delete(self.selectedFilter) self.db.insert(data[0], data[1]) self.reloadFilters() def importFile(self): options = QFileDialog.Options() filename, filtr = QFileDialog.getOpenFileName( self, config.thisTranslation["import"], config.thisTranslation["liveFilter"], "File (*.*)", "", options) if filename: try: with open(filename, errors='ignore') as f: for line in f: data = line.split(":::") filter = data[0].strip() pattern = data[1].strip() if self.db.checkFilterExists(filter): self.db.delete(filter) self.db.insert(filter, pattern) except Exception as e: print(e) self.reloadFilters()
class SampleLogsView(QSplitter): """Sample Logs View This contains a table of the logs, a plot of the currently selected logs, and the statistics of the selected log. """ def __init__(self, presenter, parent = None, name = '', isMD=False, noExp = 0): super(SampleLogsView, self).__init__(parent) self.presenter = presenter self.setWindowTitle("{} sample logs".format(name)) self.setWindowFlags(Qt.Window) self.setAttribute(Qt.WA_DeleteOnClose, True) # left hand side self.frame_left = QFrame() layout_left = QVBoxLayout() # add a spin box for MD workspaces if isMD: layout_mult_expt_info = QHBoxLayout() layout_mult_expt_info.addWidget(QLabel("Experiment Info #")) self.experimentInfo = QSpinBox() self.experimentInfo.setMaximum(noExp-1) self.experimentInfo.valueChanged.connect(self.presenter.changeExpInfo) layout_mult_expt_info.addWidget(self.experimentInfo) layout_mult_expt_info.addSpacerItem(QSpacerItem(10, 10, QSizePolicy.Expanding)) layout_left.addLayout(layout_mult_expt_info) # Create sample log table self.table = QTableView() self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.doubleClicked.connect(self.presenter.doubleClicked) self.table.contextMenuEvent = self.tableMenu layout_left.addWidget(self.table) self.frame_left.setLayout(layout_left) self.addWidget(self.frame_left) #right hand side self.frame_right = QFrame() layout_right = QVBoxLayout() #Add full_time and experimentinfo options layout_options = QHBoxLayout() if isMD: layout_options.addWidget(QLabel("Experiment Info #")) self.experimentInfo = QSpinBox() self.experimentInfo.setMaximum(noExp-1) self.experimentInfo.valueChanged.connect(self.presenter.changeExpInfo) layout_options.addWidget(self.experimentInfo) #check boxes self.full_time = QCheckBox("Relative Time") self.full_time.setToolTip( "Shows relative time in seconds from the start of the run.") self.full_time.setChecked(True) self.full_time.stateChanged.connect(self.presenter.plot_logs) layout_options.addWidget(self.full_time) self.show_filtered = QCheckBox("Filtered Data") self.show_filtered.setToolTip( "Filtered data only shows data while running and in this period.\nInvalid values are also filtered.") self.show_filtered.setChecked(True) self.show_filtered.stateChanged.connect(self.presenter.filtered_changed) layout_options.addWidget(self.show_filtered) self.spaceItem = QSpacerItem(10, 10, QSizePolicy.Expanding) layout_options.addSpacerItem(self.spaceItem) layout_right.addLayout(layout_options) # Sample log plot self.fig = Figure() self.canvas = FigureCanvas(self.fig) self.canvas.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding) self.canvas.mpl_connect('button_press_event', self.presenter.plot_clicked) self.ax = self.fig.add_subplot(111, projection='mantid') layout_right.addWidget(self.canvas) # Sample stats self.create_stats_widgets() layout_stats = QFormLayout() layout_stats.addRow('', QLabel("Log Statistics")) layout_stats.addRow('Min:', self.stats_widgets["minimum"]) layout_stats.addRow('Max:', self.stats_widgets["maximum"]) layout_stats.addRow('Time Avg:', self.stats_widgets["time_mean"]) layout_stats.addRow('Time Std Dev:', self.stats_widgets["time_standard_deviation"]) layout_stats.addRow('Mean (unweighted):', self.stats_widgets["mean"]) layout_stats.addRow('Median (unweighted):', self.stats_widgets["median"]) layout_stats.addRow('Std Dev:', self.stats_widgets["standard_deviation"]) layout_stats.addRow('Duration:', self.stats_widgets["duration"]) layout_right.addLayout(layout_stats) self.frame_right.setLayout(layout_right) self.addWidget(self.frame_right) self.setStretchFactor(0,1) self.resize(1200,800) self.show() def closeEvent(self, event): self.deleteLater() super(SampleLogsView, self).closeEvent(event) def tableMenu(self, event): """Right click menu for table, can plot or print selected logs""" menu = QMenu(self) plotAction = menu.addAction("Plot selected") plotAction.triggered.connect(self.presenter.new_plot_logs) plotAction = menu.addAction("Print selected") plotAction.triggered.connect(self.presenter.print_selected_logs) menu.exec_(event.globalPos()) def set_model(self, model): """Set the model onto the table""" self.model = model self.table.setModel(self.model) self.table.resizeColumnsToContents() self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self.table.selectionModel().selectionChanged.connect(self.presenter.update) def show_plot_and_stats(self, show_plot_and_stats): """sets wether the plot and stats section should be visible""" if self.frame_right.isVisible() != show_plot_and_stats: # the desired state is nor the current state self.setUpdatesEnabled(False) current_width = self.frame_right.width() if current_width: self.last_width = current_width else: current_width = self.last_width if show_plot_and_stats: self.resize(self.width() + current_width, self.height()) else: self.resize(self.width() - current_width, self.height()) self.frame_right.setVisible(show_plot_and_stats) self.setUpdatesEnabled(True) def plot_selected_logs(self, ws, exp, rows): """Update the plot with the selected rows""" if self.frame_right.isVisible(): self.ax.clear() self.create_ax_by_rows(self.ax, ws, exp, rows) try: self.fig.canvas.draw() except ValueError as ve: #this can throw an error if the plot has recently been hidden, but the error does not matter if not str(ve).startswith("Image size of"): raise def new_plot_selected_logs(self, ws, exp, rows): """Create a new plot, in a separate window for selected rows""" fig, ax = plt.subplots(subplot_kw={'projection': 'mantid'}) self.create_ax_by_rows(ax, ws, exp, rows) fig.show() def create_ax_by_rows(self, ax, ws, exp, rows): """Creates the plots for given rows onto axis ax""" for row in rows: log_text = self.get_row_log_name(row) ax.plot(ws, LogName=log_text, label=log_text, FullTime=not self.full_time.isChecked(), Filtered=self.show_filtered.isChecked(), ExperimentInfo=exp) ax.set_ylabel('') if ax.get_legend_handles_labels()[0]: ax.legend() def set_log_controls(self,are_logs_filtered): """Sets log specific settings based on the log clicked on""" self.show_filtered.setEnabled(are_logs_filtered) def get_row_log_name(self, i): """Returns the log name of particular row""" return str(self.model.item(i, 0).text()) def get_exp(self): """Get set experiment info number""" return self.experimentInfo.value() def get_selected_row_indexes(self): """Return a list of selected row from table""" return [row.row() for row in self.table.selectionModel().selectedRows()] def set_selected_rows(self, rows): """Set seleceted rows in table""" mode = QItemSelectionModel.Select | QItemSelectionModel.Rows for row in rows: self.table.selectionModel().select(self.model.index(row, 0), mode) def create_stats_widgets(self): """Creates the statistics widgets""" self.stats_widgets = {"minimum": QLineEdit(), "maximum": QLineEdit(), "mean": QLineEdit(), "median": QLineEdit(), "standard_deviation": QLineEdit(), "time_mean": QLineEdit(), "time_standard_deviation": QLineEdit(), "duration": QLineEdit()} for widget in self.stats_widgets.values(): widget.setReadOnly(True) def set_statistics(self, stats): """Updates the statistics widgets from stats dictionary""" for param in self.stats_widgets.keys(): self.stats_widgets[param].setText('{:.6}'.format(getattr(stats, param))) def clear_statistics(self): """Clears the values in statistics widgets""" for widget in self.stats_widgets.values(): widget.clear()
class SampleLogsView(QSplitter): """Sample Logs View This contains a table of the logs, a plot of the currently selected logs, and the statistics of the selected log. """ def __init__(self, presenter, parent = None, name = '', isMD=False, noExp = 0): super(SampleLogsView, self).__init__(parent) self.presenter = presenter self.setWindowTitle("{} sample logs".format(name)) self.setWindowFlags(Qt.Window) # Create sample log table self.table = QTableView() self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.clicked.connect(self.presenter.clicked) self.table.doubleClicked.connect(self.presenter.doubleClicked) self.table.contextMenuEvent = self.tableMenu self.addWidget(self.table) frame_right = QFrame() layout_right = QVBoxLayout() #Add full_time and experimentinfo options layout_options = QHBoxLayout() if isMD: layout_options.addWidget(QLabel("Experiment Info #")) self.experimentInfo = QSpinBox() self.experimentInfo.setMaximum(noExp-1) self.experimentInfo.valueChanged.connect(self.presenter.changeExpInfo) layout_options.addWidget(self.experimentInfo) self.full_time = QCheckBox("Relative Time") self.full_time.setChecked(True) self.full_time.stateChanged.connect(self.presenter.plot_logs) layout_options.addWidget(self.full_time) layout_right.addLayout(layout_options) # Sample log plot self.fig = Figure() self.canvas = FigureCanvas(self.fig) self.canvas.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding) self.canvas.mpl_connect('button_press_event', self.presenter.plot_clicked) self.ax = self.fig.add_subplot(111, projection='mantid') layout_right.addWidget(self.canvas) # Sample stats self.create_stats_widgets() layout_stats = QFormLayout() layout_stats.addRow('', QLabel("Log Statistics")) layout_stats.addRow('Min:', self.stats_widgets["minimum"]) layout_stats.addRow('Max:', self.stats_widgets["maximum"]) layout_stats.addRow('Mean:', self.stats_widgets["mean"]) layout_stats.addRow('Median:', self.stats_widgets["median"]) layout_stats.addRow('Std Dev:', self.stats_widgets["standard_deviation"]) layout_stats.addRow('Time Avg:', self.stats_widgets["time_mean"]) layout_stats.addRow('Time Std Dev:', self.stats_widgets["time_standard_deviation"]) layout_stats.addRow('Duration:', self.stats_widgets["duration"]) layout_right.addLayout(layout_stats) frame_right.setLayout(layout_right) self.addWidget(frame_right) self.setStretchFactor(0,1) self.resize(1200,800) self.show() def tableMenu(self, event): """Right click menu for table, can plot or print selected logs""" menu = QMenu(self) plotAction = menu.addAction("Plot selected") plotAction.triggered.connect(self.presenter.new_plot_logs) plotAction = menu.addAction("Print selected") plotAction.triggered.connect(self.presenter.print_selected_logs) menu.exec_(event.globalPos()) def set_model(self, model): """Set the model onto the table""" self.model = model self.table.setModel(self.model) self.table.resizeColumnsToContents() self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) def plot_selected_logs(self, ws, exp, rows): """Update the plot with the selected rows""" self.ax.clear() self.create_ax_by_rows(self.ax, ws, exp, rows) self.fig.canvas.draw() def new_plot_selected_logs(self, ws, exp, rows): """Create a new plot, in a separate window for selected rows""" fig, ax = plt.subplots(subplot_kw={'projection': 'mantid'}) self.create_ax_by_rows(ax, ws, exp, rows) fig.show() def create_ax_by_rows(self, ax, ws, exp, rows): """Creates the plots for given rows onto axis ax""" for row in rows: log_text = self.get_row_log_name(row) ax.plot(ws, LogName=log_text, label=log_text, marker='.', FullTime=not self.full_time.isChecked(), ExperimentInfo=exp) ax.set_ylabel('') if ax.get_legend_handles_labels()[0]: ax.legend() def get_row_log_name(self, i): """Returns the log name of particular row""" return str(self.model.item(i, 0).text()) def get_exp(self): """Get set experiment info number""" return self.experimentInfo.value() def get_selected_row_indexes(self): """Return a list of selected row from table""" return [row.row() for row in self.table.selectionModel().selectedRows()] def set_selected_rows(self, rows): """Set seleceted rows in table""" mode = QItemSelectionModel.Select | QItemSelectionModel.Rows for row in rows: self.table.selectionModel().select(self.model.index(row, 0), mode) def create_stats_widgets(self): """Creates the statistics widgets""" self.stats_widgets = {"minimum": QLineEdit(), "maximum": QLineEdit(), "mean": QLineEdit(), "median": QLineEdit(), "standard_deviation": QLineEdit(), "time_mean": QLineEdit(), "time_standard_deviation": QLineEdit(), "duration": QLineEdit()} for widget in self.stats_widgets.values(): widget.setReadOnly(True) def set_statistics(self, stats): """Updates the statistics widgets from stats dictionary""" for param in self.stats_widgets.keys(): self.stats_widgets[param].setText('{:.6}'.format(getattr(stats, param))) def clear_statistics(self): """Clears the values in statistics widgets""" for widget in self.stats_widgets.values(): widget.clear()
class LayoutSettingsDialog(QDialog): """Layout settings dialog""" def __init__(self, parent, names, order, active): super(LayoutSettingsDialog, self).__init__(parent) # variables self._parent = parent self._selection_model = None self.names = names self.order = order self.active = active # widgets self.button_move_up = QPushButton(_('Move Up')) self.button_move_down = QPushButton(_('Move Down')) self.button_delete = QPushButton(_('Delete Layout')) self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.group_box = QGroupBox(_("Layout Display and Order")) self.table = QTableView(self) self.ok_button = self.button_box.button(QDialogButtonBox.Ok) self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel) self.cancel_button.setDefault(True) self.cancel_button.setAutoDefault(True) # widget setup self.dialog_size = QSize(300, 200) self.setMinimumSize(self.dialog_size) self.setFixedSize(self.dialog_size) self.setWindowTitle('Layout Settings') self.table.setModel(LayoutModel(self.table, order, active)) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setSelectionMode(QAbstractItemView.SingleSelection) self.table.verticalHeader().hide() self.table.horizontalHeader().hide() self.table.setAlternatingRowColors(True) self.table.setShowGrid(False) self.table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table.horizontalHeader().setStretchLastSection(True) self.table.setColumnHidden(1, True) # need to keep a reference for pyside not to segfault! self._selection_model = self.table.selectionModel() # layout buttons_layout = QVBoxLayout() buttons_layout.addWidget(self.button_move_up) buttons_layout.addWidget(self.button_move_down) buttons_layout.addStretch() buttons_layout.addWidget(self.button_delete) group_layout = QHBoxLayout() group_layout.addWidget(self.table) group_layout.addLayout(buttons_layout) self.group_box.setLayout(group_layout) layout = QVBoxLayout() layout.addWidget(self.group_box) layout.addWidget(self.button_box) self.setLayout(layout) # signals and slots self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.close) self.button_delete.clicked.connect(self.delete_layout) self.button_move_up.clicked.connect(lambda: self.move_layout(True)) self.button_move_down.clicked.connect(lambda: self.move_layout(False)) self.table.model().dataChanged.connect( lambda: self.selection_changed(None, None)) self._selection_model.selectionChanged.connect( lambda: self.selection_changed(None, None)) # focus table index = self.table.model().index(0, 0) self.table.setCurrentIndex(index) self.table.setFocus() def delete_layout(self): """ """ names, order, active = self.names, self.order, self.order name = from_qvariant(self.table.selectionModel().currentIndex().data(), to_text_string) if name in names: index = names.index(name) # In case nothing has focus in the table if index != -1: order.remove(name) names[index] = None if name in active: active.remove(name) self.names, self.order, self.active = names, order, active self.table.model().set_data(order, active) index = self.table.model().index(0, 0) self.table.setCurrentIndex(index) self.table.setFocus() self.selection_changed(None, None) if len(order) == 0: self.button_move_up.setDisabled(True) self.button_move_down.setDisabled(True) self.button_delete.setDisabled(True) def move_layout(self, up=True): """ """ names, order, active = self.names, self.order, self.active row = self.table.selectionModel().currentIndex().row() row_new = row if up: row_new -= 1 else: row_new += 1 order[row], order[row_new] = order[row_new], order[row] self.order = order self.table.model().set_data(order, active) index = self.table.model().index(row_new, 0) self.table.setCurrentIndex(index) self.table.setFocus() self.selection_changed(None, None) def selection_changed(self, selection, deselection): """ """ model = self.table.model() index = self.table.currentIndex() row = index.row() order, names, active = self.order, self.names, self.active state = model.row(row)[1] name = model.row(row)[0] # Check if name changed if name not in names: # Did changed if row != -1: # row == -1, means no items left to delete old_name = order[row] order[row] = name names[names.index(old_name)] = name if old_name in active: active[active.index(old_name)] = name # Check if checbox clicked if state: if name not in active: active.append(name) else: if name in active: active.remove(name) self.active = active self.button_move_up.setDisabled(False) self.button_move_down.setDisabled(False) if row == 0: self.button_move_up.setDisabled(True) if row == len(names) - 1: self.button_move_down.setDisabled(True) if len(names) == 0: self.button_move_up.setDisabled(True) self.button_move_down.setDisabled(True)
class CompositionElementalWidget(_CompositionWidget): class _CompositionModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self.composition = OrderedDict() def rowCount(self, *args, **kwargs): return len(self.composition) def columnCount(self, *args, **kwargs): return 2 def data(self, index, role): if not index.isValid() or \ not (0 <= index.row() < len(self.composition)): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role != Qt.DisplayRole: return None z, fraction = list(self.composition.items())[index.row()] column = index.column() if column == 0: if z is None: return 'none' else: return str(get_symbol(z)) elif column == 1: return str(fraction) def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'Element' elif section == 1: return 'Fraction' 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.composition)): return False z = list(self.composition.keys())[index.row()] column = index.column() if column == 0: if value in self.composition: return False fraction = self.composition.pop(z) self.composition[value] = fraction elif column == 1: self.composition[z] = float(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) if None in self.composition: return False self.composition[None] = 0.0 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, count + row - 1) keys = list(self.composition.keys()) for key in keys[:row] + keys[row + count:]: self.composition.pop(key) self.endRemoveRows() return True class _CompositionDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): column = index.column() if column == 0: editor = PeriodicTableDialog(parent) editor.setMultipleSelection(False) editor.setRequiresSelection(True) return editor elif column == 1: editor = QLineEdit(parent) editor.setValidator(QDoubleValidator()) return editor else: return QItemDelegate.createEditor(self, parent, option, index) def setEditorData(self, editor, index): text = index.model().data(index, Qt.DisplayRole) column = index.column() if column == 0: if text != 'none': editor.setSelection(text) elif column == 1: editor.setText(text) else: QItemDelegate.setEditorData(self, editor, index) def setModelData(self, editor, model, index): column = index.column() if column == 0: model.setData(index, editor.selection()) elif column == 1: model.setData(index, editor.text()) else: return QItemDelegate.setModelData(self, editor, model, index) def __init__(self, parent=None): _CompositionWidget.__init__(self, CompositionElemental, parent) def _init_ui(self): # Widgets model = self._CompositionModel() self._table = QTableView() self._table.setModel(model) self._table.setItemDelegate(self._CompositionDelegate(self)) self._table.horizontalHeader().setStretchLastSection(True) self._toolbar = QToolBar() action_add = self._toolbar.addAction(getIcon("list-add"), "Add layer") action_remove = self._toolbar.addAction(getIcon("list-remove"), "Remove layer") # Layouts layout = _CompositionWidget._init_ui(self) layout.addRow(self._table) layout.addRow(self._toolbar) # Signals action_add.triggered.connect(self._on_add) action_remove.triggered.connect(self._on_remove) model.dataChanged.connect(self.edited) model.rowsInserted.connect(self.edited) model.rowsRemoved.connect(self.edited) return layout def _on_add(self): index = self._table.selectionModel().currentIndex() model = self._table.model() model.insertRows(index.row() + 1) def _on_remove(self): selection = self._table.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Window layer", "Select a layer") return model = self._table.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): model.removeRow(row) def _create_parameter(self): return self.CLASS('wt%') def parameter(self, parameter=None): parameter = _CompositionWidget.parameter(self, parameter) parameter.update(self._table.model().composition) return parameter def setParameter(self, condition): _CompositionWidget.setParameter(self, condition) self._table.model().composition.update(condition) self._table.model().reset() def setReadOnly(self, state): _CompositionWidget.setReadOnly(self, state) if state: trigger = QTableView.EditTrigger.NoEditTriggers else: trigger = QTableView.EditTrigger.AllEditTriggers self._table.setEditTriggers(trigger) self._toolbar.setEnabled(not state) def isReadOnly(self): return _CompositionWidget.isReadOnly(self) and \ self._table.editTriggers() == QTableView.EditTrigger.NoEditTriggers and \ not self._toolbar.isEnabled()
class ConfigManagerWindow(SiriusMainWindow): """Window to manage offline configuration of BO and SI devices. This window allows the user to create new configurations as well as interpolate or apply a tune or chromaticity delta to a configuration. """ NEW_CONFIGURATION = 0 def __init__(self, config_type, parent=None): """Init UI.""" super(ConfigManagerWindow, self).__init__(parent) self._config_type = config_type self._model = ConfigModel(self._config_type) self._delegate = ConfigDelegate() self._setup_ui() self.ld_cur_state_btn.clicked.connect(self._loadCurrentConfiguration) self.ld_config_btn.clicked.connect(self._addConfiguration) self.delete_config_btn.clicked.connect(self._removeConfiguration) self.setGeometry(100, 100, 1600, 900) self.setWindowTitle("Configuration Manager") self.show() def _setup_ui(self): self.central_widget = QWidget() self.central_widget.layout = QHBoxLayout() self.button_box = QVBoxLayout() self.ld_cur_state_btn = QPushButton("Load Current State") self.ld_config_btn = QPushButton("Load Configuration") self.ld_config_btn.setShortcut(QKeySequence.New) self.delete_config_btn = QPushButton("Delete Configuration") self.button_box.addWidget(self.delete_config_btn) self.button_box.addWidget(self.ld_config_btn) self.button_box.addWidget(self.ld_cur_state_btn) self.button_box.addStretch() # TableView self.table = QTableView(self) self.table.setModel(self._model) self.table.setItemDelegate(self._delegate) # self.table.setSelectionBehavior(QAbstractItemView.SelectColumns) self.table.setContextMenuPolicy(Qt.CustomContextMenu) self.table.customContextMenuRequested.connect(self._showHeaderMenu) self.table.resizeColumnsToContents() self.table.resizeRowsToContents() # TableView Headers self.headers = self.table.horizontalHeader() self.headers.setContextMenuPolicy(Qt.CustomContextMenu) self.headers.customContextMenuRequested.connect(self._showHeaderMenu) self.central_widget.layout.addLayout(self.button_box) self.central_widget.layout.addWidget(self.table) self.central_widget.setLayout(self.central_widget.layout) # Set widget self.setCentralWidget(self.central_widget) def closeEvent(self, event): """Close window. The user is warned if there are any unsaved changes. """ columns = list(range(len(self._model.configurations))) columns.sort(reverse=True) if not self._closeConfigurations(columns): event.ignore() def keyPressEvent(self, event): """Override keyPressEvent. Ctrl+S - Save changes Ctrl+W - Close configuration on focus F2 - Rename configuration on focus Ctrl+Z - Undo Ctrl+R - Redo """ if event.key() == Qt.Key_S: self._saveChanges() return if event.key() == Qt.Key_W: self._closeConfigurationOnFocus() return if event.key() == Qt.Key_F2: self._renameOnFocus() return if event.key() == Qt.Key_Z: print(self._model._undo) if len(self._model._undo) > 0: self._model._undo.pop()[1]() return if event.key() == Qt.Key_R: if len(self._model._redo) > 0: self._model._redo.pop()[1]() return @Slot(QPoint) def _showHeaderMenu(self, point): column = self.headers.logicalIndexAt(point.x()) if column == -1: return menu = QMenu(self) # Actions cols = self.table.selectionModel().selectedColumns() if len(cols) != 2 or column not in [col.column() for col in cols]: self.table.selectColumn(column) menu.aboutToHide.connect(lambda: self.table.clearSelection()) save = QAction("Save", menu) save.triggered.connect(lambda: self._saveConfiguration(column)) save_all = QAction("Save all", menu) save_all.triggered.connect(lambda: self._saveChanges()) save_all.setShortcut(QKeySequence.Save) rename = QAction("Rename", menu) rename.triggered.connect(lambda: self._renameConfiguration(column)) close = QAction("Close", menu) close.triggered.connect(lambda: self._closeConfiguration(column)) close.setShortcut(QKeySequence.Close) close_right = QAction("Close to the right", menu) close_right.triggered.connect( lambda: self._closeConfigurationsToTheRight(column)) close_others = QAction("Close other", menu) close_others.triggered.connect( lambda: self._closeOtherConfigurations(column)) close_all = QAction("Close all", menu) close_all.triggered.connect(lambda: self._closeAllConfigurations()) tune = QAction("Tune", menu) tune.triggered.connect(lambda: self._tuneConfiguration(column)) menu.addActions([save, save_all]) menu.addSeparator() menu.addActions([rename]) menu.addSeparator() menu.addActions([close, close_right, close_others, close_all]) menu.addSeparator() menu.addActions([tune]) else: bar = QAction("Interpolate", menu) bar.triggered.connect(lambda: self._barConfiguration(cols)) menu.addAction(bar) vheader_offset = self.table.verticalHeader().width() point.setX(point.x() + vheader_offset) menu.popup(self.mapToGlobal(point)) # ContextMenu Actions @Slot(int) def _saveConfiguration(self, column): try: self._model.saveConfiguration(column) return True except Exception as e: QMessageBox(QMessageBox.Warning, "Failed to save data", "{}, {}".format(e, type(e))).exec_() return False @Slot(int) def _renameConfiguration(self, column): new_name, ok = QInputDialog.getText(self, "New name", "Rename to:") if ok and new_name: return self._model.renameConfiguration(column, new_name) @Slot(int) def _closeConfiguration(self, column): self._closeConfigurations([column]) @Slot(int) def _closeConfigurationsToTheRight(self, column): columns = list() i = len(self._model.configurations) - 1 while i > column: columns.append(i) i -= 1 self._closeConfigurations(columns) @Slot(int) def _closeOtherConfigurations(self, column): columns = list() i = len(self._model.configurations) - 1 while i >= 0: if i == column: i -= 1 continue columns.append(i) i -= 1 self._closeConfigurations(columns) @Slot() def _closeAllConfigurations(self): columns = list() i = len(self._model.configurations) - 1 while i >= 0: columns.append(i) i -= 1 self._closeConfigurations(columns) def _closeConfigurations(self, columns): save = self._maybeSaveChanges(columns) if save == QMessageBox.Discard: for column in columns: self._model.cleanUndo(column) self._model.closeConfiguration(column) return True elif save == QMessageBox.Save: for column in columns: if self._saveConfiguration(column): self._model.cleanUndo(column) self._model.closeConfiguration(column) else: return False return True else: return False @Slot(int) def _tuneConfiguration(self, column): dlg = TuneDlg(self) ok1 = dlg.exec_() if ok1: # Get Matrix Calculate deltaK and show to user tune = [dlg.tune_x.value(), dlg.tune_y.value()] try: inv_matrix = self._model.getTuneMatrix() except Exception as e: self._showWarningBox("{}".format(e), "Failed to retrieve tune matrix") else: delta_f = tune[0] * inv_matrix[0][0] + tune[1] * inv_matrix[0][ 1] delta_d = tune[0] * inv_matrix[1][0] + tune[1] * inv_matrix[1][ 1] # config_name, ok2 = QInputDialog.getText( # self, "Select value", "New Configuration Name:") # if ok2: # if not config_name: proceed = QMessageBox(QMessageBox.Question, "Delta K", ("\u0394K<sub>d</sub> = {:1.3f}<br>" "\u0394K<sub>f</sub> = {:1.3f}<br>" "Proceed?").format(delta_d, delta_f), QMessageBox.Ok | QMessageBox.Cancel, self).exec_() if proceed == QMessageBox.Ok: config_name = self._getNextName() self._model.deriveConfiguration(config_name, column, ConfigModel.TUNE, [delta_d, delta_f]) @Slot(int) def _barConfiguration(self, cols): if len(cols) != 2: raise SystemError("Must interpolate 2 columns") new_name, ok = QInputDialog.getText(self, "New name", "Rename to:") if ok: if not new_name: new_name = self._getNextName() self._model.interpolateConfiguration(new_name, cols[0].column(), cols[1].column()) # Window menu slots @Slot() def _addConfiguration(self): try: configs = self._model.getConfigurations() except Exception as e: self._showWarningBox(e, "Failed to retrieve configurations") else: if configs: options = [item["name"] for item in configs] config_name, ok = QInputDialog.getItem( self, "Available Configurations", "Select a configuration:", options, 0, False) if ok and config_name: if not self._isConfigurationLoaded(config_name): self._model.loadConfiguration(name=config_name) else: QMessageBox( QMessageBox.Information, "Configuration already loaded", "Configuration is already loaded.").exec_() # Highlight new column; or the one that is already loaded col = self._model.getConfigurationColumn(config_name) self.table.selectColumn(col) else: self._showMessageBox("No configuration found") return @Slot() def _removeConfiguration(self): try: configs = self._model.getConfigurations() except Exception as e: self._showWarningBox(e, "Failed to retrieve configurations") else: if configs: # Show configs available options = [item["name"] for item in configs] config, ok = QInputDialog.getItem(self, "Available Configurations", "Select a configuration:", options, 0, False) if ok and config: # Ask for confirmation if self._isConfigurationLoaded(config): msg = ("Configuration is currenty loaded." "Delete it anyway?") else: msg = ("This will permanently delete configuration {}." "Proceed?").format(config) if self._showDialogBox(msg) == QMessageBox.Cancel: return # Delete configuration config = configs[options.index(config)] try: self._model.deleteConfiguration(config) except Exception as e: self._showWarningBox(e) else: self._showMessageBox( "Configuration {} was deleted.".format( config['name'])) else: self._showMessageBox("No configuration found") return @Slot() def _loadCurrentConfiguration(self): try: t = LoadingThread(self._getNextName(), self._model._vertical_header, self) dlg = \ LoadingDialog("Loading", len(self._model._vertical_header), self) t.taskUpdated.connect(dlg.update) t.taskFinished.connect(dlg.done) t.start() dlg.exec_() except Exception as e: self._showWarningBox("{}".format(e)) # Actions binded with keys def _saveChanges(self): for column in range(len(self._model.configurations)): self._saveConfiguration(column) def _closeConfigurationOnFocus(self): cols = self.table.selectionModel().selectedColumns() columns = list() for col in cols: columns.append(col.column()) columns.sort(reverse=True) self._closeConfigurations(columns) def _renameOnFocus(self): cols = self.table.selectionModel().selectedColumns() if len(cols) == 1: self._renameConfiguration(cols[0].column()) # Helpers def _isConfigurationLoaded(self, config_name): ret = self._model.getConfigurationColumn(config_name) if ret == -1: return False return True def _getNextName(self): # Treat if there already exist saved configuration with this name configs = self._model.getConfigurations(deleted=None) configs = [item["name"] for item in configs] configs.extend([item.name for item in self._model.configurations]) new_name = 'config-{}'.format(self.NEW_CONFIGURATION) while new_name in configs: self.NEW_CONFIGURATION += 1 new_name = 'config-{}'.format(self.NEW_CONFIGURATION) return new_name def _maybeSaveChanges(self, columns): ask_to_save = False for column in columns: if self._model.configurations[column].dirty: ask_to_save = True break # If nothing to save, will close all columns if not ask_to_save: return QMessageBox.Discard # Ask if user wants to save changes msg_box = QMessageBox( QMessageBox.Question, "There are unsaved changes", "Keep changes?", QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, self) return msg_box.exec_() def _showWarningBox(self, message, title="Warning"): QMessageBox(QMessageBox.Warning, title, '{}'.format(message)).exec_() def _showMessageBox(self, message, title="Message"): return QMessageBox(QMessageBox.Information, title, message).exec_() def _showDialogBox(self, message, title="Dialog"): return QMessageBox(QMessageBox.Information, title, message, QMessageBox.Ok | QMessageBox.Cancel).exec_()
class BasePlotCurveEditorDialog(QDialog): """QDialog that is used in Qt Designer to edit the properties of the curves in a waveform plot. This dialog is shown when you double-click the plot, or when you right click it and choose 'edit curves'. This thing is mostly just a wrapper for a table view, with a couple buttons to add and remove curves, and a button to save the changes.""" TABLE_MODEL_CLASS = BasePlotCurvesModel def __init__(self, plot, parent=None): super(BasePlotCurveEditorDialog, self).__init__(parent) self.plot = plot self.setup_ui() self.table_model = self.TABLE_MODEL_CLASS(self.plot) self.table_view.setModel(self.table_model) self.table_model.plot = plot # self.table_view.resizeColumnsToContents() self.add_button.clicked.connect(self.addCurve) self.remove_button.clicked.connect(self.removeSelectedCurve) self.remove_button.setEnabled(False) self.table_view.selectionModel().selectionChanged.connect( self.handleSelectionChange) self.table_view.doubleClicked.connect(self.handleDoubleClick) self.resize(800, 300) def setup_ui(self): self.vertical_layout = QVBoxLayout(self) self.table_view = QTableView(self) self.table_view.setEditTriggers(QAbstractItemView.DoubleClicked) self.table_view.setProperty("showDropIndicator", False) self.table_view.setDragDropOverwriteMode(False) self.table_view.setSelectionMode(QAbstractItemView.SingleSelection) self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.table_view.setSortingEnabled(False) self.table_view.horizontalHeader().setStretchLastSection(True) self.table_view.verticalHeader().setVisible(False) self.table_view.setColumnWidth(0, 160) self.table_view.setColumnWidth(1, 160) self.table_view.setColumnWidth(2, 160) self.vertical_layout.addWidget(self.table_view) self.add_remove_layout = QHBoxLayout() spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.add_remove_layout.addItem(spacer) self.add_button = QPushButton("Add Curve", self) self.add_remove_layout.addWidget(self.add_button) self.remove_button = QPushButton("Remove Curve", self) self.add_remove_layout.addWidget(self.remove_button) self.vertical_layout.addLayout(self.add_remove_layout) self.button_box = QDialogButtonBox(self) self.button_box.setOrientation(Qt.Horizontal) self.button_box.addButton("Done", QDialogButtonBox.AcceptRole) self.vertical_layout.addWidget(self.button_box) self.button_box.accepted.connect(self.saveChanges) self.button_box.rejected.connect(self.reject) self.setWindowTitle("Waveform Curve Editor") def setup_delegate_columns(self, index=2): symbol_delegate = SymbolColumnDelegate(self) self.table_view.setItemDelegateForColumn(index + 3, symbol_delegate) line_delegate = LineColumnDelegate(self) self.table_view.setItemDelegateForColumn(index + 1, line_delegate) color_delegate = ColorColumnDelegate(self) self.table_view.setItemDelegateForColumn(index, color_delegate) @Slot() def addCurve(self): self.table_model.append() @Slot() def removeSelectedCurve(self): self.table_model.removeAtIndex(self.table_view.currentIndex()) @Slot(QItemSelection, QItemSelection) def handleSelectionChange(self, selected, deselected): self.remove_button.setEnabled( self.table_view.selectionModel().hasSelection()) @Slot(QModelIndex) def handleDoubleClick(self, index): if self.table_model.needsColorDialog(index): # The table model returns a QBrush for BackgroundRole, not a QColor init_color = self.table_model.data(index, Qt.BackgroundRole).color() color = QColorDialog.getColor(init_color, self) if color.isValid(): self.table_model.setData(index, color, role=Qt.EditRole) @Slot() def saveChanges(self): formWindow = QDesignerFormWindowInterface.findFormWindow(self.plot) if formWindow: formWindow.cursor().setProperty("curves", self.plot.curves) self.accept()
class WindowWidget(ParameterWidget): class _WindowModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self.layers = [] def rowCount(self, *args, **kwargs): return len(self.layers) def columnCount(self, *args, **kwargs): return 2 def data(self, index, role): if not index.isValid() or not (0 <= index.row() < len(self.layers)): return None if role != Qt.DisplayRole: return None layer = self.layers[index.row()] column = index.column() if column == 0: return layer.material elif column == 1: return '%s' % layer.thickness def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'Material' elif section == 1: return 'Thickness' 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.layers)): return False layer = self.layers[index.row()] column = index.column() if column == 0: layer.material = value elif column == 1: layer.thickness = 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 i in range(count): self.layers.insert(row + i, WindowLayer("unknown", 0.0)) 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) self.layers = self.layers[:row] + self.layers[row + count:] self.endRemoveRows() return True class _WindowDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): column = index.column() if column == 0: return TextAttributeLineEdit(WindowLayer.material, parent) elif column == 1: return NumericalAttributeLineEdit(WindowLayer.thickness, parent) else: return QItemDelegate.createEditor(self, parent, option, index) def setEditorData(self, editor, index): text = index.model().data(index, Qt.DisplayRole) column = index.column() if column == 0: editor.setText(text) elif column == 1: editor.setText(text) else: QItemDelegate.setEditorData(self, editor, index) def setModelData(self, editor, model, index): column = index.column() if column == 0: model.setData(index, editor.text()) elif column == 1: model.setData(index, editor.text()) else: return QItemDelegate.setModelData(self, editor, model, index) def __init__(self, parent=None): ParameterWidget.__init__(self, Window, parent) def _init_ui(self): # Widgets model = self._WindowModel() self._table = QTableView() self._table.setModel(model) self._table.setItemDelegate(self._WindowDelegate(self)) self._table.horizontalHeader().setStretchLastSection(True) self._toolbar = QToolBar() action_add = self._toolbar.addAction(getIcon("list-add"), "Add layer") action_remove = self._toolbar.addAction(getIcon("list-remove"), "Remove layer") # Layouts layout = ParameterWidget._init_ui(self) layout.addRow(self._table) layout.addRow(self._toolbar) # Signals action_add.triggered.connect(self._on_add) action_remove.triggered.connect(self._on_remove) model.dataChanged.connect(self.edited) model.rowsInserted.connect(self.edited) model.rowsRemoved.connect(self.edited) return layout def _on_add(self): index = self._table.selectionModel().currentIndex() model = self._table.model() model.insertRows(index.row() + 1) def _on_remove(self): selection = self._table.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Window layer", "Select a layer") return model = self._table.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): model.removeRow(row) def parameter(self, parameter=None): parameter = ParameterWidget.parameter(self, parameter) parameter.layers.clear() for layer in self._table.model().layers: parameter.append_layer(layer.material, layer.thickness) # copy return parameter def setParameter(self, window): model = self._table.model() model.layers = window.layers model.reset() def window(self): return self.parameter() def setWindow(self, window): self.setParameter(window) def setReadOnly(self, state): ParameterWidget.setReadOnly(self, state) if state: trigger = QTableView.EditTrigger.NoEditTriggers else: trigger = QTableView.EditTrigger.AllEditTriggers self._table.setEditTriggers(trigger) self._toolbar.setEnabled(not state) def isReadOnly(self): return ParameterWidget.isReadOnly(self) and \ self._table.editTriggers() == QTableView.EditTrigger.NoEditTriggers and \ not self._toolbar.isEnabled()