def reloadData(self, roles=None): if not roles: roles = [Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.CheckStateRole] self.dataChanged.emit(QModelIndex(), QModelIndex(), roles) self.layoutChanged.emit() self.loading_done.emit()
def parent(self, index): if not index.isValid(): return QModelIndex() child_item = index.internalPointer() if not child_item: return QModelIndex() parent_item = child_item.parent_() if parent_item == self._root_item: return QModelIndex() return self.createIndex(parent_item.row(), 0, parent_item)
def index(self, row, column, parent): if not self.hasIndex(row, column, parent): return QModelIndex() if not parent.isValid(): parent_item = self._root_item else: parent_item = parent.internalPointer() child_item = parent_item.child_at(row) if child_item: return self.createIndex(row, column, child_item) else: return QModelIndex()
def clearRowsFromTable(self): """Delete all rows and data.""" answer = QMessageBox.warning( self, "Clear Table", f"Do you really want to clear the {self._headers[0]} table?", QMessageBox.StandardButton.No | QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No) if answer == QMessageBox.StandardButton.Yes: # Clear the table and update the Money Left Over table self.model.removeRows(self.model.rowCount(QModelIndex()), 0, self.model.rowCount(QModelIndex())) self.totals_updated.emit(self.running_totals) self.addRowToTable()
def index(self, row, column, parent=QModelIndex()): if not self._container: return QModelIndex() if not self.hasIndex(row, column, parent): return QModelIndex() definition = None if not parent.isValid(): definition = self._container.definitions[row] else: definition = parent.internalPointer().children[row] return self.createIndex(row, column, definition)
def setUpMainWindow(self): """Set up the GUI's main window.""" header_label = QLabel("List of Users") # Create model and table objects model = QStandardItemModel() model.setColumnCount(3) model.setHorizontalHeaderLabels(["Name", "Birthdate", "Actions"]) table_view = QTableView() table_view.setEditTriggers( QAbstractItemView.EditTrigger.NoEditTriggers) # NOTE: Uncomment for table cells to be unselectable #table_view.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection) table_view.setModel(model) table_view.horizontalHeader().setSectionResizeMode( 0, QHeaderView.ResizeMode.Stretch) names_list = ["Willman, Joshua", "Davis, Scott", "Garcia, Sky"] # Add items to each row in the table by looping over # the names_list and adding the date edit and button widgets for row, name in enumerate(names_list): model.setItem(row, QStandardItem(name)) # Setting the widget at an index in a QTableView involves # acquiring the QModelIndex values of the current position. # One way to do this is to use the QAbstractItemModel.sibling() # method to retrieve the QModelIndex index from the specified # row and column (here the column is 1) index = table_view.model().sibling(row, 1, QModelIndex()) date_edit = QDateEdit(QDate.currentDate( )) # Create QDateEdit object that starts at current date date_edit.setDateRange(QDate(1900, 1, 1), QDate.currentDate()) date_edit.setDisplayFormat("MM/dd/yyyy") date_edit.setAlignment( Qt.AlignmentFlag.AlignRight) # Align the text date_edit.setAutoFillBackground(True) table_view.setIndexWidget(index, date_edit) # Set the widgets in the final column for each row index = table_view.model().sibling(row, 2, QModelIndex()) table_view.setIndexWidget(index, EditCellWidget(table_view)) # Set up main layout and container object for main window main_v_box = QVBoxLayout() main_v_box.addWidget(header_label) main_v_box.addWidget(table_view) container = QWidget() container.setLayout(main_v_box) self.setCentralWidget(container)
def update_db(self, index: QModelIndex, val: Any) -> None: """Update single value from row in database TODO - this could maybe move to TableWidget? """ df = self.df header = df.columns[index.column()] # view header # model asks parent's table_widget for dbtable to update based on col dbtable = self.table_widget.get_dbtable(header=header) check_exists = False if not header in self.view.mcols[ 'check_exist'] else True row = dbt.Row(data_model=self, dbtable=dbtable, i=index.row()) row.update_single(header=header, val=val, check_exists=check_exists)
def row_changed(self, index_current: QModelIndex, index_prev: QModelIndex): # only redraw the table if the row index changes (faster for horizontal switching) if not index_current.row() == index_prev.row(): # view = self.view # view.setUpdatesEnabled(False) # self.current_row = index_current.row() # for row in (index_current.row(), index_prev.row()): # for col in range(self.columnCount()): # view.childAt(row, col).repaint() # view.setUpdatesEnabled(True) self.layoutAboutToBeChanged.emit() self.current_row = index_current.row() self.layoutChanged.emit()
def setItems(self, items: List[Dict[str, Any]]) -> None: """Replace all items at once. :param items: The new list of items. """ # We do not use model reset because of the following: # - it is very slow # - it can cause crashes on Mac OS X for some reason when endResetModel() is called (CURA-6015) # So in this case, we use insertRows(), removeRows() and dataChanged signals to do # smarter model update. old_row_count = len(self._items) new_row_count = len(items) changed_row_count = min(old_row_count, new_row_count) need_to_add = old_row_count < new_row_count need_to_remove = old_row_count > new_row_count # In the case of insertion and deletion, we need to call beginInsertRows()/beginRemoveRows() and # endInsertRows()/endRemoveRows() before we modify the items. # In the case of modification on the existing items, we only need to modify the items and then emit # dataChanged(). # # Here it is simplified to replace the complete items list instead of adding/removing/modifying them one by one, # and it needs to make sure that the necessary signals (insert/remove/modified) are emitted before and after # the item replacement. if need_to_add: self.beginInsertRows(QModelIndex(), old_row_count, new_row_count - 1) elif need_to_remove: self.beginRemoveRows(QModelIndex(), new_row_count, old_row_count - 1) self._items = items if need_to_add: self.endInsertRows() elif need_to_remove: self.endRemoveRows() # Notify that the existing items have been changed. if changed_row_count >= 0: self.dataChanged.emit(self.index(0, 0), self.index(changed_row_count - 1, 0)) # Notify with the custom signal itemsChanged to keep it backwards compatible in case something relies on it. self.itemsChanged.emit()
def rowCount(self, parent=QModelIndex()): if parent.column() > 0: return 0 if not parent.isValid(): parent_item = self._root_item else: parent_item = parent.internalPointer() return parent_item.child_count()
def removeRows(self, parent, first, last): """Delete all values in the table and model.""" self.beginRemoveRows(QModelIndex(), first, last) # Delete all rows from the table and model self._data.clear() self.values_edited.emit() self.endRemoveRows() self.layoutChanged.emit() return True
def parent(self, child): if not self._container: return QModelIndex() if not child.isValid(): return QModelIndex() parent = child.internalPointer().parent if not parent: return QModelIndex() row = None if not parent.parent: row = self._container.definitions.index(parent) else: row = parent.parent.children.index(parent) return self.createIndex(row, 0, parent)
def updateTotalValues(self): """Update values in the second column and the Money Left Over Table.""" index = self.model.index(len(self.model._data) - 1, 1, QModelIndex()) if "Money In" in self._headers: self.running_totals[0] = index.data() elif "Money Out" in self._headers: self.running_totals[1] = index.data() self.totals_updated.emit(self.running_totals)
def insertRows(self, row, count, parent): """Insert a row into the model. Call values_edited to update the totals in the Money Left Over table.""" self.beginInsertRows(QModelIndex(), row, row) self._data.append(["", "$0.00"]) # Append a list with 2 strings to _data self.values_edited.emit() self.endInsertRows() self.layoutChanged.emit() return True
def removeItem(self, index: int) -> None: """Remove an item from the list. :param index: The index of the item to remove. """ self.beginRemoveRows(QModelIndex(), index, index) del self._items[index] self.endRemoveRows() self.itemsChanged.emit()
def insertItem(self, index: int, item: Dict[str, Any]) -> None: """Insert an item into the list at an index. :param index: The index where to insert. :param item: The item to add. """ self.beginInsertRows(QModelIndex(), index, index) self._items.insert(index, item) self.endInsertRows() self.itemsChanged.emit()
def update_select_all_checkbox(self): check_states = [] for irow in range(self.dbx_model._root_item.child_count_loaded()): index = self.dbx_model.index(irow, 1, QModelIndex()) check_states.append( self.dbx_model.data(index, Qt.ItemDataRole.CheckStateRole) ) if all(cs == 2 for cs in check_states): self.selectAllCheckBox.setChecked(True) else: self.selectAllCheckBox.setChecked(False)
def updateTotalsRow(self, totals_list): """Update the value for the second column.""" if (totals_list[0] and totals_list[1]) != None: income = float(totals_list[0].replace("$", "")) expenses = float(totals_list[1].replace("$", "")) difference = income - expenses # Convert the difference to a string and update the model/view difference_str = f"${difference:.2f}" # Convert to a string index = self.model.index(0, 1, QModelIndex()) self.model.setData(index, difference_str, Qt.ItemDataRole.EditRole)
def count(self, parent=QModelIndex()): if not self._container: return 0 if parent.column() > 0: return 0 if not parent.isValid(): return len(self._container.definitions) setting = parent.internalPointer() return len(setting.children)
def data(self, index: QModelIndex, _: int = ...) -> Any: """ Example: rows = [{name: "Jeff", age: 100, height: 10.2}, ....] headers = { 0: "name", 1: "age", 2: "height" } row = 0 # index of row in rows column = 2 # index of header in headers return rows[0]["height"] """ if index.column() not in self._headers.keys(): # Check if column (int) has corresponding header (str) return "" i_row = index.row() header = self._headers[index.column()] value = self._rows[i_row][header] return value or ""
def displayItemValues(self): """Simple method that demonstrates how to retrieve the values from the widgets.""" # Get the model index of the item (in this case the view_button) # at the viewport coordinates index = self.table_view.indexAt( self.pos()) # Get the index of the button pushed row = index.row() # Get the row of that button # Use the row value to get the index of the cells in that row, column 1 widget_index = self.table_view.model().sibling(row, 1, QModelIndex()) # Use indexWidget to get the QDateEdit widget date_edit_widget = self.table_view.indexWidget(widget_index) # Get value from the cell in column 0 for the selected row name = self.table_view.model().sibling(row, 0, QModelIndex()).data() # Display name and date in a QMessageBox QMessageBox.information( self, "User Information", f"""Name: {name}<br> <b>Birthdate: </b>{date_edit_widget.date().toString("MM/dd/yyyy")}""" )
def _updateVisibleRows(self) -> None: # This function is scheduled on the Qt event loop. By the time this is called, this object can already been # destroyed because the owner QML widget was destroyed or so. We cannot cancel a call that has been scheduled, # so in this case, we should do nothing if this object has already been destroyed. if self._destroyed: return # Reset the scheduled flag self._update_visible_row_scheduled = False currently_visible = set( self._row_index_list) # A set of currently visible items new_visible = set() # A new set of visible items for index in range(len(self._definition_list)): if self._isDefinitionVisible(self._definition_list[index]): new_visible.add(index) # Calculate the difference between the sets. The items that are in new_visible and # not in currently_visible are items that need to be added, the items that are in # currently_visible and not in new_visible are items that should be removed. to_add = new_visible - currently_visible to_remove = currently_visible - new_visible # Add the new items. Currently doing this one by one since that proved fast enough. for index in sorted(list(to_add)): row = self._findRowToInsert(index) self.beginInsertRows(QModelIndex(), row, row) self._row_index_list.insert(row, index) self.endInsertRows() # Remove items. Also doing this one by one currently. for index in sorted(list(to_remove)): row = self._row_index_list.index(index) self.beginRemoveRows(QModelIndex(), row, row) del self._row_index_list[row] self.endRemoveRows() self.visibleCountChanged.emit()
def test_dataUnhappy(): model = createModel("single_setting.def.json") # Out of bounds assert model.data(model.index(250, 0), model.KeyRole) == QVariant() # Invalid index assert model.data(QModelIndex(), model.KeyRole) == QVariant() # Unknown role assert model.data(model.index(0, 0), Qt.ItemDataRole.UserRole + 100) == QVariant() empty_model = SettingDefinitionsModel() assert empty_model.data(model.index(0, 0), model.KeyRole) == QVariant()
def setUpMainWindow(self): """Set up the GUI's main window.""" header_label = QLabel("List of Users") # Create model and table objects model = QStandardItemModel() model.setColumnCount(3) model.setHorizontalHeaderLabels(["Name", "Birthdate", "Actions"]) table_view = QTableView() # NOTE: setEditTriggers() is not used so that the user # can double-click and edit cells table_view.setModel(model) table_view.horizontalHeader().setSectionResizeMode( 0, QHeaderView.ResizeMode.Stretch) # Set the item delegate for a specific column, in this case column 1 table_view.setItemDelegateForColumn(1, DateEditDelegate()) names_list = ["Willman, Joshua", "Davis, Scott", "Garcia, Sky"] # Add items to each row in the table by looping over # the names_list and adding the date edit and button widgets for row, name in enumerate(names_list): model.setItem(row, QStandardItem(name)) # Create an item and set the initial value for the second column. # Here the QDate values are converted to strings to make it easier # to align the text without having to subclass a model class date_item = QStandardItem( QDate.currentDate().toString("MM/dd/yyyy")) date_item.setTextAlignment(Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignRight) model.setItem(row, 1, date_item) # Set the widgets in the final column for each row index = model.index(row, 2, QModelIndex()) table_view.setIndexWidget(index, EditCellWidget(table_view)) # Set up main layout and container object for main window main_v_box = QVBoxLayout() main_v_box.addWidget(header_label) main_v_box.addWidget(table_view) container = QWidget() container.setLayout(main_v_box) self.setCentralWidget(container)
def insertRows(self, m: dict, i: int = None, num_rows=1, select=False): """Insert new row to table Parameters ---------- m : dict Vals for new row to insert i : int, optional Insert at specific location, default end of table num_rows : int, optional number of rows to insert, default 1 select : bool, optional select row after insert, default False """ if i is None: i = self.rowCount() self.beginInsertRows(QModelIndex(), i, i + num_rows - 1) self._df_orig = self._df_orig.pipe(f.append_default_row) df_new_row = self._df_orig.iloc[-1:] # concat this way to preserve index of new row from _df_orig self._df = pd.concat([self.df, self._df_orig.iloc[-1:]]) # setting new data will trigger update of static dfs for col, val in m.items(): icol = self.get_col_idx(col) if not icol is None: index = self.createIndex(i, icol) self.setData(index=index, val=val, update_db=False, triggers=False) self.set_static_dfs(df=self._df_orig.iloc[-1:], reset=False) self.update_rows_label() self.endInsertRows() if select: self.view.select_by_index(index=self.createIndex(i, 1)) return df_new_row
def removeRows(self, i: int, num_rows: int = 1): """Remove single row from table model Parameters ---------- i : int row int to remove num_rows : int, optional not implemented, default 1 - TODO build remove more than one row """ self.beginRemoveRows(QModelIndex(), i, i + num_rows - 1) df = self.df row_name = df.index[i] self._df_orig = self._df_orig.drop(row_name) self._df = df.drop( row_name) # can't call self.df layout signals mess table up self.update_rows_label() self.endRemoveRows()
def sort(self, column, order): self.layoutAboutToBeChanged.emit() self._root_item.sort(column, order) self.dataChanged.emit(QModelIndex(), QModelIndex()) self.layoutChanged.emit()
def on_tableWidget_doubleClicked( self, index: QModelIndex): # получение списка обьектов r = self.ui.tableWidget.item(index.row(), index.column()).text() clipboard.setText(r)
def on_tableWidget_clicked( self, index: QModelIndex): # получение индекса строки при нажатие self.id = int(self.ui.tableWidget.item(index.row(), 0).text())
def on_select_all_clicked(self, checked): checked_state = 2 if checked else 0 for irow in range(self.dbx_model._root_item.child_count_loaded()): index = self.dbx_model.index(irow, 1, QModelIndex()) self.dbx_model.setCheckState(index, checked_state)