如何解决处理 QListWidget 中的数千个项目并减少延迟
我有 QListWidget 和我正在使用 for 循环循环的字典。 for 循环的每次迭代都会向 QListWidget 添加一个项目以及附加到每个项目的几个按钮和标签。一切正常,但我遇到了每次刷新列表时列表需要很长时间(1k 项目大约 20 秒)加载的问题。在此期间,GUI 完全没有响应(只要不花太长时间,我就可以接受)。我发现的一个解决方案是,如果我在执行迭代时隐藏 (self.hide()
) QMainWindow,然后在它完成后显示 (self.show()
)(大约 1.5 秒),刷新时间会显着减少对于 1k 个项目),所以我假设这是资源问题。是否有可能获得大约 1.5 秒的刷新时间,同时仍然保持 GUI 可见(和无响应),就像冻结 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_())
解决方法
最初的问题是,当它显示时,每次您添加一个项目(和小部件)时,它都会重新绘制所有内容,这与隐藏它并在只有一幅画的地方显示它的任务不同。
另一种不会减少加载时间但确实使 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)
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_())
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。