I have a QTableWidget with QLineEdits as CellWidgets inside of it. If I clear the table and refill it with the same function it needs much longer to finish. In this example the difference is between 0.1 and 3.9 seconds, but in my real code the difference is between 0.1 seconds and 10 minutes.
So here is the example code:
class Test_Layout(QWidget):
def __init__(self, parent=None):
super(Test_Layout, self).__init__(parent=None)
self.left = 0
self.top = 0
self.width = 0
self.height = 0
self.initUI()
self.isMaximized()
def initUI(self):
self.setGeometry(self.left, self.top, self.width, self.height)
self.createTable()
start_time = time.time()
self.fillTable()
print(time.time() - start_time)
self.combo = QComboBox(self)
self.combo.addItem("Reset")
for i in range(0, 5):
self.combo.addItem(str(i))
self.combo.currentTextChanged.connect(self.on_combo_changed)
self.vbox = QVBoxLayout()
self.vbox.addWidget(self.combo)
self.vbox.addWidget(self.table)
self.setLayout(self.vbox)
def fill_row(self, row):
self.table.insertRow(row)
placeholder = QLineEdit()
self.table.setCellWidget(row, 0, placeholder)
placeholder = QLineEdit()
self.table.setCellWidget(row, 1, placeholder)
placeholder = QLineEdit()
self.table.setCellWidget(row, 2, placeholder)
def on_combo_changed(self, currentText):
self.table.setRowCount(0)
if currentText == "Reset":
start_time = time.time()
self.fillTable()
print(time.time() - start_time)
else:
for row in range(0, int(currentText)):
self.fill_row(row)
def createTable(self):
self.table = QTableWidget()
self.table.setColumnCount(3)
self.table.setHorizontalHeaderLabels([
"LineEdit0",
"LineEdit1",
"LineEdit2",
])
header = self.table.horizontalHeader()
for i in range(0, 3):
header.setSectionResizeMode(i, QHeaderView.ResizeToContents)
def fillTable(self):
for row in range(0, 1000):
self.fill_row(row)
Output:
0.14005303382873535
And after using the QCombobox and setting it back to "Reset":
3.9842889308929443
And before somebody asks, I fill the QTableWidget with QLineEdits, because I want to use placeholders.
The difference is not only due to the fact that you're using cell widgets for each cell (and, let me say that 3000 widgets are a lot), but because you're calling
setRowCount()
each time.You can also see that the problem happens not after "clearing", but just when creating the new cells: just remove the first call to
fillTable
in the__init__
and the same delay occurs.Each time the model layout changes (by adding/removing rows or columns), lots of things happen not only for the model, but for the view that shows its contents, and since you are adding rows individually, this results in longer time required for the view to process its contents, even if you cannot see it instantly (and that's because the repainting is queued and only happens as soon as the event queue is cleared).
To improve performance, in your case, you should call
setRowCount()
only once with the final number of rows that are going to be shown:Finally, if you're really going to show that many rows, I strongly suggest to find an alternative, as the documentation explains for
setIndexWidget()
(which is internally called bysetCellWidget()
):This is because large amounts of widgets will cause drastic performances issues (exactly like yours).
If what you need is a placeholder, using a QLineEdit for each cell is a bad choice, not only for performance reasons, but also because in that way you don't have direct access to the model data, but you would always need to find the cell widget before that. A more elegant and preferable solution is to use a custom delegate, which will show the placeholder text when there is no data for the cell, and add
CurrentChanged
to the table edit triggers:A simple delegate implementation could be like this:
Note: you should not use
setGeometry()
with 0 width and height, and always provide a default position can be very annoying for users with large screens or more than one screen; also,width
andheight
are default properties for all QWidget subclasses, and should never be overwritten with custom attributes.