加载本地大图片会有卡顿现象,因为
imageLabel
是同步加载
imageLabel
Qt小组件 - 3 imageLabel
Qt小组件 - 2(布局)瀑布流布局,GridLayout,FlowLayout
LoadImageThread最好使用线程池代替否则创建线程过多阻碍主线程刷新
# coding: utf-8
import sys
from pathlib import Path
from typing import List, Union
from PySide6.QtCore import QRect, QThread, Signal, Qt, QThreadPool, QTimer
from PySide6.QtGui import QImage, QPixmap
from PySide6.QtWidgets import QVBoxLayout, QApplication, QScrollArea, QWidget
from components import ImageLabel, WaterfallLayout
class LoadImageThread(QThread):
loadSignal = Signal(QImage)
def __init__(self, parent=None):
super().__init__(parent)
self.url = None
def run(self):
if not self.url:
return
if isinstance(self.url, (str, Path)):
url = str(self.url)
image = QImage(url)
else:
image = self.url
self.loadSignal.emit(image)
def setUrl(self, url):
self.url = url
self.start()
class LazyLoadThread(QThread):
loadSignal = Signal(object, object)
def __init__(self, parent=None):
super().__init__(parent)
self.isStopped = False
self.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 = rect
self.labels = labels
self.start()
def verify(self):
viewport_rect: QRect = self.rect
for 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 = True
self.terminate()
class LazyLoadImage(ImageLabel):
def _postInit(self):
self.dataSource = None
self.loadThread = LoadImageThread(self)
self.loadThread.loadSignal.connect(self.setImage)
self.finished.connect(self.on_finished)
def setDataSource(self, source: str):
self.dataSource = source
def 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():
return
self.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左右的时间