QTableWidget 上的 PYQT5 setCellWidget() 会减慢 UI

Posted

技术标签:

【中文标题】QTableWidget 上的 PYQT5 setCellWidget() 会减慢 UI【英文标题】:PYQT5 setCellWidget() on QTableWidget slows down UI 【发布时间】:2020-02-25 14:09:41 【问题描述】:

QTableWidget() 上使用 PyQT5 中的 setCellWidget() 时,我遇到了性能问题。一旦我的for-loop 包含来自 SQL 数据库的大约 100 条记录,延迟就会变得明显。大约 500 条记录时,延迟最多需要 3 秒。

我已禁用 setCellWidget() 部分并测试了 20.000 条记录,几乎没有延迟。因此执行和获取查询不会延迟代码。

self.queueTable 是一个 QTableWidget(),有 8 列,行数与存储在变量 tasks 中的查询返回的行数一样多

这是我使用的代码:

    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])))


         ....

我读到 QPushButton 是“昂贵的”(Why get's Python with PyQt5 slow when creating a large amount of QPushButtons?) ,但是在使用其他小部件(例如组合框)时问题仍然存在(组合框的不实用代码示例:)

    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])))

         ....

只有不使用QPushButton()QComboBox()QTableWidget 进行setCellWidget() 调用,我才能毫无延迟地呈现表格。

在典型的用例中,大约有 500 - 750 个排队的任务。我怎样才能拥有QPushButton() 而没有setCellWidget() 造成的延迟?我已经有一个cellDoubleClicked.connectlistener 和一个自定义上下文菜单在桌子上,所以这些不是一个选项。

我的系统:

Python 3.7 PYQT5 5.14.1 Windows 10 64 位

【问题讨论】:

您可以尝试在 for 循环之前将表的 rowCount 设置为类似于 self.queueTable.rowCount()+len(tasks) 的值,而不是在 for 循环中一次插入一行。 它创造了奇迹;谢谢!但是为什么只有结合setWidgetItem才有这样的效果呢?根据您的建议,我将self.queueTable.setRowCount(len(tasks)) 放在for 循环的前面,并使用迭代器将每个任务放在不同的行中。如果您将其发布为答案,我可以将其标记为这样。 我对 PyQt5 的内部工作原理了解得不够多,恐怕无法解释时差。尽管如此,我还是发布了我的评论作为答案。 【参考方案1】:

您可以尝试在 for 循环之前设置行数,而不是一次添加一行。例如下面的例子

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_()

输出

set row count: 0.0359 seconds
insert rows: 1.0572 seconds

【讨论】:

以上是关于QTableWidget 上的 PYQT5 setCellWidget() 会减慢 UI的主要内容,如果未能解决你的问题,请参考以下文章

Pyqt5 qtablewidget 检测单元格何时更改

如何在 PyQt5 中设置 QTableWidget 的单元格样式?

Qtablewidget 去除黑色空间 PyQt5

如何使用 PYQT5 使 QTableWidget 单元格只读?

在 pyqt5 QTableWidget 中应用样式:已选择

PyQt5-高级控件使用(QTableWidget)