QTableWidget在使用QLineEdits清除后加载速度非常慢

2024-04-19 05:27:51 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个QLineEdits作为CellWidget的QTableWidget。 如果我清除表格并使用相同的功能重新填充,则需要更长的时间才能完成。 在本例中,差异在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

使用QCOMBOX并将其设置回“重置”后:

3.9842889308929443

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


Tags: inselffortimedeftablerangefill
1条回答
网友
1楼 · 发布于 2024-04-19 05:27:51

这种差异不仅是因为您为每个单元格使用了单元格小部件(让我说,3000个小部件是很多),还因为您每次都调用setRowCount()

您还可以看到,问题不是在“清除”之后发生的,而是在创建新单元格时发生的:只需删除__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)

最后,如果您真的要显示这么多行,我强烈建议您找到一个替代方案,正如文档中对^{}(内部称为setCellWidget())所解释的那样:

This function should only be used to display static content within the visible area corresponding to an item of data. If you want to display custom dynamic content or implement a custom editor widget, subclass QStyledItemDelegate instead.

这是因为大量的小部件将导致严重的性能问题(与您的完全相同)

如果您需要的是占位符,那么为每个单元格使用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子类的默认属性,永远不应使用自定义属性覆盖。

相关问题 更多 >