zhiyiYo / PyQt-Fluent-Widgets

A fluent design widgets library based on C++ Qt/PyQt/PySide. Make Qt Great Again.
https://qfluentwidgets.com
GNU General Public License v3.0
5.61k stars 541 forks source link

[Bug]: QCompleter.setMaxVisibleItems对Lineedit不起作用 #782

Closed abcdesteve closed 7 months ago

abcdesteve commented 7 months ago

What happened?

在使用qcompleter时,由于项数过多会造成严重的卡顿,因此尝试使用setmaxvisibleitems来限制提示项数 但此选项似乎对 lineedit 不起作用,设置为20以后仍然会显示远超设置数的项 经过测试,卡顿是由界面渲染引起,同样匹配100万项,当成功匹配的项数较少时显示速度非常快,而以空字符匹配时则会造成严重卡顿

Operation System

Windows11 23h2

Python Version

3.10.13 x64

PyQt/PySide Version

pyside6 6.4.2

PyQt/PySide-Fluent-Widgets Version

v1.5.1

How to Reproduce?

image 如图,在设置最大项数为20之后,实际匹配数量远超20

Minimum code

completer=QCompleter(self.cache.get_mdx_keys())
completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
completer.setMaxVisibleItems(20)
LineEdit().setCompleter(completer)
AlexZhu2001 commented 7 months ago

按照Qt官方文档的描述,maxVisibleItems仅仅是限制屏幕上可以显示的items的数量,而非加载到内存中的,原文如下

This property holds the maximum allowed size on screen of the completer, measured in items By default, this property has a value of 7.

所以,这个选项仅仅影响了你在滚动时会不会卡顿,我测试的100000个对象情况下,滚动并没有明显的卡顿

但是在首次弹出菜单时确实消耗了很长的时间。使用vizTracer分析,主要的时间消耗在了_showCompleterMenu这个函数上,主要是创建CompleterMenu和填充数据耗时过大,原因可能是因为Python的循环太慢了。 图片 解决方法: 在优化加载方法之前,你可以尝试自己继承QCompleter,重写completionModel()方法,在返回时通过限制返回的数量来减少卡顿,不过你需要自己设计一套舍弃不需要的item的算法了。 补充一下我的最小测试用例

from qfluentwidgets import LineEdit
from PyQt6.QtWidgets import QHBoxLayout, QWidget, QVBoxLayout, QApplication, QCompleter, QLineEdit
from PyQt6.QtCore import Qt, QEvent, QObject

class Demo(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.cache = (f"Test case {i}" for i in range(100000))
        self.view = QVBoxLayout(self)
        self.completer = QCompleter(self.cache)
        self.completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
        self.completer.setMaxVisibleItems(7)
        self.lineEdit = LineEdit()
        self.lineEdit.setCompleter(self.completer)
        self.view.addWidget(self.lineEdit, 0, Qt.AlignmentFlag.AlignCenter)

if __name__ == "__main__":
    app = QApplication([])
    window = Demo()
    window.show()
    app.exec()