Python和PySide中带超时的线程化上载

Python和PySide中带超时的线程化上载,python,multithreading,pyqt,multiprocessing,pyside,Python,Multithreading,Pyqt,Multiprocessing,Pyside,我正在寻找一种基于threading.Thread、multiprocessing或Queue的设计模式,用于上传带有超时的项目列表。线程允许GUI保持响应。如果连接挂起,则应触发超时,程序应正常退出 下面的示例可以工作,但GUI仍然被阻止。如何改进这一点以允许上载列表、手动取消进程、上载进程超时以及非阻塞GUI 从PySide.QtGui导入* 从PySide.QtCore导入* 导入系统 导入时间 导入线程 类上载窗口(QDialog): def uuu init uuu(self,par

我正在寻找一种基于threading.Thread、multiprocessing或Queue的设计模式,用于上传带有超时的项目列表。线程允许GUI保持响应。如果连接挂起,则应触发超时,程序应正常退出

下面的示例可以工作,但GUI仍然被阻止。如何改进这一点以允许上载列表、手动取消进程、上载进程超时以及非阻塞GUI

从PySide.QtGui导入*
从PySide.QtCore导入*
导入系统
导入时间
导入线程
类上载窗口(QDialog):
def uuu init uuu(self,parent=None):
超级(上传窗口,自我)。\uuuu初始化\uuuuu(父级)
self.uploadBtn=QPushButton('Upload'))
mainLayout=QVBoxLayout()
mainLayout.addWidget(self.uploadBtn)
self.uploadBtn.clicked.connect(self.do\u上传)
self.progressDialog=QProgressDialog(self)
self.progressDialog.cancelled.connect(self.cancelDownload)
self.progressDialog.hide()
self.setLayout(主布局)
self.show()
自我提升()
def do_上传(自):
self.uploadBtn.setEnabled(False)
self.progressDialog.setMaximum(10)
self.progressDialog.show()
self.upload\u thread=上传线程(self)
self.upload_thread.start()
self.upload\u thread\u stopped=False
#要上载的项目列表
对于范围(10)内的i:
self.upload\u thread=上传线程(i)
self.upload_thread.start()
self.upload_线程连接(5)
self.progressDialog.setValue(i)
如果self.upload\u线程\u停止:
打破
self.progressDialog.hide()
self.uploadBtn.setEnabled(True)
def取消下载(自):
self.upload\u thread\u stopped=True
类上载线程(threading.Thread):
定义初始化(self,i):
super(上传线程,self)。\uuuu init\uuuu()
self.i=i
self.setDaemon(True)
def运行(自):
时间。睡眠(0.25)#模拟上传时间
打印自我
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
app=QApplication(sys.argv)
w=上传窗口()

sys.exit(app.exec_())
GUI没有响应,因为您在
do_upload
中完成了所有工作,从未返回到主循环

此外,您还可以调用
Thread.join()
,它将阻止所有操作,直到线程完成(请参阅)

您应该使用
PySide.QtCore.QThread
来利用信号和插槽。 这是一本书。我用PyQt在Python3.4中实现了它,但是您也应该能够在PySide中使用它


您可能还需要查看
PySide.QtCore.QProcess
,以避免使用线程。

在这里,我将一些代码放在一起,以满足您的需要

对于一个实际的项目,请确保跟踪上传的内容更好&/或者使用比.terminate()更安全的方法按需停止线程

import sys
from PySide import QtGui, QtCore
import time

class MySigObj(QtCore.QObject):
    strSig = QtCore.Signal(str)
    tupSig = QtCore.Signal(tuple)

class UploadThread(QtCore.QThread):

    def __init__(self, parent=None):
        super(UploadThread, self).__init__(parent)
        self.endNow = False
        self.fileName = None
        self.sig = MySigObj()
        self.fileNames = []
        self.uploaded = []

    @QtCore.Slot(str)
    def setFileNames(self, t):
        self.fileNames = list(t)

    def run(self):
        while self.fileNames:
            print(self.fileNames)
            time.sleep(2)
            name = self.fileNames.pop(0)
            s = 'uploaded file: ' + name + '\n'
            print(s)
            self.sig.strSig.emit(s)
            self.uploaded.append(name)
            if len(self.fileNames) == 0:
                self.sig.strSig.emit("files transmitted: %s" % str(self.uploaded))
        else:
            time.sleep(1)   #if the thread started but no list, wait 1 sec every cycle thru
                            #that was this thread should release the Python GIL (Global Interpreter Lock)


class ULoadWin(QtGui.QWidget):

    def __init__(self, parent=None):
        super(ULoadWin, self).__init__(parent)
        self.upThread = UploadThread()
        self.sig = MySigObj()
        self.sig.tupSig.connect(self.upThread.setFileNames)
        self.upThread.sig.strSig.connect(self.txtMsgAppend)
        self.sig.tupSig.connect(self.upThread.setFileNames)
        self.layout = QtGui.QVBoxLayout()
        self.stButton = QtGui.QPushButton("Start")
        self.stButton.clicked.connect(self.uploadItems)
        self.stpButton = QtGui.QPushButton("Stop")
        self.stpButton.clicked.connect(self.killThread)
        self.testButton = QtGui.QPushButton("write txt\n not(?) blocked \nbelow")
        self.testButton.setMinimumHeight(28)
        self.testButton.clicked.connect(self.tstBlking)
        self.lbl = QtGui.QTextEdit()
        self.lbl.setMinimumHeight(325)
        self.lbl.setMinimumWidth(290)

        self.layout.addWidget(self.stButton)
        self.layout.addWidget(self.stpButton)
        self.layout.addWidget(self.testButton)
        self.layout.addWidget(self.lbl)

        self.setLayout(self.layout)

        self.l = ['a', 'list', 'of_files', 'we', 'will_pretend_to_upload', 'st', 'uploading']
        self.upThread.start()

    def tstBlking(self):
        self.lbl.append("txt not(?) blocked")

    def uploadItems(self):
        t = tuple(self.l)
        self.sig.tupSig.emit(t)
        self.upThread.start()

    def killThread(self):
        self.upThread.terminate()
        time.sleep(.01)
        self.upThread = UploadThread()

    @QtCore.Slot(str)
    def txtMsgAppend(self, txt):
        self.lbl.append(txt + "  |  ")


if __name__ == '__main__':
    app=QtGui.QApplication(sys.argv)
    widg=ULoadWin()
    widg.show()
    sys.exit(app.exec_())

最后,我通过调整概述的方法解决了这个问题,我发现这是一个优雅的解决方案。我创建的类如下所示:

类上载线程(threading.Thread):
#输入和结果是队列。队列对象
定义初始化(自身、输入、结果):
super(上传线程,self)。\uuuu init\uuuu()
self.input\u q=输入
self.result\u q=结果
self.stoprequest=threading.Event()#threadsafe标志
def运行(自):
''将无限期运行,直到调用self.join()为止。
一旦项目被放置在输入_q中,线程就会处理它们,直到输入_q被清空。
'''
而不是self.stoprequest.isSet():#可以从主gui设置stoprequest
尝试:
#Queue.get超时以允许检查self.stoprequest
num=self.input_q.get(True,0.1)#当队列为空时,它会等待100毫秒后再引发队列。空错误
打印“线程中,正在处理”,num
睡眠时间(0.5)
self.result_q.put(True)#向主线程指示项目已成功处理。
队列除外。空为e:
持续
def连接(自身,超时=无):
self.stoprequest.set()
超级(上传线程,自我).join(超时)
在主线程中,创建上载线程,并在输入_q中加载要上载的项目。创建一个QTimer,通过检查结果_q中的内容,定期检查上传进度。它还会更新进度条。如果在超时时间内未取得任何进展,则表示上载连接失败


使用Queue.Queue对象在线程之间进行通信的一个优点是,可以创建共享相同输入和结果队列的多个线程。

感谢您的输入。这如何解决检测上传是否挂起的问题?很抱歉,当前代码还没有实现超时功能。不过,还是去看看吧。他的基于QThread的应用程序示例代码实现了一个超时功能,其工作原理与您所需的相同。他在任何地方都没有将其标记为超时,但请查看他的类MyLongThread如何知道何时关闭。祝你好运&很抱歉反应太慢,这不是一个坏方法。但我看不出它是如何处理检测何时上传挂起的情况的。