PyQt 将图像从 URL 加载到 QPixmap 会导致冻结和崩溃



我做了一个应用程序,从网站收集图像的URL,并将它们逐个显示。但是,当您使用 QComboBox 滚动浏览图像时,程序会冻结 2-3 秒,考虑到有 100 多个 URL 需要滚动,这很烦人。如果您尝试快速滚动,应用程序会崩溃。图片甚至不是那么大(小于300KB(,互联网连接足够好。有什么解决方案可以解决这个问题吗?

下面是一段代码:

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import urllib.request
import sys
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setWindowTitle(" ")
lo2 = QVBoxLayout()


self.pixmap = QPixmap()
self.widgetIMG = QLabel()
self.widgetIMG.setAlignment(Qt.AlignHCenter)
self.widgetList = QComboBox()
self.widgetList.addItems(['first image','second image', 'third image'])
self.widgetList.currentIndexChanged.connect(self.Display)
self.url_list = ['http://cards.hearthcards.net/3062325e.png', 'http://cards.hearthcards.net/4fc517c5.png', 'http://cards.hearthcards.net/6c9f07e2.png']

lo2.addWidget(self.widgetIMG)
lo2.addWidget(self.widgetList)
widget = QWidget()             
widget.setLayout(lo2)  
self.setCentralWidget(widget)
def Display(self, id):
print(id)
URL = self.url_list[id]
img = urllib.request.urlopen(URL).read()
self.pixmap.loadFromData(img)
self.widgetIMG.setPixmap(self.pixmap.scaledToHeight(380))

if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

也许有一种方法可以在启动时预加载所有图像并以某种方式临时缓存这些图像?什么是最佳解决方案?

问题是urllib.request.urlopen()函数阻塞导致窗口冻结,解决方案是在另一个线程中执行该任务并通过信号将信息发送到主线程。

from functools import partial
import sys
import urllib.request

from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt, QThread, QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import (
QApplication,
QComboBox,
QLabel,
QMainWindow,
QVBoxLayout,
QWidget,
)

class Downloader(QObject):
resultsChanged = pyqtSignal(bytes)
@pyqtSlot(str)
def download(self, url):
img = urllib.request.urlopen(url).read()
self.resultsChanged.emit(img)

class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowTitle("")
self.thread = QThread(self)
self.thread.start()
self.downloader = Downloader()
self.downloader.moveToThread(self.thread)
self.downloader.resultsChanged.connect(self.on_resultsChanged)
self.widgetIMG = QLabel(alignment=Qt.AlignHCenter)
self.widgetList = QComboBox()
self.widgetList.currentIndexChanged.connect(self.display)
for text, url in zip(
("first image", "second image", "third image"),
(
"http://cards.hearthcards.net/3062325e.png",
"http://cards.hearthcards.net/4fc517c5.png",
"http://cards.hearthcards.net/6c9f07e2.png",
),
):
self.widgetList.addItem(text, url)
widget = QWidget()
lo2 = QVBoxLayout(widget)
lo2.addWidget(self.widgetIMG)
lo2.addWidget(self.widgetList)
self.setCentralWidget(widget)
@pyqtSlot(int)
def display(self, ix):
url = self.widgetList.itemData(ix)
wrapper = partial(self.downloader.download, url)
QTimer.singleShot(0, wrapper)
@pyqtSlot(bytes)
def on_resultsChanged(self, img):
pixmap = QPixmap()
pixmap.loadFromData(img)
self.widgetIMG.setPixmap(pixmap.scaledToHeight(380))
def closeEvent(self, event):
self.thread.quit()
self.thread.wait()
super().closeEvent(event)

if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

另一种可能的解决方案是使用Qt网络:

import sys

from PyQt5.QtCore import pyqtSlot, Qt, QUrl
from PyQt5.QtGui import QPixmap
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest
from PyQt5.QtWidgets import (
QApplication,
QComboBox,
QLabel,
QMainWindow,
QVBoxLayout,
QWidget,
)

class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowTitle("")
self.manager = QNetworkAccessManager()
self.manager.finished.connect(self.on_finished)
self.widgetIMG = QLabel(alignment=Qt.AlignHCenter)
self.widgetList = QComboBox()
self.widgetList.currentIndexChanged.connect(self.display)
for text, url in zip(
("first image", "second image", "third image"),
(
"http://cards.hearthcards.net/3062325e.png",
"http://cards.hearthcards.net/4fc517c5.png",
"http://cards.hearthcards.net/6c9f07e2.png",
),
):
self.widgetList.addItem(text, url)
widget = QWidget()
lo2 = QVBoxLayout(widget)
lo2.addWidget(self.widgetIMG)
lo2.addWidget(self.widgetList)
self.setCentralWidget(widget)
@pyqtSlot(int)
def display(self, ix):
url = self.widgetList.itemData(ix)
self.start_request(url)
def start_request(self, url):
request = QNetworkRequest(QUrl(url))
self.manager.get(request)
@pyqtSlot(QNetworkReply)
def on_finished(self, reply):
target = reply.attribute(QNetworkRequest.RedirectionTargetAttribute)
if reply.error():
print("error: {}".format(reply.errorString()))
return
elif target:
newUrl = reply.url().resolved(target)
self.start_request(newUrl)
return
pixmap = QPixmap()
pixmap.loadFromData(reply.readAll())
self.widgetIMG.setPixmap(pixmap.scaledToHeight(380))

if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

一般来说,我更喜欢第二种解决方案(Qt风格的解决方案(,因为它通过消除应用程序的复杂性来避免使用线程,因为Qt网络使用事件循环并且不会阻塞它。

相关内容

  • 没有找到相关文章

最新更新