处理 QListWidget 中的数千个项目并减少延迟
Posted
技术标签:
【中文标题】处理 QListWidget 中的数千个项目并减少延迟【英文标题】:Handling thousands of items in a QListWidget and reducing lag 【发布时间】:2021-07-08 00:45:03 【问题描述】:我有 QListWidget 和我正在使用 for 循环循环的字典。 for 循环的每次迭代都会向 QListWidget 添加一个项目,以及附加到每个项目的几个按钮和标签。一切正常,但是每次刷新列表时,我都会遇到列表需要很长时间(1k 个项目大约 20 秒)才能加载的问题。在此期间,GUI 完全没有响应(只要不花费太长时间,我就可以接受)。我发现的一个解决方案是,如果我在 QMainWindow 进行迭代时隐藏(self.hide()
)QMainWindow,然后在它完成后显示(self.show()
)它(1k 个项目大约需要 1.5 秒),刷新时间会大大减少,所以我假设这是资源的问题。是否有可能通过冻结 GUI 来获得大约 1.5 秒的刷新时间,同时仍然保持 GUI 可见(并且无响应),以便在刷新列表时它不会占用尽可能多的资源。
示例代码:
import sys
import time
from PyQt5.QtWidgets import QApplication, QMainWindow, QListWidget, QListWidgetItem, QPushButton, QVBoxLayout, QWidget
class window(QMainWindow):
def __init__(self):
super(window, self).__init__()
self.show()
self.setFixedSize(800, 500)
self.listwidget()
self.refreshlist()
def listwidget(self):
self.list = QListWidget(self)
self.list.setFixedSize(800, 500)
self.list.show()
def refreshlist(self): # uncomment self.hide() and self.show() to see how much faster it is
start = time.time()
# self.hide()
for i in range(1000):
item = QListWidgetItem(str(i))
self.list.addItem(item)
widget = QWidget(self.list)
layout = QVBoxLayout(widget)
layout.addWidget(QPushButton())
self.list.setItemWidget(item, widget)
# self.show()
print(f"took time.time() - start seconds")
"""
average time with hiding and showing was 0.3 seconds
average time without hiding and showing was 14 seconds
"""
if __name__ == '__main__':
app = QApplication([])
Gui = window()
sys.exit(app.exec_())
【问题讨论】:
【参考方案1】:最初的问题是,当它显示时,每次您添加一个项目(和小部件)时,它都会重新绘制所有内容,这与隐藏它并在只有一幅画的地方显示它的任务不同。
另一种不减少加载时间但使 GUI 可见的替代方法是使用队列和计时器每 T 秒添加项目块。您还可以添加一个 gif,向用户指示正在加载信息。
from collections import deque
from functools import cached_property
import sys
from PyQt5.QtCore import pyqtSignal, QTimer
from PyQt5.QtGui import QMovie
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QLabel,
QListWidget,
QListWidgetItem,
QPushButton,
QStackedWidget,
QVBoxLayout,
QWidget,
)
class ListWidget(QListWidget):
started = pyqtSignal()
finished = pyqtSignal()
CHUNK = 50
INTERVAL = 0
def __init__(self, parent=None):
super().__init__(parent)
self.timer.timeout.connect(self.handle_timeout)
@cached_property
def queue(self):
return deque()
@cached_property
def timer(self):
return QTimer(interval=self.INTERVAL)
def fillData(self, data):
self.started.emit()
self.queue.clear()
self.queue.extend(data)
self.timer.start()
def handle_timeout(self):
for i in range(self.CHUNK):
if self.queue:
value = self.queue.popleft()
self.create_item(str(value))
else:
self.timer.stop()
self.finished.emit()
break
def create_item(self, text):
item = QListWidgetItem(text)
self.addItem(item)
widget = QWidget()
layout = QVBoxLayout(widget)
button = QPushButton(text)
layout.addWidget(button)
layout.setContentsMargins(0, 0, 0, 0)
self.setItemWidget(item, widget)
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setFixedSize(800, 500)
self.setCentralWidget(self.stackedWidget)
self.stackedWidget.addWidget(self.gifLabel)
self.stackedWidget.addWidget(self.listWidget)
self.listWidget.started.connect(self.handle_listwidget_started)
self.listWidget.finished.connect(self.handle_listwidget_finished)
@cached_property
def stackedWidget(self):
return QStackedWidget()
@cached_property
def listWidget(self):
return ListWidget()
@cached_property
def gifLabel(self):
label = QLabel(scaledContents=True)
movie = QMovie("loading.gif")
label.setMovie(movie)
return label
def handle_listwidget_started(self):
self.gifLabel.movie().start()
self.stackedWidget.setCurrentIndex(0)
def handle_listwidget_finished(self):
self.gifLabel.movie().stop()
self.stackedWidget.setCurrentIndex(1)
if __name__ == "__main__":
app = QApplication([])
w = Window()
w.show()
w.listWidget.fillData(range(1000))
sys.exit(app.exec_())
【讨论】:
以上是关于处理 QListWidget 中的数千个项目并减少延迟的主要内容,如果未能解决你的问题,请参考以下文章
在单击+拖动时禁用 QListWidget 中的自动选择项目