David_Hansson | 2020-06-30 11:46:58 UTC | #1
Hello, how would remove and insertrow look like for Martin Fitzpatricks example above?
First one needs def removeRows()
/def InserRows()
for the QAbstractTableModel
class and also some behaviour inside the QMainwindow
class
# This I have for my QAbstractTabelModel class
def insertRows(self, position, rows, QModelIndex, parent):
self.beginInsertRows(QModelIndex(), position, position+rows-1)
for i in range(rows):
default_row = ['']*len(self._headers)
self_data.insert(position, default_row)
self.endInsertRows()
return true
def removeRows(self, position, rows, QModelIndex, parent):
self.beginRemoveRows(QModelIndex(), position, position+rows-1)
for i in range(rows):
del(self._data[position])
self.endRemoveRows()
return true
Not sure if that is correct, Its what I've pieced together from examples on other sites.
For the QMainwindow
class I'm not relay sure how to proceed, the examples on other sites are not very clear to understand.
Some structured extra explanation on this subject for Martins example of this would be helpful.
The row of data should be removed for both the model and the view.
PyQt6 Crash Course — a new tutorial in your Inbox every day
Beginner-focused crash course explaining the basics with hands-on examples.
mike2750 | 2020-06-20 19:16:07 UTC | #2
Not sure if this is what your looking for but this is what I have working for my sqlite tableview setup.
def initializedModel(self):
self.model.setTable("commands")
# self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
self.model.setEditStrategy(QSqlTableModel.OnManualSubmit)
self.model.select()
self.model.setHeaderData(0, Qt.Horizontal, "ID")
self.model.setHeaderData(1, Qt.Horizontal, "Category")
self.model.setHeaderData(2, Qt.Horizontal, "command_alias")
self.model.setHeaderData(3, Qt.Horizontal, "command")
self.model.setHeaderData(4, Qt.Horizontal, "requires")
self.model.setHeaderData(5, Qt.Horizontal, "description")
self.model.setHeaderData(6, Qt.Horizontal, "controlpanel")
self.model.setHeaderData(7, Qt.Horizontal, "verification")
def onAddRow(self):
self.model.insertRows(self.model.rowCount(), 1)
self.model.submit()
def onDeleteRow(self):
self.model.removeRow(self.tableView.currentIndex().row())
self.model.submit()
self.model.select()
def closeEvent(self, event):
db.close()
To clear all the rows or content you can do something like the below.
self.w_tablewidget.clearContents()
self.w_tablewidget.setRowCount(0)
David_Hansson | 2020-06-28 10:23:26 UTC | #3
This is how I have set up my QTableView(not subclassed) and QAbstractTableModel (subclassed as TableModel):
self.model = TableModel(data)
self.proxyModel = QSortFilterProxyModel()
self.proxyModel.setSourceModel(self.model)
self.table_clients.setSortingEnabled(True)
self.table_clients.setModel(self.proxyModel)
self.selectionModel = self.table_clients.selectionModel()
self.table_clients.setSelectionBehavior(QTableView.SelectRows)
I for the QAbstractTableModel class I know have this:
def removeRows(self, position, rows, parent):
print("passed def removingrows")
#print "\n\t\t ...removeRows() Starting position: '%s'"%position, 'with the total rows to be deleted: ', rows
self.beginRemoveRows(parent or QModelIndex(), position, position + rows - 1)
print("passed beginremovingrows")
for i in range(rows):
del(self._data[position])
print("passed deleted row")
self.endRemoveRows()
print("passed endRemoveRows")
return true
For the Mainwindow class I have this:
def removerow(self):
#self.table_clients.model().layoutAboutToBeChanged.emit()
print("passed def removerow")
proxy_index = self.table_clients.selectedIndexes()[0]
print("passed proxyindex = " + str(proxy_index))
#convert
source_index = self.proxyModel.mapToSource(proxy_index)
print("passed sorceindex = " + str(source_index))
#delete row in source model
self.model.removeRow(source_index.row())
# ---- --- something is wrong after this passage - it crashes here ----
print("passed modelremoveRow source index")
#self.model.layoutChanged.emit()
self.table_clients.clearSelection()
It crahes after self.model.removeRow(source:index.row()) because the promt prints this:
= RESTART: C:\Users\svart\Documents\Python\Pythonprojects\AIXCRM\PRM_AIX_200629.py
passed def removerow
passed proxyindex =
This might be something basic thing that is escaping me? And it doesnt change anything if I remove the # from the lines with layoutAboutToBeChanged/layoutChanged, it still crashes at the same place.
David_Hansson | 2020-06-28 10:25:33 UTC | #4
And thank you Mike2750 for the reply but I couldnt make anthing of that advice, partially I guess becouse my program is setup a bit different.
martin | 2020-06-30 12:10:51 UTC | #5
Hi @David_Hansson welcome to the forum! I've written up a small example with two actions which trigger add/remove of rows on the table model. One important thing is to notify the view that the layout of the data has changed after doing this, otherwise it won't update.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def data(self, index, role):
if role == Qt.DisplayRole:
value = self._data[index.row()][index.column()]
return str(value)
def rowCount(self, index):
return len(self._data)
def columnCount(self, index):
return len(self._data[0])
def insertRows(self, position, rows, QModelIndex, parent):
self.beginInsertRows(QModelIndex, position, position+rows-1)
default_row = ['']*len(self._data[0]) # or _headers if you have that defined.
for i in range(rows):
self._data.insert(position, default_row)
self.endInsertRows()
self.layoutChanged.emit()
return True
def removeRows(self, position, rows, QModelIndex):
self.beginRemoveRows(QModelIndex, position, position+rows-1)
for i in range(rows):
del(self._data[position])
self.endRemoveRows()
self.layoutChanged.emit()
return True
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.insertaction = QtWidgets.QAction("Insert")
self.insertaction.triggered.connect(self.insert_row)
self.deletedaction = QtWidgets.QAction("Delete")
self.deletedaction.triggered.connect(self.delete_row)
toolbar = QtWidgets.QToolBar("Edit")
toolbar.addAction(self.insertaction)
toolbar.addAction(self.deletedaction)
self.addToolBar(toolbar)
self.table = QtWidgets.QTableView()
data = [
[1, 9, 2],
[1, 0, -1],
[3, 5, 2],
[3, 3, 2],
[5, 8, 9],
]
self.model = TableModel(data)
self.table.setModel(self.model)
self.setCentralWidget(self.table)
def insert_row(self):
index = self.table.currentIndex()
print(index)
self.model.insertRows(index.row(), 1, index, None)
def delete_row(self):
index = self.table.currentIndex()
self.model.removeRows(index.row(), 1, index)
app=QtWidgets.QApplication(sys.argv)
window=MainWindow()
window.show()
app.exec_()
David_Hansson | 2020-07-23 12:51:02 UTC | #6
Thank you Martin, this worked for me. Great help.
For others that like me have a proxy model in between the Model and the QTableView I found out that I had to add self.layoutAboutToBeChanged.emit() in the begining of the def insertRows() or def removeRows(). Without that I found out that I could only delete one row or insert one row before the application would be non-responsive, or the delete or insert didnt work at alla after one row insert or delete.
so final code for QSortFilterProxyModel In the sub-classed QAbstractTableModel
def insertRows(self, position, rows, QModelIndex, parent):
self.layoutAboutToBeChanged.emit()
self.beginInsertRows(QModelIndex, position, position+rows-1)
default_row = ['']*len(self._data[0])
for i in range(rows):
self._data.insert(position, default_row)
self.endInsertRows()
self.layoutChanged.emit()
return True
def removeRows(self, position, rows, QModelIndex):
self.layoutAboutToBeChanged.emit()
self.beginRemoveRows(QModelIndex, position, position+rows-1)
for i in range(rows):
del(self._data[position])
self.endRemoveRows()
self.layoutChanged.emit()
return True
In the Mainwindow the delete_row() would be like this
def delete_row(self):
proxy_index = self.table.currentIndex()
index = self.proxyModel.mapToSource(proxy_index)
self.model.removeRows(index.row(), 1, index)
mike2750 | 2020-07-23 03:21:53 UTC | #7
@David_Hansson Nice thanks for the tip. I'm probably going to need that snippet soon. Saved.
Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PyQt5 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!