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

2024-09-29 21:32:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我制作了一个应用程序,从一个网站收集图像的URL,并在屏幕上逐个显示。但是,当您使用QComboBox滚动浏览图像时,程序会冻结2-3秒,这很烦人,因为要滚动浏览的URL超过100个。如果你尝试快速滚动,应用程序就会崩溃。图片甚至没有那么大(小于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_())

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


Tags: from图像imageimportselfhttpurlsys
1条回答
网友
1楼 · 发布于 2024-09-29 21:32:27

问题是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网络使用事件循环并且不会阻止它

相关问题 更多 >

    热门问题