【问题标题】:QTableWidget loads very slow after clearing it with QLineEdits inside使用 QLineEdits 清除 QTableWidget 后加载速度非常慢
【发布时间】:2020-10-04 10:37:10
【问题描述】:

我有一个 QTableWidget,里面有 QLineEdits 作为 CellWidgets。 如果我清理桌子并用相同的功能重新填充它,它需要更长的时间才能完成。 在此示例中,差异在 0.1 和 3.9 秒之间,但在我的实际代码中,差异在 0.1 秒和 10 分钟之间。

下面是示例代码:

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)

输出:

0.14005303382873535

在使用 QCombobox 并将其设置回“重置”之后:

3.9842889308929443

在有人问之前,我用 QLineEdits 填充 QTableWidget,因为我想使用占位符。

【问题讨论】:

  • 只是出于好奇,为什么要在表格中填满 1000 个 QLineEdits 而不仅仅是让表格可编辑?
  • 我在问题的最后写了这个,因为我想使用占位符,而 QTableWidgetItems 没有机会添加它们。
  • @VolLioMenTT 虽然我能理解这种需求,但使用可见的独立和持久的 QLineEdits 并不是一个好的选择,尤其是出于性能(速度和内存)的原因,因为您只需要显示一些东西而不是实际上 always 需要 QLineEdit 的所有功能来处理 all 3000 个单元格。此外,访问模型数据会更加困难,因为您需要始终访问行编辑才能获取其内容。

标签: python pyqt5 qt5 qtablewidget qlineedit


【解决方案1】:

不同之处不仅在于您为每个单元格使用单元格小部件(而且,让我说 3000 个小部件很多),还因为您正在调用 @ 987654322@每次。

您还可以看到问题不是在“清除”之后发生,而是在创建新单元格时发生:只需删除__init__ 中对fillTable 的第一次调用,就会发生相同的延迟。
每次模型布局更改(通过添加/删除行或列)时,不仅模型会发生很多事情,而且显示其内容的视图也会发生很多事情,并且由于您是单独添加行,这导致需要更长的时间处理其内容的视图,即使您无法立即看到它(这是因为重绘是排队,并且只有在事件队列被清除后才会发生)。

为了提高性能,在您的情况下,您应该只调用一次 setRowCount() 并显示最终的行数:

    def fill_row(self, row):
        # comment or remove the following line
        # 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:
            count = int(currentText)
            self.table.setRowCount(count)
            for row in range(0, int(currentText)):
                self.fill_row(row)

    def fillTable(self):
        self.table.setRowCount(1000)
        for row in range(0, 1000):
            self.fill_row(row)

最后,如果您真的要显示这么多行,我强烈建议您寻找替代方案,正如文档中对 setIndexWidget()(由 setCellWidget() 内部调用)所解释的那样:

这个函数应该只用于在与数据项对应的可见区域内显示静态内容。如果您想显示自定义动态内容或实现自定义编辑器小部件,请改为子类 QStyledItemDelegate。

这是因为大量的小部件会导致严重的性能问题(与您的完全一样)。

如果您需要的是占位符,则为每个单元格使用 QLineEdit 是一个糟糕的选择,不仅出于性能原因,而且因为这样您无法直接访问模型数据,但您总是需要在此之前找到单元格小部件。 更优雅和更可取的解决方案是使用自定义委托,当单元格没有数据时,它将显示占位符文本,并将CurrentChanged 添加到表格编辑触发器:

self.table.setEditTriggers(self.table.editTriggers() | self.table.CurrentChanged)

一个简单的委托实现可能是这样的:

class PlaceholderDelegate(QStyledItemDelegate):
    def __init__(self, placeholder='', parent=None):
        super().__init__(parent)
        self.placeholder = placeholder

    def createEditor(self, parent, option, index):
        editor = super().createEditor(parent, option, index)
        if isinstance(editor, QLineEdit):
            editor.setPlaceholderText(self.placeholder)
        return editor

    def paint(self, painter, option, index):
        super().paint(painter, option, index)
        if not index.data():
            try:
                # placeholderText palette role was introduced on Qt 5.12
                color = option.palette.placeholderText().color()
            except:
                color = option.palette.text().color()
                color.setAlpha(128)
            painter.setPen(color)
            style = option.widget.style()
            margin = style.pixelMetric(style.PM_FocusFrameHMargin, option, option.widget)
            rect = option.rect.adjusted(margin, 0, -margin, 0)
            text = option.fontMetrics.elidedText(self.placeholder, option.textElideMode, rect.width())
            painter.drawText(rect, option.displayAlignment, text)


class Test_Layout(QWidget):
    # ...
    def createTable(self):
        # ...
        for i in range(0, 3):
            header.setSectionResizeMode(i, QHeaderView.ResizeToContents)
            self.table.setItemDelegateForColumn(
                i, PlaceholderDelegate('empty {}'.format(i + 1), self.table))

注意:你不应该使用宽度和高度为0的setGeometry(),并且总是提供一个默认位置对于大屏幕或多个屏幕的用户来说可能会很烦人;此外,widthheight 是所有 QWidget 子类的默认属性,不应被自定义属性覆盖。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-06
    • 2018-05-02
    • 1970-01-01
    相关资源
    最近更新 更多