2017-08-07 9 views
0

私はpyqt QProgressBarを複数のスレッドから更新しようとしています。これを行うための最善の方法は、シグナルをメインのGUIスレッドに戻すことです(QProgressBarオブジェクトワーカーのスレッドには、それが動作するように見えたが、私は通訳で大量の警告を得た)。次のコードでは、progressSignalシグナルを設定し、スレッドに接続して(今のところ)出力されたものだけを出力します。次に、各スレッドから合計パーセンテージを出します。私は、スレッド47の外にランダムな放射を放つだけで、これはスレッドの外で動作することを知っています。それを経由させるように見えることはありませんので、しかしライン36からEMITは、何をトリガしません...pyqt threading threadからのシグナルを放出

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でサンプルを。この1つの実行されますが、それは定期的にクラッシュし、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

私は、Pythonのスレッドがバックメイン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

あなたの例で2つの主要な問題があります。

最初に、シグナルを出すオブジェクトはメイン/ GUIスレッドで作成されるため、シグナルが出すシグナルはクロススレッドではなくスレッドセーフではありません。これに対する明らかな解決策は、内にというワーキングスレッドのターゲット機能を作成することです。つまり、スレッドごとに別々のインスタンスが必要です。

第2に、ターゲット関数内のwhileループが終了することはありません。これは、現在のコピー操作が完了した後にすべてのThreadedCopyオブジェクトが有効に保たれることを意味します。これらのオブジェクトはすべて同じキューを共有しているため、コピー操作を繰り返す試みがあると、その動作は予測できなくなります。これに対する明らかな解決策は、キューが空になるとwhileループから抜け出すことです。

以下は、これらの問題を解決するMultithreadedCopy_5.pyのの書き直しです。しかし、コメントに記載されているように、このシナリオではPythonスレッドではなく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) 
関連する問題