allenwjohnson11084 | 2021-05-14 14:39:58 UTC | #1
I am new to PyQt and having to really dig to understand how the objects fit together. I am probably overlooking some basic things at this point. I have started building my application, but keep tripping into things that don't work the way I want. Please be understanding if I am missing basic concepts. Doing this as retirement project, no longer being paid to code. The problem is that I want to add a row to a tableView and have it selected. This code is extracted from a more complex environment. What am I doing wrong?
import sys from PyQt5 import QtCore as qtc from PyQt5 import QtWidgets as qtw """ Model: based on QAbstractTableModel View: QTable view data: simple table of integers (3 columns) Implemented insert and remove row methods. I want an inserted table row to be selected Problem: selectRow after insert of table row works only if I have just deleted the next to last entry in the table """ # Model +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class TableModel(qtc.QAbstractTableModel): def __init__(self, data): super(TableModel, self).__init__() self._data = data def data(self, index, role): if role == qtc.Qt.DisplayRole: value = self._data[index.row()][index.column()] return str(value) def setData(self, index, value, role): if role == qtc.Qt.EditRole: self._data[index.row()][index.column()] = value return True def rowCount(self, index): rows = len(self._data) return rows def columnCount(self, index): cols = len(self._data) return cols def flags(self, index): flags = qtc.Qt.ItemIsSelectable|qtc.Qt.ItemIsEnabled|qtc.Qt.ItemIsEditable return flags def removeRow(self, row): self.rowsAboutToBeRemoved.emit(qtc.QModelIndex(), row, row) self._data.pop(row) self.rowsRemoved.emit(qtc.QModelIndex(), row, row) def insertRow(self, data)->int: row = len(self._data) + 1 self.rowsAboutToBeInserted.emit(qtc.QModelIndex(), row, row) self._data.append(data) self.rowsInserted.emit(qtc.QModelIndex(), row, row) return row # end class TableModel class MainWindow(qtw.QMainWindow): def __init__(self): super().__init__() self.setMinimumSize(qtc.QSize(350, 200)) self.cIndex = None # current selection self.pIndex = None # previous selection self.rowData = 10 # for generated adds self.sm = None # selection model # view +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ self.table = qtw.QTableView() self.table.setSelectionMode(qtw.QAbstractItemView.SingleSelection) self.table.setSelectionBehavior(qtw.QAbstractItemView.SelectRows) # model +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ data = [ [1, 9, 2] ] # start with 1 row self.model = TableModel(data) self.table.setModel(self.model) sm = self.table.selectionModel() sm.currentRowChanged.connect(self.currentRowChanged) self.sm = sm # buttons ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ buttons = qtw.QHBoxLayout() self.pbAdd = qtw.QPushButton('Add') self.pbDelete = qtw.QPushButton('Delete') self.pbAdd.clicked.connect(self.pbAddClicked) self.pbDelete.clicked.connect(self.pbDeleteClicked) # layout ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vBox = qtw.QVBoxLayout() vBox.addWidget(self.table) buttons.addWidget(self.pbAdd) buttons.addWidget(self.pbDelete) vBox.addLayout(buttons) # complete main window self.widget = qtw.QWidget() self.widget.setLayout(vBox) self.setCentralWidget(self.widget) self.show() def currentRowChanged(self, cIndex, pIndex): self.cIndex = cIndex self.pIndex = pIndex # add row def pbAddClicked(self): col = self.rowData data = [col, col+1, col+2] self.rowData += 10 row = self.model.insertRow(data) # select the inserted row # *** this only works when the next to last entry has been deleted ??? self.table.selectRow(row) # remove row def pbDeleteClicked(self): row = self.cIndex.row() self.model.removeRow(row) # end class MainWindow app=qtw.QApplication() window=MainWindow() app.exec_()
allenwjohnson11084 | 2021-05-19 07:06:37 UTC | #2
I found my own very basic problem. It wasn't related to the interaction between the view, selection model and model. Which thanks to my problem and hours spent looking at the documentation and drawing diagrams I now understand much better. I was using an invalid index for my selectRow call. I would expect an error, but apparently the call is just ignored. No additional responses are needed.
martin | 2021-05-19 11:55:24 UTC | #3
Thanks for the update @allenwjohnson11084 glad you got it sorted!
When you say invalid index, was the index out of bounds or was
False? There are quite a few methods that will return an "invalid index" if something isn't there, but it's not considered an error -- e.g. the parent of a top-level item in a tree will return an invalid parent index. In a lot of cases you can consider it the model equivalent of Python's
None -- a valid empty value.
Not sure if it's relevant to the issue but when overriding methods on your subclass you should follow the same signature as the superclass, e.g.
insertRow should accept both the data and the location to insert it, and return a
bool for success/failure, rather than the index.
insertRow(int row, const QModelIndex &parent = QModelIndex())
Since selections are handled by the view, and inserts on the model, to do what you're trying to achieve (auto select inserted rows) I'd probably use the model
.rowsInserted and hook that into the selection model for the table view.
P.S. Apologies for the delay in replying I'm out of office (house being renovated) so checking in less frequently for the next 2 weeks.