Qt小组件 - 5 图片懒加载样例
加载本地大图片会有卡顿现象,因为
imageLabel
是同步加载
imageLabel
Qt小组件 - 3 imageLabel
Qt小组件 - 2(布局)瀑布流布局,GridLayout,FlowLayout
LoadImageThread最好使用线程池代替否则创建线程过多阻碍主线程刷新
# coding: utf-8
import sys
from pathlib import Path
from typing import List, Unionfrom PySide6.QtCore import QRect, QThread, Signal, Qt, QThreadPool, QTimer
from PySide6.QtGui import QImage, QPixmap
from PySide6.QtWidgets import QVBoxLayout, QApplication, QScrollArea, QWidgetfrom components import ImageLabel, WaterfallLayoutclass LoadImageThread(QThread):loadSignal = Signal(QImage)def __init__(self, parent=None):super().__init__(parent)self.url = Nonedef run(self):if not self.url:returnif isinstance(self.url, (str, Path)):url = str(self.url)image = QImage(url)else:image = self.urlself.loadSignal.emit(image)def setUrl(self, url):self.url = urlself.start()class LazyLoadThread(QThread):loadSignal = Signal(object, object)def __init__(self, parent=None):super().__init__(parent)self.isStopped = Falseself.rect = QRect()self.labels = [] # type: List[LazyLoadImage]def run(self):try:self.verify()except Exception as e:print(e)def setData(self, rect: QRect, labels: List['LazyLoadImage']):self.rect = rectself.labels = labelsself.start()def verify(self):viewport_rect: QRect = self.rectfor item in self.labels:item_rect = item.geometry()if viewport_rect.intersects(item_rect):self.loadSignal.emit(item, True)else:self.loadSignal.emit(item, False)def stop(self):self.isStopped = Trueself.terminate()class LazyLoadImage(ImageLabel):def _postInit(self):self.dataSource = Noneself.loadThread = LoadImageThread(self)self.loadThread.loadSignal.connect(self.setImage)self.finished.connect(self.on_finished)def setDataSource(self, source: str):self.dataSource = sourcedef setImageUrl(self, url: str):self.loadThread.setUrl(url)def setLoading(self, loading: bool):if loading and self.isNull():if self.dataSource.startswith('http'):self.setUrl(self.dataSource)else:self.setImageUrl(self.dataSource)else:if self.isNull():returnself.setImageUrl(QImage())class LazyLoadScrollArea(QScrollArea):def __init__(self, parent=None):super().__init__(parent)self.labels = []self.timer = QTimer(self) # 避免多次加载self.lazy_load_thread = LazyLoadThread(self)self.lazy_load_thread.loadSignal.connect(self.on_load_signal)self.setWidget(QWidget())self.widget().setLayout(WaterfallLayout())self.setWidgetResizable(True)self.verticalScrollBar().valueChanged.connect(self.start)self.timer.timeout.connect(self.updateLazy)self.timer.setSingleShot(True)self.timer.setInterval(150)def on_load_signal(self, item: LazyLoadImage, loading: bool):try:item.setLoading(loading)item.finished.connect(lambda: item.scaledToWidth(item.width()))except Exception as e:print(e)def start(self):self.timer.stop()self.timer.start()def updateLazy(self):rect = self.viewport().rect()rect.translate(self.horizontalScrollBar().value(), self.verticalScrollBar().value())self.lazy_load_thread.setData(rect, self.labels)self.updateGeometry()def add_image(self, url: str):image_label = LazyLoadImage()image_label.setDataSource(url)image_label.setScaledContents(True)image_label.setMinimumHeight(400)self.labels.append(image_label)layout = self.widget().layout()layout.addWidget(image_label)if layout.sizeHint().height() < self.height():self.start()def resizeEvent(self, event):self.start()super().resizeEvent(event)def closeEvent(self, event):self.lazy_load_thread.stop()self.timer.stop()super().closeEvent(event)if __name__ == "__main__":app = QApplication(sys.argv)scroll_area = LazyLoadScrollArea()scroll_area.setWindowTitle("图片懒加载示例")scroll_area.resize(400, 600)# 添加一些图片URL到滚动区域image_urls = ["http://browser9.qhimg.com/bdm/1024_632_0/t010824ab8b5cdfa138.jpg","http://browser9.qhimg.com/bdm/512_316_0/t010448c46c1ecf7cab.jpg","http://browser9.qhimg.com/bdm/1024_632_0/t013a4ed4683039d101.jpg"]for url in image_urls:scroll_area.add_image(url)for url in Path(r'G:\手机\壁纸\碧蓝航线\5.26立绘\Picture').glob('*.*'):scroll_area.add_image(url.as_posix())scroll_area.show()sys.exit(app.exec())
连续创建了2675个组件左右,初次打开需要10S左右的时间