【问题标题】:PyQT QTableWidget extremely slowPyQT QTableWidget 极慢
【发布时间】:2016-12-29 03:24:13
【问题描述】:

这是我用来填充在 QT Designer 中绘制的表格的代码。 设计为适用于任何桌子,它工作正常,但是...... 当我尝试显示包含 18 列和 ~12000 行的数据时,它只会冻结 30 秒或更长时间。 那么,我做错了什么,有没有办法加快速度,让代码仍然适用于任何表格?

这是我的代码:

...blablabla...

self.connect(self, SIGNAL("set"), self.real_set)

...blablabla...

def set_table(self, table, data):
    self.emit(SIGNAL('set'), table, data)

def real_set(self, table, data):
    """
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    Assuming data is list of dict and table is a QTableWidget.

    Get first key and get len of contents
    """
    for key in data:
        rows = len(data[key])
        table.setRowCount(rows)
        break

    """
    Forbid resizing(speeds up)
    """
    table.horizontalHeader().setResizeMode(QHeaderView.Fixed)
    table.verticalHeader().setResizeMode(QHeaderView.Fixed)
    table.horizontalHeader().setStretchLastSection(False)
    table.verticalHeader().setStretchLastSection(False)

    """
    Set number of columns too
    """
    table.setColumnCount(len(data))
    table.setHorizontalHeaderLabels(sorted(data.keys()))

    """
    Now fill data
    """
    for n, key in enumerate(sorted(data.keys())):
        for m, item in enumerate(data[key]):
            newitem = QTableWidgetItem(item)
            table.setItem(m, n, newitem)

【问题讨论】:

  • 你的意思是在标题中说QTableWidget?因为您似乎在代码中使用了QTableWidgetItem...另外,更完整的代码会很好,因为现在不知道table 或item 的类型是什么。
  • 您不能一次添加所有 12000 个项目,因为这会阻塞事件循环太长时间。在那个答案中给出的分页解决方案的技术是可行的,但不是通过用户操作进行分页,您可以只使用一个间隔为 0 的计时器,并添加一些合适数量的项目(通用解决方案是在运行时计时,并在您花费 100 毫秒添加项目时停止为该超时添加项目。然后在下次触发计时器超时时继续)。这样 Qt 事件循环可以继续运行,并且 GUI 可以工作。
  • @pmus。一张 18x12000 的桌子非常小。填充它不应该花费近 30 秒的时间。在我自己的测试中,一张 20x15000 的表格只需要 3-4 秒就可以填满。您需要提供一个显示性能问题的完整示例,以便其他人可以尝试重现它。
  • @hyde。我的测试使用了一个随机数据集,在填充表格后结果包括sortByColumn。必须有一些其他代码,OP 没有显示是导致速度变慢的原因。
  • @pmus。如果您将QTableView 与直接从数据结构中提取值的自定义模型一起使用,您可以获得更好的性能。主要瓶颈是创建QTableWidgetItem 的所有这些实例。在我的测试中,使用自定义模型时,一张 20x15000 的表格只需要几分之一秒的时间就可以填满。

标签: python qt pyqt qtablewidget


【解决方案1】:

这是一个测试脚本,它比较了填充表格的几种方法。

自定义模型要快得多,因为它不必预先创建所有项目 - 但请注意,它是一个非常基本的实现,因此不实现排序、编辑等。(有关更多信息,请参阅Model/View Programming详情)。

from random import shuffle
from PyQt4 import QtCore, QtGui

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, data, parent=None):
        super(TableModel, self).__init__(parent)
        self._data = data

    def rowCount(self, parent=None):
        return len(self._data)

    def columnCount(self, parent=None):
        return len(self._data[0]) if self.rowCount() else 0

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role == QtCore.Qt.DisplayRole:
            row = index.row()
            if 0 <= row < self.rowCount():
                column = index.column()
                if 0 <= column < self.columnCount():
                    return self._data[row][column]

class Window(QtGui.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.table = QtGui.QTableView(self)
        self.tablewidget = QtGui.QTableWidget(self)
        self.tablewidget.setSortingEnabled(True)
        self.button1 = QtGui.QPushButton('Custom Model', self)
        self.button1.clicked.connect(
            lambda: self.populateTable('custom'))
        self.button2 = QtGui.QPushButton('StandardItem Model', self)
        self.button2.clicked.connect(
            lambda: self.populateTable('standard'))
        self.button3 = QtGui.QPushButton('TableWidget', self)
        self.button3.clicked.connect(
            lambda: self.populateTable('widget'))
        self.spinbox = QtGui.QSpinBox(self)
        self.spinbox.setRange(15000, 1000000)
        self.spinbox.setSingleStep(10000)
        layout = QtGui.QGridLayout(self)
        layout.addWidget(self.table, 0, 0, 1, 4)
        layout.addWidget(self.tablewidget, 1, 0, 1, 4)
        layout.addWidget(self.button1, 2, 0)
        layout.addWidget(self.button2, 2, 1)
        layout.addWidget(self.button3, 2, 2)
        layout.addWidget(self.spinbox, 2, 3)
        self._data = []

    def populateTable(self, mode):
        if mode == 'widget':
            self.tablewidget.clear()
            self.tablewidget.setRowCount(self.spinbox.value())
            self.tablewidget.setColumnCount(20)
        else:
            model = self.table.model()
            if model is not None:
                self.table.setModel(None)
                model.deleteLater()
        if len(self._data) != self.spinbox.value():
            del self._data[:]
            rows = list(range(self.spinbox.value()))
            shuffle(rows)
            for row in rows:
                items = []
                for column in range(20):
                    items.append('(%d, %d)' % (row, column))
                self._data.append(items)
        timer = QtCore.QElapsedTimer()
        timer.start()
        if mode == 'widget':
            self.tablewidget.setSortingEnabled(False)
            for row, items in enumerate(self._data):
                for column, text in enumerate(items):
                    item = QtGui.QTableWidgetItem(text)
                    self.tablewidget.setItem(row, column, item)
            self.tablewidget.sortByColumn(0, QtCore.Qt.AscendingOrder)
        else:
            self.table.setSortingEnabled(False)
            if mode == 'custom':
                model = TableModel(self._data, self.table)
            elif mode == 'standard':
                model = QtGui.QStandardItemModel(self.table)
                for row in self._data:
                    items = []
                    for column in row:
                        items.append(QtGui.QStandardItem(column))
                    model.appendRow(items)
            self.table.setModel(model)
            self.table.setSortingEnabled(True)
            self.table.sortByColumn(0, QtCore.Qt.AscendingOrder)
        print('%s: %.3g seconds' % (mode, timer.elapsed() / 1000))

if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 50, 1200, 800)
    window.show()
    sys.exit(app.exec_())

【讨论】:

  • 我必须承认你是一个强大的程序员。我需要一些时间来审查和分析您的脚本!
【解决方案2】:

在 GUI 应用程序中,人们会遇到一种需要 以表格或列表格式显示大量项目(例如 在表中显示大量行)。一种方法来增加 GUI响应性是在屏幕显示时加载一些项目 并根据用户操作延迟加载其余项目。 Qt 提供了一种解决方案来解决将数据加载到 需求。

您可以在link 中找到这种称为分页的技术的实现

【讨论】:

  • 始终欢迎提供指向潜在解决方案的链接,但请add context around the link,以便您的其他用户知道它是什么以及为什么存在。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。考虑到仅仅是指向外部站点的链接是Why and how are some answers deleted? 的一个可能原因。
  • 这是一个好点,但不适合我的情况,因为需要立即显示表格。
  • @pmus 如果你要一次显示所有数据,你需要一个强大的大显示器;)
  • @hyde 不,就在我看到表格时,我需要按值对其进行排序。因此,逐页加载不是一种选择。
  • @pmus 可以对数据预分页进行排序。
猜你喜欢
  • 2018-08-02
  • 2019-05-14
  • 2015-05-29
  • 1970-01-01
  • 1970-01-01
  • 2022-11-24
  • 2013-03-03
  • 2016-03-19
  • 2012-05-03
相关资源
最近更新 更多