具有大项目数的 QComboBox 的初始显示性能缓慢

Posted

技术标签:

【中文标题】具有大项目数的 QComboBox 的初始显示性能缓慢【英文标题】:Slow initial show performance of QComboBox with large item count 【发布时间】:2020-04-28 05:48:40 【问题描述】:

看起来在包含具有大量项目数的 QComboBox 的小部件/窗口上调用 show 非常慢。

拿下面的 sn-p 比较一下使用 QComboBox 和 QTreeWidget 的性能

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from time import perf_counter

import sys

class MainWindowComboBox(QMainWindow):

    def __init__(self, items, *args, **kwargs):
        super().__init__(*args, **kwargs)

        widget = QComboBox()
        widget.addItems(items)
        self.setCentralWidget(widget)

class MainWindowTreeWidget(QMainWindow):

    def __init__(self, items, *args, **kwargs):
        super().__init__(*args, **kwargs)

        widget = QTreeWidget()
        items = [QTreeWidgetItem([item]) for item in items]
        widget.addTopLevelItems(items)
        self.setCentralWidget(widget)

items = [f'item i' for i in range(100_000)]
app = QApplication(sys.argv)

window = MainWindowTreeWidget(items)

s = perf_counter()
window.show()
print('took', perf_counter()-s)
app.exec_()

我得到以下时间:

    使用 QComboBOx -> 8.09s 使用 QTreeWidget -> 0.06s

使用 QComboBox 的速度要慢几个数量级。

【问题讨论】:

【参考方案1】:

QTreeWidget 与其他 QAbstractItemView 后代的项目视图一样,不知道它们的内容,因此它们通常默认使用最小尺寸提示来指示小部件大小,并且最终可以调整自己的大小(通常通过扩展 if有可用空间)。

另一方面,QComboBox 具有sizeAdjustPolicy 属性,该属性根据其内部模型的内容提供不同的大小提示。

该属性的默认值为AdjustToContentsOnFirstShow,这会导致小部件在模型的整个内容中导航以找到最大的项目大小,并在组合显示第一次。 为了获得每个项目的大小,Qt 使用为每个项目初始化的 QItemDelegate,计算文本大小,添加一个图标(如果存在)以及图标和文本之间所需的间距,并将其调整为代理的边距。可以想象,为大量项目执行该过程需要大量时间。

作为AdjustToMinimumContentsLengthWithIcon value 报告的文档:

组合框将调整为 minimumContentsLength 加上空格 图标。 出于性能原因,请在大型模型上使用此策略。

一旦您将策略属性设置为该值,上述大小计算将不会发生,小部件将立即显示。

当然,这样做的缺点是,如果组合位于非常小的窗口或包含需要更多空间的其他小部件的布局中,它将非常小,甚至可能根本不显示任何文本内容.

为了防止这种情况,您可以根据项目文本为组合设置任意最小宽度;它是完美的(因为 Qt 在文本周围添加了一些边距,您还应该考虑向下箭头),但它会快得多。 请注意,根据字体,这可能会给您带来意想不到的结果,因为我使用max 来对抗字符串长度,而不是实际的字体宽度:带有 8 "i" 的字符串将被认为大于带有 7 " 的字符串w",但当您不使用等宽字体时,后者可能会更大。

    combo = QComboBox()
    combo.setSizeAdjustPolicy(combo.AdjustToMinimumContentsLengthWithIcon)
    combo.addItems(items)
    # use font metrics to obtain the pixel width of the (possibly) longest
    # text in the list;
    textWidth = self.fontMetrics().width(max(items))
    # add some arbitrary margin
    combo.setMinimumWidth(textWidth + 20)

【讨论】:

以上是关于具有大项目数的 QComboBox 的初始显示性能缓慢的主要内容,如果未能解决你的问题,请参考以下文章

如何从 QComboBox 中获取所选项目以显示在 PyQt5 的 QTableWidget 中? (QComboBox 有复选框来选择项目)

在启动时发送 QComboBox 中项目的当前显示索引

QT QComboBox 怎么在点击过后把它设置为初始状态??

为啥QT中的QComboBox 没有显示下拉箭头,只显示一条竖线?

QT中QComboBox如何不显示那个黑色的小箭头?

将项目添加到 QComboBox