PySide 线程和 http 下载

Posted

技术标签:

【中文标题】PySide 线程和 http 下载【英文标题】:PySide threading and http downloading 【发布时间】:2012-02-08 12:29:01 【问题描述】:

我很难让这段代码正常工作!!!!当我逐步调试它时它运行良好,但是当我正常运行时它只是崩溃。最初我使用 QThread 来更新 ImagePreview 像素图,但在经历了一整天的崩溃和痛苦之后,我改变了方向。现在它可以工作了,在上述情况下使用调试器时,但否则我很难过。请帮我!这段代码有什么问题?我可以使用另一种方法吗?我正在尝试使用从 url 下载的图像不断更新图像预览。

import sys

import io
import urllib2

from PySide import QtCore, QtGui, QtNetwork
import time

class QDownloadBuffer(QtCore.QBuffer):
    downloadFinished = QtCore.Signal()
    def __init__(self):
        super(QDownloadBuffer, self).__init__()
        self.open(QtCore.QBuffer.ReadWrite)
        self.url = QtCore.QUrl("http://www.google.com.au/images/srpr/logo3w.png")
        self.manager = QtNetwork.QNetworkAccessManager()
        self.request = QtNetwork.QNetworkRequest(self.url)
        self.manager.finished.connect(self.onFinished)

    def startDownload(self):
        print("Starting Download --")
        self.reply = self.manager.get(self.request)

        self.reply.error[QtNetwork.QNetworkReply.NetworkError].connect(self.onError)

    def onFinished(self):
        print("Download Finished -- ")
        print(self.write(self.reply.readAll()))
        self.reply.close()
        self.downloadFinished.emit()

    def onError(self):
        print("oh no there is an error -- ")
        print(self.reply.error())

class ImagePreview(QtGui.QWidget):
    def __init__(self, parent=None):
        super(ImagePreview, self).__init__(parent)
        self.setMinimumSize(50, 50)
        self.text = None
        self.pixmap = None
        self.dl_n = 0


    def paintEvent(self, paintEvent):
        painter = QtGui.QPainter(self)

        if(self.pixmap):
            painter.drawPixmap(0, 0, self.pixmap)

        if(self.text):
            painter.setPen(QtCore.Qt.blue)
            painter.setFont(QtGui.QFont("Arial", 30))
            painter.drawText(self.rect(), QtCore.Qt.AlignCenter, self.text)

    def startDownload(self):
        self.setText(str(self.dl_n))
        self.dl_n += 1
        print("Starting Download 0".format(self.dl_n))

        self.db = QDownloadBuffer()
        self.connect(self.db, QtCore.SIGNAL("downloadFinished()"), self, QtCore.SLOT("ondownloadFinished()"))
        self.db.startDownload()

    def ondownloadFinished(self):
        self.paintImage()
        print("download finished?")
        self.db.close()
        self.startDownload()

    def paintImage(self):
        print("Painting")
        pixmap = QtGui.QPixmap()
        pixmap.loadFromData(self.db.data())
        self.setPixmap(pixmap)

    def setPixmap(self, pixmap):
        self.pixmap = pixmap
        self.setMinimumSize(pixmap.width(), pixmap.height())
        self.update()

    def setText(self, text):
        self.text = text
        self.update()


class MainWindow(QtGui.QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.imagepreview = ImagePreview()
        self.button = QtGui.QPushButton("Start")
        self.button.clicked.connect(self.imagepreview.startDownload)
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.button)
        layout.addWidget(self.imagepreview)
        self.setLayout(layout)



if __name__ == "__main__":
    import sys

    try:
        app = QtGui.QApplication(sys.argv)
    except RuntimeError:
        pass

    mainwindow = MainWindow()
    mainwindow.show()

    sys.exit(app.exec_())

【问题讨论】:

【参考方案1】:

我认为问题在于您正在从插槽(信号处理程序)调用self.startDownload()。因此,您不会将控制权返回给 Qt 主循环(或类似的东西)。正确的方法是将其称为延迟事件,例如通过QTimer.singleShot 调用它:

def ondownloadFinished(self):
    self.paintImage()
    print("download finished?")
    self.db.close()
    QtCore.QTimer.singleShot(0, self.startDownload)

请注意,singleShotmsec 设置为 0:

QtCore.QTimer.singleShot(0, self.startDownload)

等同于:

QtCore.QMetaObject.invokeMethod(self, 'startDownload',  QtCore.Qt.QueuedConnection)

(source, related question)

【讨论】:

只是一个简单的问题,您认为对这样的操作使用单独的线程是否更明智?我已经非常努力地尝试使用线程来实现它,但没有运气,但如果值得的话,我会在掌握这些新知识的情况下再试一次。 @kellpossible,你也可以用线程来做。例如pastebin.com/b4MD5jKh。但我不知道在你的情况下什么更好。 你太棒了。非常感谢,我真的在互联网上苦苦挣扎,寻找一个适合我情况的线程的好例子,但是你自己做了一个!

以上是关于PySide 线程和 http 下载的主要内容,如果未能解决你的问题,请参考以下文章

PySide 子线程中的计时器

PySide2:如何使装饰槽在其工作线程上执行?

PySide / PyQtGraph 访问主 Qt 事件线程

PySide2 和 Matplotlib:如何让 MatPlotLib 在单独的进程中运行? ..因为它不能在单独的线程中运行

如何正确退出 Pyside2 多线程 GUI 应用程序?

如何使用线程自动关闭 PyQt/PySide 窗口?