gpt4 book ai didi

PyQt 从 URL 加载图像到 QPixmap 会导致卡住和崩溃

转载 作者:行者123 更新时间:2023-12-02 19:44:35 28 4
gpt4 key购买 nike

我制作了一个应用程序,可以从网站收集图像的 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 Network:

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 Network 使用事件循环并且不会阻止它。

关于PyQt 从 URL 加载图像到 QPixmap 会导致卡住和崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59537402/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com