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

时间:2019-12-30 22:12:05

标签: pyqt pyqt5 freeze qlabel qpixmap

我制作了一个应用程序,该应用程序从网站收集图像的URL,并将其逐个显示在显示器上。但是,当您使用QComboBox滚动浏览图像时,考虑到要滚动的URL超过100个,该程序会冻结2-3秒,这很烦人。如果尝试快速滚动,则应用程序将崩溃。图片甚至还不大(小于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_())

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

1 个答案:

答案 0 :(得分:3)

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

urllib.request.urlopen()

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

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样式的解决方案),因为Qt Network使用事件循环并且不会阻塞它,因为它通过从应用程序中消除复杂性来避免使用线程。

相关问题