2017-08-07 74 views
0

我试图更新从多个线程的PyQt QProgressBar发出的信号,并从我的理解来做到这一点的最好办法是通过发射信号返回到主界面线程(我想传递QProgressBar对象对工作者线程,尽管它似乎工作,我在解释器中得到了很多警告)。在下面的代码中,我设置了一个progressSignal信号并将其连接到一个线程(现在)只是打印发出的任何信号。然后我从每个线程发出总比例。我知道这可以在线程之外工作,只需在47行中抛出一个随机发射即可。然而从线36 EMIT不会触发任何东西,所以它似乎永远不会让它通过......PyQt的线程从线程

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 

UPDATE:

继承人与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()') 
+0

我开始意识到,蟒蛇线程不能发射信号返回到主PyQt的应用程序,因此,“正确”的方式做,这将与PyQt的QThreads - 我宁愿不做。每当工作线程完成时,是否会有简单的方法在主线程中发出信号? – Spencer

+0

请参阅我的答案,解决方案应至少解决问题中显示的示例问题。 – ekhumoro

回答

-2

的主要问题是在发送信号和接收之间的时间延迟,我们可以使用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() 
+0

完美!谢谢,那正是我需要的。 – Spencer

+0

为什么downvote? – eyllanesc

+0

“插槽在信号线程中执行。”这听起来不太好。不应该GUI元素只能由GUI线程修改? – Trilarion

1

没有与你的例子两个主要的问题。

首先,在主/ GUI线程被创建,其发射的信号中的对象,因此,任何信号发射其不会跨线程,因此不是线程安全。此显而易见的解决方案是创建的信令对象内部工作线程的目标函数 - 这意味着必须有对于每个线程一个单独的实例。其次,目标函数内部的while循环永远不会终止,这意味着每个ThreadedCopy对象将在当前的复制操作完成后保持活动状态。由于所有这些对象共享相同的队列,如果尝试重复复制操作,行为将变得不可预知。这个明显的解决方案是在队列为空时跳出while循环。

下面是一个重写MultithreadedCopy_5.py应该解决这些问题。然而,正如评论所说,我仍然强烈建议使用QThread,而不是在这种情况下蟒蛇线程,因为它很可能会提供一个更强大,更容易维护的解决方案。

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)