PySide线程和HTTP下载

4
我一直很苦恼这段代码无法正常工作!!!在逐步调试时,它可以正常运行,但正常运行时却会崩溃。最初,我使用 QThread 更新 ImagePreview pixmap,但经过一整天的崩溃和痛苦后,我改变了方向。现在,在上述情况下使用调试器时它可以正常工作,但是其他情况下我感到困惑。请帮我!这段代码有什么问题?还有其他方法可以使用吗?我试图通过从 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个回答

8
我认为问题在于您正在从插槽(信号处理程序)中调用self.startDownload()。因此,您没有将控制权返回给Qt主循环(或类似内容)。正确的方法是通过延迟事件调用它,例如通过QTimer.singleShot调用它:
def ondownloadFinished(self):
    self.paintImage()
    print("download finished?")
    self.db.close()
    QtCore.QTimer.singleShot(0, self.startDownload)

请注意,当使用将 msec 设置为0的 singleShot 时:
QtCore.QTimer.singleShot(0, self.startDownload)

等同于:

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

(source, related question)


只是一个快速的问题,您认为对于这样的操作使用单独的线程会更明智吗?我已经尝试过使用线程来实现它,但没有成功,但如果值得的话,我会利用这些新知识再试一次。 - kellpossible
2
@kellpossible,你也可以使用线程来完成它。这是一个示例:http://pastebin.com/b4MD5jKh。但是我不确定在你的情况下哪种方式更好。 - reclosedev
你太棒了。非常感谢,我一直在苦苦寻找一个适合我的线程示例,但是你已经自己做了一个! - kellpossible

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接