def setupTabs(self): """ Setup the various tabs in the AddressWidget. """ groups = ["ABC", "DEF", "GHI", "JKL", "MNO", "PQR", "STU", "VW", "XYZ"] for group in groups: proxyModel = QSortFilterProxyModel(self) proxyModel.setSourceModel(self.tableModel) proxyModel.setDynamicSortFilter(True) tableView = QTableView() tableView.setModel(proxyModel) tableView.setSortingEnabled(True) tableView.setSelectionBehavior(QAbstractItemView.SelectRows) tableView.horizontalHeader().setStretchLastSection(True) tableView.verticalHeader().hide() tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) tableView.setSelectionMode(QAbstractItemView.SingleSelection) # This here be the magic: we use the group name (e.g. "ABC") to # build the regex for the QSortFilterProxyModel for the group's # tab. The regex will end up looking like "^[ABC].*", only # allowing this tab to display items where the name starts with # "A", "B", or "C". Notice that we set it to be case-insensitive. reFilter = "^[%s].*" % group proxyModel.setFilterRegExp(QRegExp(reFilter, Qt.CaseInsensitive)) proxyModel.setFilterKeyColumn(0) # Filter on the "name" column proxyModel.sort(0, Qt.AscendingOrder) # This prevents an application crash (see: http://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash) viewselectionmodel = tableView.selectionModel() tableView.selectionModel().selectionChanged.connect(self.selectionChanged) self.addTab(tableView, group)
def setupTabs(self): """ Setup the various tabs in the AddressWidget. """ groups = ["ABC", "DEF", "GHI", "JKL", "MNO", "PQR", "STU", "VW", "XYZ"] for group in groups: proxyModel = QSortFilterProxyModel(self) proxyModel.setSourceModel(self.tableModel) proxyModel.setDynamicSortFilter(True) tableView = QTableView() tableView.setModel(proxyModel) tableView.setSortingEnabled(True) tableView.setSelectionBehavior(QAbstractItemView.SelectRows) tableView.horizontalHeader().setStretchLastSection(True) tableView.verticalHeader().hide() tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) tableView.setSelectionMode(QAbstractItemView.SingleSelection) # This here be the magic: we use the group name (e.g. "ABC") to # build the regex for the QSortFilterProxyModel for the group's # tab. The regex will end up looking like "^[ABC].*", only # allowing this tab to display items where the name starts with # "A", "B", or "C". Notice that we set it to be case-insensitive. reFilter = "^[%s].*" % group proxyModel.setFilterRegExp(QRegExp(reFilter, Qt.CaseInsensitive)) proxyModel.setFilterKeyColumn(0) # Filter on the "name" column proxyModel.sort(0, Qt.AscendingOrder) # This prevents an application crash (see: http://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash) viewselectionmodel = tableView.selectionModel() tableView.selectionModel().selectionChanged.connect( self.selectionChanged) self.addTab(tableView, group)
class AddressWidget(QDialog): selectionChanged = Signal(QItemSelection) def __init__(self, parent=None): super(AddressWidget, self).__init__(parent) self.tableModel = TableModel() self.tableView = QTableView() self.setupTable() statusLabel = QLabel("Tabular data view demo") layout = QVBoxLayout() layout.addWidget(self.tableView) layout.addWidget(statusLabel) self.setLayout(layout) self.setWindowTitle("Address Book") self.resize(800,500) # add test data self.populateTestData() def setupTable(self): proxyModel = QSortFilterProxyModel(self) proxyModel.setSourceModel(self.tableModel) proxyModel.setDynamicSortFilter(True) self.tableView.setModel(proxyModel) self.tableView.setSortingEnabled(True) self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) self.tableView.horizontalHeader().setStretchLastSection(True) self.tableView.verticalHeader().hide() self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tableView.setSelectionMode(QAbstractItemView.SingleSelection) proxyModel.setFilterKeyColumn(0) # Filter on the "name" column proxyModel.sort(0, Qt.AscendingOrder) viewselectionmodel = self.tableView.selectionModel() self.tableView.selectionModel().selectionChanged.connect(self.selectionChanged) def populateTestData(self): addresses = [{"name": "John Doe", "address": "Alameda"}, {"name": "Alan Turing", "address": "San Deigo"}, {"name": "Bjarne Stroutsup", "address": "Columbia"}, {"name": "Herb Sutter", "address": "Seattle"}, {"name": "Micheal Konin", "address": "Colorado"}] for i in range(len(addresses)): self.tableModel.insertRows(0) ix = self.tableModel.index(0, 0, QModelIndex()) self.tableModel.setData(ix, addresses[i]["name"], Qt.EditRole) ix = self.tableModel.index(0, 1, QModelIndex()) self.tableModel.setData(ix, addresses[i]["address"], Qt.EditRole) self.tableView.resizeRowToContents(ix.row())
class _ExtractSeriesWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) # {series name: {frame name: [ (Attribute, QPersistentModelIndex) ] self.seriesOptions: Dict[str, Dict[str, List[Tuple[ int, QPersistentModelIndex]]]] = dict() # {frame name: attribute model} self.models: Dict[str, CustomProxyAttributeModel] = dict() self.seriesView = CustomSignalView(parent=self) self.seriesModel = CustomStringListModel(self) self.seriesModel.setHeaderLabel('Series name') self.addSeriesButton = QPushButton('Add', self) self.removeSeriesButton = QPushButton('Remove', self) self.addSeriesButton.clicked.connect(self.addSeries) self.removeSeriesButton.clicked.connect(self.removeSeries) self.seriesView.setModel(self.seriesModel) self.seriesView.setDragDropMode(QTableView.InternalMove) self.seriesView.setDragDropOverwriteMode(False) self.seriesView.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.seriesView.verticalHeader().hide() # Connect selection to change self.seriesView.selectedRowChanged[str, str].connect( self.onSeriesSelectionChanged) # When a series is added it should be immediately edited self.seriesModel.rowAppended.connect(self.editSeriesName) self.seriesModel.rowsInserted.connect(self.checkNoSeries) self.seriesModel.rowsRemoved.connect(self.checkNoSeries) self.workbenchView = WorkbenchView(self, editable=False) self.workbench: WorkbenchModel = None self.workbenchView.selectedRowChanged[str, str].connect( self.onFrameSelectionChanged) self.attributesView = SearchableAttributeTableWidget( self, True, False, False, [Types.Numeric, Types.Ordinal]) firstRowLayout = QHBoxLayout() firstRowLayout.setSpacing(5) selectionGroup = QGroupBox( title= 'Select a time series. Then select the columns to add from the current ' 'datasets', parent=self) firstRowLayout.addWidget(self.seriesView) buttonLayout = QVBoxLayout() buttonLayout.addWidget(self.addSeriesButton) buttonLayout.addWidget(self.removeSeriesButton) firstRowLayout.addLayout(buttonLayout) firstRowLayout.addSpacing(30) firstRowLayout.addWidget(self.workbenchView) firstRowLayout.addSpacing(30) firstRowLayout.addWidget(self.attributesView) selectionGroup.setLayout(firstRowLayout) # Time axis labels model with add/remove buttons self.timeAxisModel = CustomStringListModel(self) self.timeAxisModel.setHeaderLabel('Time labels') self.timeAxisView = CustomSignalView(self) self.timeAxisView.setModel(self.timeAxisModel) self.addTimeButton = QPushButton('Add', self) self.removeTimeButton = QPushButton('Remove', self) self.addTimeButton.clicked.connect(self.addTimeLabel) self.removeTimeButton.clicked.connect(self.removeTimeLabel) self.timeAxisView.setDragDropMode(QTableView.InternalMove) self.timeAxisView.setDragDropOverwriteMode(False) self.timeAxisView.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.timeAxisView.verticalHeader().hide() self.timeAxisModel.rowAppended.connect(self.editTimeLabelName) # Concatenation model self.timeSeriesDataModel = ConcatenatedModel(self) self.timeSeriesDataView = QTableView(self) self.timeSeriesDataView.setSelectionMode(QTableView.NoSelection) self.timeSeriesDataView.setItemDelegateForColumn( 1, ComboBoxDelegate(self.timeAxisModel, self.timeSeriesDataView)) self.timeSeriesDataView.setEditTriggers(QTableView.CurrentChanged | QTableView.DoubleClicked) self.timeSeriesDataView.verticalHeader().hide() # Update the label column when some label changes in the label table self.timeAxisModel.dataChanged.connect( self.timeSeriesDataModel.timeAxisLabelChanged) groupTime = QGroupBox( title= 'Add the time points (ordered) and set the correspondence to every selected column', parent=self) secondRowLayout = QHBoxLayout() secondRowLayout.setSpacing(5) # labelLayout = QVBoxLayout() # lab = QLabel('Here you should define every time point, in the correct order. After adding ' # 'double-click a row to edit the point name and drag rows to reorder them', self) # lab.setWordWrap(True) # labelLayout.addWidget(lab) # labelLayout.addWidget(self.timeAxisView) secondRowLayout.addWidget(self.timeAxisView) timeButtonLayout = QVBoxLayout() timeButtonLayout.addWidget(self.addTimeButton) timeButtonLayout.addWidget(self.removeTimeButton) secondRowLayout.addLayout(timeButtonLayout) secondRowLayout.addSpacing(30) # labelLayout = QVBoxLayout() # lab = QLabel('Every selected column for the current series will be listed here. Click the right ' # 'column of the table to set the time label associated with every original column', # self) # lab.setWordWrap(True) # labelLayout.addWidget(lab) secondRowLayout.addWidget(self.timeSeriesDataView) # secondRowLayout.addLayout(labelLayout) groupTime.setLayout(secondRowLayout) self.outputName = QLineEdit(self) self.warningLabel = MessageLabel(text='', color='orange', icon=QMessageBox.Warning, parent=self) lastRowLayout = QFormLayout() lastRowLayout.addRow('Output variable name:', self.outputName) self.outputName.setPlaceholderText('Output name') lastRowLayout.setVerticalSpacing(0) lastRowLayout.addRow('', self.warningLabel) lastRowLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) self.warningLabel.hide() self.outputName.textChanged.connect(self.checkOutputName) layout = QVBoxLayout(self) layout.addWidget(selectionGroup) layout.addWidget(groupTime) layout.addLayout(lastRowLayout) self.checkNoSeries() def setWorkbench(self, w: WorkbenchModel) -> None: """ Sets the workbench and initialises every attribute model (one for each frame) """ self.workbench = w self.workbenchView.setModel(w) # Set a default name for output if self.workbench: name = 'time_series_{:d}' n = 1 name_n = name.format(n) while name_n in self.workbench.names: n += 1 name_n = name.format(n) self.outputName.setText(name_n) def addSourceFrameModel(self, frameName: str) -> None: if self.workbench: dfModel = self.workbench.getDataframeModelByName(frameName) # Create an attribute model with checkboxes standardModel = AttributeTableModel(self, checkable=True, editable=False, showTypes=True) standardModel.setFrameModel(dfModel) # Create a proxy to filter data in the concatenation customProxy = CustomProxyAttributeModel(self) customProxy.setSourceModel(standardModel) # Add proxy to the list of models self.models[frameName] = customProxy # Add proxy as source model self.timeSeriesDataModel.addSourceModel(customProxy) @Slot() def checkNoSeries(self) -> None: if not self.seriesModel.rowCount(): self.workbenchView.setEnabled(False) self.attributesView.setEnabled(False) self.timeAxisView.setEnabled(False) self.addTimeButton.setEnabled(False) self.removeTimeButton.setEnabled(False) self.timeSeriesDataView.setEnabled(False) else: self.workbenchView.setEnabled(True) self.attributesView.setEnabled(True) self.timeAxisView.setEnabled(True) self.addTimeButton.setEnabled(True) self.removeTimeButton.setEnabled(True) self.timeSeriesDataView.setEnabled(True) def persistOptionsSetForSeries(self, seriesName: str) -> None: if seriesName: seriesValues: Dict[str, List[Tuple[int, QPersistentModelIndex]]] = dict() for r in range(self.timeSeriesDataModel.rowCount()): column0Index: QModelIndex = self.timeSeriesDataModel.index( r, 0, QModelIndex()) column1Index: QModelIndex = self.timeSeriesDataModel.index( r, 1, QModelIndex()) sourceIndex: QModelIndex = self.timeSeriesDataModel.mapToSource( column0Index) proxy: CustomProxyAttributeModel = sourceIndex.model() frameName: str = proxy.sourceModel().frameModel().name attrIndexInFrame: int = proxy.mapToSource(sourceIndex).row() timeLabelIndex: QPersistentModelIndex = column1Index.data( Qt.DisplayRole) if seriesValues.get(frameName, None): seriesValues[frameName].append( (attrIndexInFrame, timeLabelIndex)) else: seriesValues[frameName] = [(attrIndexInFrame, timeLabelIndex)] self.seriesOptions[seriesName] = seriesValues @Slot(str, str) def onSeriesSelectionChanged(self, new: str, old: str) -> None: # Save current set options self.persistOptionsSetForSeries(old) if new: # Get options of new selection newOptions: Dict[str, List[Tuple[int, QPersistentModelIndex]]] = \ self.seriesOptions.get(new, dict()) for frameName, proxyModel in self.models.items(): frameOptions = newOptions.get(frameName, None) self.setOptionsForFrame(frameName, frameOptions) # Update proxy view on the time label columns proxyModel.dataChanged.emit( proxyModel.index(0, 1, QModelIndex()), proxyModel.index(proxyModel.rowCount() - 1, 1, QModelIndex()), [Qt.DisplayRole, Qt.EditRole]) # Every time series change clear frame selection in workbench self.workbenchView.clearSelection() @Slot(str, str) def onFrameSelectionChanged(self, newFrame: str, _: str) -> None: if not newFrame: # Nothing is selected return self.attributesView.setAttributeModel( AttributeTableModel(self)) # Check if frame is already in the source models if newFrame not in self.models.keys(): # Create a new proxy and add it to source models self.addSourceFrameModel(newFrame) if len(self.models) == 1: # If it is the first model added then set up the view self.timeSeriesDataView.setModel(self.timeSeriesDataModel) self.timeSeriesDataView.horizontalHeader( ).setSectionResizeMode(0, QHeaderView.Stretch) self.timeSeriesDataView.horizontalHeader( ).setSectionResizeMode(1, QHeaderView.Stretch) # Update the attribute table self.attributesView.setAttributeModel( self.models[newFrame].sourceModel()) def setOptionsForFrame( self, frameName: str, options: Optional[List[Tuple[int, QPersistentModelIndex]]]) -> None: customProxyModel = self.models[frameName] attributeTableModel = customProxyModel.sourceModel() attributeTableModel.setAllChecked(False) if options: proxySelection: Dict[int, QPersistentModelIndex] = { i: pmi for i, pmi in options } customProxyModel.attributes = proxySelection attributeTableModel.setChecked(list(proxySelection.keys()), value=True) else: customProxyModel.attributes = dict() @Slot() def addSeries(self) -> None: # Append new row self.seriesModel.appendEmptyRow() # In oder to avoid copying previous options for frameName, proxyModel in self.models.items(): self.setOptionsForFrame(frameName, None) @Slot() def removeSeries(self) -> None: selected: List[QModelIndex] = self.seriesView.selectedIndexes() if selected: seriesName: str = selected[0].data(Qt.DisplayRole) # Remove row self.seriesModel.removeRow(selected[0].row()) # Remove options for series if they exists self.seriesOptions.pop(seriesName, None) @Slot() def addTimeLabel(self) -> None: self.timeAxisModel.appendEmptyRow() @Slot() def removeTimeLabel(self) -> None: selected: List[QModelIndex] = self.timeAxisView.selectedIndexes() if selected: self.timeAxisModel.removeRow(selected[0].row()) # Update model self.timeSeriesDataModel.dataChanged.emit( self.timeSeriesDataModel.index(0, 1, QModelIndex()), self.timeSeriesDataModel.index( self.timeSeriesDataModel.rowCount() - 1, 1, QModelIndex()), [Qt.DisplayRole, Qt.EditRole]) @Slot() def editSeriesName(self) -> None: index = self.seriesModel.index(self.seriesModel.rowCount() - 1, 0, QModelIndex()) self.seriesView.setCurrentIndex(index) self.seriesView.edit(index) @Slot() def editTimeLabelName(self) -> None: index = self.timeAxisModel.index(self.timeAxisModel.rowCount() - 1, 0, QModelIndex()) self.timeAxisView.setCurrentIndex(index) self.timeAxisView.edit(index) @Slot(str) def checkOutputName(self, text: str) -> None: if self.workbench and text in self.workbench.names: self.warningLabel.setText( 'Variable {:s} will be overwritten'.format(text)) self.warningLabel.show() else: self.warningLabel.hide()
def _create_table(self, ) -> QTableView: widget = QTableView(self) widget.horizontalHeader().hide() widget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) widget.setEditTriggers(QAbstractItemView.AllEditTriggers, ) return widget
class WTableParameterEdit(QWidget): dataChanged = Signal() def __init__(self, obj): super(WTableParameterEdit, self).__init__() self._obj = obj self._model = WTableParameterModel(self._obj) self._view = QTableView() self._view.setModel(self._model) self.setupUi() self.setNotEditableHidden(True) self.resizeToContent() self._model.dataChanged.connect(self.dataChanged.emit) def resizeToContent(self): """ Resize all the columns (but the last) to fit the content or the minimum size. """ rootIndex = self._view.rootIndex() for colId in range(self._model.columnCount() - 1): self._view.resizeColumnToContents(colId) def setNotEditableHidden(self, hidden): """Set the hide state of all rows that are not editable.""" rootIndex = self._view.rootIndex() for rowId in range(self._model.rowCount()): isEditableRow = False for colId in range(self._model.columnCount()): flag = self._model.flags( self._model.index(rowId, colId, rootIndex)) if flag & Qt.ItemIsEditable: isEditableRow = True self._view.setRowHidden(rowId, not isEditableRow and hidden) def setupUi(self): header = QLabel(f"{type(self._obj).__name__}") font = header.font() font.setBold(True) header.setFont(font) self.mainLayout = QVBoxLayout() self.mainLayout.addWidget(header) if isinstance(self._obj, (list, dict)): label = f"The {type(self._obj).__name__} has {len(self._obj)} element" if len(self._obj) != 1: label += "s" self.mainLayout.addWidget(QLabel(label + ".")) self.mainLayout.addStretch() elif self._obj is None: self.mainLayout.addWidget(QLabel("Not implemented yet.")) self.mainLayout.addStretch() else: self.mainLayout.addWidget(self._view) self.setLayout(self.mainLayout) # === table settings === # self._view.resizeColumnsToContents() self._view.verticalHeader().setVisible(False) self._view.setFocusPolicy(Qt.NoFocus) self._view.setSelectionMode(QAbstractItemView.SingleSelection) self._view.setEditTriggers(QAbstractItemView.AllEditTriggers) self._view.horizontalHeader().setStretchLastSection(True) self._view.horizontalHeader().setSectionsClickable(False) # self._view.setMinimumWidth(650) self.setMinimumWidth(600)
class AddressWidget(QDialog): selectionChanged = Signal(QItemSelection) def __init__(self, parent=None): super(AddressWidget, self).__init__(parent) self.tableModel = TableModel() self.tableView = QTableView() self.setupTable() statusLabel = QLabel("Tabular data view demo") layout = QVBoxLayout() layout.addWidget(self.tableView) layout.addWidget(statusLabel) self.setLayout(layout) self.setWindowTitle("Address Book") self.resize(800, 500) # add test data self.populateTestData() def setupTable(self): proxyModel = QSortFilterProxyModel(self) proxyModel.setSourceModel(self.tableModel) proxyModel.setDynamicSortFilter(True) self.tableView.setModel(proxyModel) self.tableView.setSortingEnabled(True) self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) self.tableView.horizontalHeader().setStretchLastSection(True) self.tableView.verticalHeader().hide() self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tableView.setSelectionMode(QAbstractItemView.SingleSelection) proxyModel.setFilterKeyColumn(0) # Filter on the "name" column proxyModel.sort(0, Qt.AscendingOrder) viewselectionmodel = self.tableView.selectionModel() self.tableView.selectionModel().selectionChanged.connect( self.selectionChanged) def populateTestData(self): addresses = [{ "name": "John Doe", "address": "Alameda" }, { "name": "Alan Turing", "address": "San Deigo" }, { "name": "Bjarne Stroutsup", "address": "Columbia" }, { "name": "Herb Sutter", "address": "Seattle" }, { "name": "Micheal Konin", "address": "Colorado" }] for i in range(len(addresses)): self.tableModel.insertRows(0) ix = self.tableModel.index(0, 0, QModelIndex()) self.tableModel.setData(ix, addresses[i]["name"], Qt.EditRole) ix = self.tableModel.index(0, 1, QModelIndex()) self.tableModel.setData(ix, addresses[i]["address"], Qt.EditRole) self.tableView.resizeRowToContents(ix.row())
class AttributesTab(QWidget): def __init__(self): """ Widget containing the table summary of all attributes """ QWidget.__init__(self) # Data self.attributes = {} # Attributes names to ids -> {attr_name: attr_id, ...} self.attr_idx = {} # Attributes ids to indexes -> {attr_id: attr_idx, ...} self.students = {} # Students names to ids -> {std_name: std_id, ...} self.std_idx = {} # Students ids to indexes -> {std_id: std_idx, ...} self.data = {} # Attributes and students ids to cell data -> {(attr_id, std_id): cell_data, ...} self.dm: AttributesTableModel = None # Widget self.table_attributes = QTableView() self.table_attributes.setEditTriggers(QAbstractItemView.NoEditTriggers) self.table_attributes.setSelectionMode(QAbstractItemView.SingleSelection) self.table_attributes.verticalHeader().setSectionResizeMode(QHeaderView.Fixed) self.table_attributes.verticalHeader().setFixedWidth(150) # Signals self.table_attributes.clicked.connect(self.on_cell_clicked) self.sig_cell_clicked: Signal = None # Layout self.__set_layout() self.set_data([], [], {}) def __set_layout(self) -> None: layout = QHBoxLayout() layout.setSpacing(0) layout.addWidget(self.table_attributes) self.setLayout(layout) def set_data(self, attributes_data: list, students_data: list, data: dict) -> None: """ Sets the data inside the table :param attributes_data: Attributes list (needs to be ordered) = [(attr_id, attr_name), ...] :param students_data: Students list (needs to be ordered) = [(std_id, std_name), ...] :param data: Table's data {(attr_id, std_id}: cell_data, ...} """ self.attributes = {} self.attr_idx = {} i = 0 for attr_id, attr_name in attributes_data: self.attributes[attr_name] = attr_id self.attr_idx[attr_id] = i i += 1 self.students = {} self.std_idx = {} i = 0 for std_id, std_name in students_data: self.students[std_name] = std_id self.std_idx[std_id] = i i += 1 self.data = data attr_header = [a[1] for a in attributes_data] std_header = [s[1] for s in students_data] data_for_model = [] for _ in range(len(std_header)): data_for_model.append([None] * len(attr_header)) for attr_id, std_id in data: data_for_model[self.std_idx[std_id]][self.attr_idx[attr_id]] = data[(attr_id, std_id)] self.dm = AttributesTableModel(self.table_attributes, data_for_model, attr_header, std_header) def on_cell_clicked(self, item) -> None: """ Triggered when a cell is clicked. Emits a signal with the attribute and student ids """ attr_id = None for a in self.attr_idx: if self.attr_idx[a] == item.column(): attr_id = a std_id = None for s in self.std_idx: if self.std_idx[s] == item.row(): std_id = s self.sig_cell_clicked.emit(attr_id, std_id)
class VStdAttributesDialog(QDialog): def __init__(self, parent, sig_attribute_edition: Signal, student: Student, attributes: list): """ Confirm dialog for dangerous actions :param parent: gui's main window :param sig_attribute_edition: Signal to emit in order to edit the selected attribute :param student: Current student :param attributes: List of attributes """ QDialog.__init__(self, parent) self.setFixedSize(QSize(350, 350)) self.setWindowFlag(Qt.WindowStaysOnTopHint) self.student = student self.attributes = attributes # Widgets self.std_info = QLabel(f"{student.firstname} {student.lastname}") self.table_attributes = QTableView() self.table_attributes.setFixedWidth(300) self.table_attributes.horizontalHeader().setStretchLastSection(True) self.table_attributes.horizontalHeader().hide() self.table_attributes.setEditTriggers(QAbstractItemView.NoEditTriggers) self.table_attributes.setSelectionMode( QAbstractItemView.SingleSelection) self.table_attributes.verticalHeader().setSectionResizeMode( QHeaderView.Fixed) self.table_attributes.verticalHeader().setFixedWidth(150) self.dm: AttributesTableModel = None self.attr_idx = { } # Attributes ids to indexes -> {attr_id: attr_idx, ...} # Close button self.ok_btn = QPushButton("Ok") self.ok_btn.clicked.connect(self.accept) self.ok_btn.setFixedSize(QSize(60, 33)) # Signals self.sig_attribute_edition = sig_attribute_edition self.table_attributes.clicked.connect(self.on_attr_clicked) # Layout layout = QVBoxLayout() layout.addWidget(self.std_info) layout.setAlignment(self.std_info, Qt.AlignCenter) layout.addWidget(self.table_attributes) layout.addWidget(self.ok_btn) layout.setAlignment(self.ok_btn, Qt.AlignCenter) self.setLayout(layout) self.setStyleSheet(get_stylesheet("dialog")) # Initialization with the given attributes self.attributes_updated(self.attributes) def attributes_updated(self, attributes: list) -> None: """ Called when an attribute changed and needs to be updated in the tableview :param attributes: List of attributes """ self.attributes = attributes header = [a[1] for a in attributes] # Table Header data_for_model = [a[2] for a in attributes] # Table data # Save indexes self.attr_idx = {} i = 0 for attr_id, _, _ in attributes: self.attr_idx[attr_id] = i i += 1 # Create the table model self.dm = AttributesTableModel(self.table_attributes, data_for_model, header) def on_attr_clicked(self, item) -> None: """ Triggered when a cell is clicked. Emits a signal with the attribute and student ids """ attr_id = None for a in self.attr_idx: if self.attr_idx[a] == item.row(): attr_id = a self.sig_attribute_edition.emit(attr_id, self.student.id)