[英]pyqt emit signal from threading thread
我正在尝试从多个线程更新pyqt QProgressBar,据我所知,实现此目的的最佳方法是将信号发送回主GUI线程(我尝试将QProgressBar对象传递给工作线程,尽管看起来确实如此)上班时,口译员收到了很多警告)。 在下面的代码中,我设置了一个progressSignal信号并将其连接到一个线程(该线程现在仅打印所发出的任何内容)。 然后,我从每个线程发出总百分比。 我知道这可以通过在第47行中抛出一个随机发射来实现,而这确实通过了。 但是,第36行的发射不会触发任何东西,因此它似乎从未通过...
import Queue, threading
from PyQt4 import QtCore
import shutil
import profile
fileQueue = Queue.Queue()
class Communicate(QtCore.QObject):
progressSignal = QtCore.pyqtSignal(int)
class ThreadedCopy:
totalFiles = 0
copyCount = 0
lock = threading.Lock()
def __init__(self, inputList, progressBar="Undefined"):
self.totalFiles = len(inputList)
self.c = Communicate()
self.c.progressSignal.connect(self.updateProgressBar)
print str(self.totalFiles) + " files to copy."
self.threadWorkerCopy(inputList)
def CopyWorker(self):
while True:
self.c.progressSignal.emit(2000)
fileName = fileQueue.get()
shutil.copy(fileName[0], fileName[1])
fileQueue.task_done()
with self.lock:
self.copyCount += 1
percent = (self.copyCount * 100) / self.totalFiles
self.c.progressSignal.emit(percent)
def threadWorkerCopy(self, fileNameList):
for i in range(16):
t = threading.Thread(target=self.CopyWorker)
t.daemon = True
t.start()
for fileName in fileNameList:
fileQueue.put(fileName)
fileQueue.join()
self.c.progressSignal.emit(1000)
def updateProgressBar(self, percent):
print percent
更新:
这是带有gui的示例。 这个程序运行但是很不稳定,它经常崩溃,并且UI做一些奇怪的事情(进度条没有完成,等等)。
Main.py:
import sys, os
import MultithreadedCopy_5
from PyQt4 import QtCore, QtGui
def grabFiles(path):
# gets all files (not folders) in a directory
for file in os.listdir(path):
if os.path.isfile(os.path.join(path, file)):
yield os.path.join(path, file)
class MainWin(QtGui.QWidget):
def __init__(self):
super(MainWin, self).__init__()
self.initUI()
def initUI(self):
self.progress = QtGui.QProgressBar()
box = QtGui.QVBoxLayout()
box.addWidget(self.progress)
goBtn = QtGui.QPushButton("Start copy")
box.addWidget(goBtn)
self.setLayout(box)
goBtn.clicked.connect(self.startCopy)
def startCopy(self):
files = grabFiles("folder/with/files")
fileList = []
for file in files:
fileList.append([file,"folder/to/copy/to"])
MultithreadedCopy_5.ThreadedCopy(fileList, self.progress)
def main():
app = QtGui.QApplication(sys.argv)
ex = MainWin()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
MultithreadedCopy_5.py:
import Queue, threading
from PyQt4 import QtCore
import shutil
import profile
fileQueue = Queue.Queue()
class Communicate(QtCore.QObject):
progressSignal = QtCore.pyqtSignal(int)
class ThreadedCopy:
totalFiles = 0
copyCount = 0
lock = threading.Lock()
def __init__(self, inputList, progressBar="Undefined"):
self.progressBar = progressBar
self.totalFiles = len(inputList)
self.c = Communicate()
self.c.progressSignal.connect(self.updateProgressBar, QtCore.Qt.DirectConnection)
print str(self.totalFiles) + " files to copy."
self.threadWorkerCopy(inputList)
def CopyWorker(self):
while True:
fileName = fileQueue.get()
shutil.copy(fileName[0], fileName[1])
fileQueue.task_done()
with self.lock:
self.copyCount += 1
percent = (self.copyCount * 100) / self.totalFiles
self.c.progressSignal.emit(percent)
def threadWorkerCopy(self, fileNameList):
for i in range(16):
t = threading.Thread(target=self.CopyWorker)
t.daemon = True
t.start()
for fileName in fileNameList:
fileQueue.put(fileName)
fileQueue.join()
def updateProgressBar(self, percent):
self.progressBar.setValue(percent)
#profile.run('ThreadedCopy()')
您的示例有两个主要问题。
首先,发出信号的对象是在main / gui线程中创建的,因此其发出的任何信号都不会是跨线程的,因此不是线程安全的。 显而易见的解决方案是在工作线程的目标函数内创建信令对象-这意味着每个线程必须有一个单独的实例。
其次,目标函数内部的while循环永远不会终止,这意味着在当前复制操作完成后,每个ThreadedCopy
对象都将保持活动状态。 由于所有这些对象共享相同的队列,因此,如果尝试重复复制操作,则该行为将变得不可预测。 明显的解决方案是在队列为空时退出while循环。
下面是对MultithreadedCopy_5.py的重新编写,它应该可以解决这些问题。 但是,正如评论中所述,在这种情况下,我仍然强烈建议使用QThread
而不是python线程,因为它可能会提供更健壮和更易于维护的解决方案。
import Queue, threading
from PyQt4 import QtCore
import shutil
import profile
fileQueue = Queue.Queue()
class Communicate(QtCore.QObject):
progressSignal = QtCore.pyqtSignal(int)
class ThreadedCopy:
totalFiles = 0
copyCount = 0
lock = threading.Lock()
def __init__(self, inputList, progressBar="Undefined"):
self.progressBar = progressBar
self.totalFiles = len(inputList)
print str(self.totalFiles) + " files to copy."
self.threadWorkerCopy(inputList)
def CopyWorker(self):
c = Communicate()
c.progressSignal.connect(self.updateProgressBar)
while True:
try:
fileName = fileQueue.get(False)
except Queue.Empty:
break
else:
shutil.copy(fileName[0], fileName[1])
with self.lock:
self.copyCount += 1
percent = (self.copyCount * 100) / self.totalFiles
c.progressSignal.emit(percent)
fileQueue.task_done()
def threadWorkerCopy(self, fileNameList):
if fileQueue.empty():
for i in range(16):
t = threading.Thread(target=self.CopyWorker)
t.daemon = True
t.start()
for fileName in fileNameList:
fileQueue.put(fileName)
fileQueue.join()
def updateProgressBar(self, percent):
self.progressBar.setValue(percent)
主要问题是发送信号和接收信号之间的时间延迟,我们可以使用processEvents()
减少时间:
当程序忙于执行长时间的操作(例如,复制文件)时,可以偶尔调用此函数。
def CopyWorker(self):
while True:
fileName = fileQueue.get()
shutil.copy(fileName[0], fileName[1])
fileQueue.task_done()
with self.lock:
self.copyCount += 1
print(self.copyCount)
percent = (self.copyCount * 100) / self.totalFiles
self.c.progressSignal.emit(percent)
QtCore.QCoreApplication.processEvents()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.