Python和PySide中带超时的线程化上载
我正在寻找一种基于threading.Thread、multiprocessing或Queue的设计模式,用于上传带有超时的项目列表。线程允许GUI保持响应。如果连接挂起,则应触发超时,程序应正常退出 下面的示例可以工作,但GUI仍然被阻止。如何改进这一点以允许上载列表、手动取消进程、上载进程超时以及非阻塞GUIPython和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
从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如何知道何时关闭。祝你好运&很抱歉反应太慢,这不是一个坏方法。但我看不出它是如何处理检测何时上传挂起的情况的。