When using the setCellWidget()
in PyQT5 on a QTableWidget()
I run into performance issues. As soon as my for
-loop contains about 100 records coming from an SQL database the delay gets noticeable. At about 500 records the delay takes up to 3 seconds.
I've disabled the setCellWidget()
part and tested for 20.000 records and there was hardly a delay. So performing and fetching the query isn't delaying the code.
the self.queueTable
is a QTableWidget() of 8 columns and as many rows as returned by the query stored in the variable tasks
Here is the code I used:
def buildQueueInUI(self):
global userAccount
.....
tasks = Query(SQLconn, 'SQLITE', False).readParameterized(QueryStrings.myQueuedJobsList, [userAccount])
for row in tasks:
rowPosition = self.queueTable.rowCount()
self.queueTable.insertRow(rowPosition)
btt=QPushButton('DELETE')
btt.clicked.connect(cancelTask)
self.queueTable.setCellWidget(rowPosition, 0, btt) ##turning this into a comment fixes the slowdown issue
self.queueTable.setItem(rowPosition, 1, Tables.noEditTableWidget(self, str(row[0])))
self.queueTable.setItem(rowPosition, 2, Tables.noEditTableWidget(self, str(row[2])))
....
I've read that the QPushButton
is 'expensive'(Why get's Python with PyQt5 slow when creating a large amount of QPushButtons?) , but the problem remains when using other widgets such as a combobox (An unpractical code example with combobox:)
def buildQueueInUI(self):
global userAccount
.....
tasks = Query(SQLconn, 'SQLITE', False).readParameterized(QueryStrings.myQueuedJobsList, [userAccount])
for row in tasks:
rowPosition = self.queueTable.rowCount()
self.queueTable.insertRow(rowPosition)
combo = QComboBox()
combo.addItem("keep")
combo.addItem("remove")
self.queueTable.setCellWidget(rowPosition, 0, combo) ##turning this into a comment fixes the slowdown issue
self.queueTable.setItem(rowPosition, 1, Tables.noEditTableWidget(self, str(row[0])))
self.queueTable.setItem(rowPosition, 2, Tables.noEditTableWidget(self, str(row[2])))
....
Only by not doing the setCellWidget()
call on the QTableWidget
with either a QPushButton()
or a QComboBox()
I can render the table without delays.
In a typical use-case there'd be about 500 - 750 queued tasks. How can I have QPushButton()
without the delays caused by setCellWidget()
? I already have a cellDoubleClicked.connect
listener and a custom context menu on the table`, so those are not an option.
My system:
You could try setting the row count before the for-loop instead of adding one row at the time. Consider for example the following example
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QApplication, QTableWidgetItem
from time import time
class CreateTable(QtWidgets.QWidget):
def __init__(self, parent = None):
super().__init__(parent)
fill_button_1 = QtWidgets.QPushButton('fill table - set row count')
fill_button_1.clicked.connect(self.buildQueueInUI_1)
fill_button_2 = QtWidgets.QPushButton('fill table - insert rows')
fill_button_2.clicked.connect(self.buildQueueInUI_2)
hlayout = QtWidgets.QHBoxLayout()
hlayout.addWidget(fill_button_1)
hlayout.addWidget(fill_button_2)
self.table = QtWidgets.QTableWidget(self)
self.table.setColumnCount(2)
layout = QtWidgets.QVBoxLayout(self)
layout.addLayout(hlayout)
layout.addWidget(self.table)
def buildQueueInUI_1(self):
nrows = 500
self.table.setRowCount(0)
t0 = time()
last_row = self.table.rowCount()
self.table.setRowCount(nrows+self.table.rowCount())
for i in range(500):
row = last_row+i
button = QtWidgets.QPushButton('Click', self)
button.clicked.connect(lambda _, x=row+1: print('button', x))
self.table.setCellWidget(row, 0, button)
self.table.setItem(row, 1, QTableWidgetItem(f'item {row}'))
print(f'set row count: {time()-t0:.4f} seconds')
def buildQueueInUI_2(self):
nrows = 500
self.table.setRowCount(0)
t0 = time()
for i in range(nrows):
row = self.table.rowCount()
self.table.insertRow(row)
button = QtWidgets.QPushButton('Click', self)
button.clicked.connect(lambda _, x=row+1: print('button', x))
self.table.setCellWidget(row, 0, button)
self.table.setItem(row, 1, QTableWidgetItem(f'item {row}'))
print(f'insert rows: {time() - t0:.4f} seconds')
if __name__ == "__main__":
app = QApplication([])
win = CreateTable()
win.show()
app.exec_()
Output
set row count: 0.0359 seconds
insert rows: 1.0572 seconds