2017-07-18 97 views
1

私は初心者ですので、私の質問に耐えてください。私はpyqtのプログレスバーを実装する際に問題を抱えていますが、私が見てきたすべての例は正しく実装する方法を実際には説明していません。exampleexample私は幾分部分的に機能させましたが、私はこのコードを持っている:マルチスレッドのPython pyqtパルス進行状況バー

class Window(QtGui.QMainWindow): 
    def __init__(self): 
     super(Window, self).__init__() 
     self.setGeometry(750, 450, 400, 200) 
     self.setFixedSize(self.size()) 
     btn1 = QtGui.QPushButton("Convert", self) 
     btn1.move(210,171) 
     btn1.clicked.connect(self.progbar) 

    def progbar (self): 
     self.prog_win = QDialog() 
     self.prog_win.resize(400, 100) 
     self.prog_win.setFixedSize(self.prog_win.size()) 
     self.prog_win.setWindowTitle("Processing request") 
     self.lbl = QLabel(self.prog_win) 
     self.lbl.setText("Please Wait. . .") 
     self.lbl.move(15,18) 
     self.progressBar = QtGui.QProgressBar(self.prog_win) 
     self.progressBar.resize(410, 25) 
     self.progressBar.move(15, 40) 
     self.progressBar.setRange(0,1) 
     self.myLongTask = TaskThread() 

     #I think this is where I am wrong 
     #because all of the answers here is very specific 
     #or just not for beginners 
     self.prog_win.show() 
     self.myLongTask.taskFinished.connect(self.onStart) 
     self.output_settings() 

    def onStart(self): 
     self.progressBar.setRange(0,0) 
     self.myLongTask.start() 

    def output_convert(self): 
     #very long process to convert a txt file to excel 

#My Thread 
class TaskThread(QtCore.QThread): 
    taskFinished = QtCore.pyqtSignal() 
    def run(self): 
     time.sleep(3) 
     self.taskFinished.emit() 

def run(): 
    app = QtGui.QApplication(sys.argv) 
    GUI = Window() 
    app.exec_() 
run() 

をここで例と投稿はすべて、プログレスバーの実装を理解する上ではなく、すべての例では、私はプログレスバーの実装を理解することはできません特定の問題のための具体的な答えを持って非常に参考にされています標準pyqtアプリであなたは少なくとも正しい方向に私を指差してくれますか?感謝します。

答えて

0

これは非常に基本的なプログレスバーで、最低限必要なものだけを使用します。

この全体の例を最後まで読むことは賢明でしょう。

import sys 
import time 

from PyQt5.QtWidgets import (QApplication, QDialog, 
          QProgressBar, QPushButton) 

TIME_LIMIT = 100 

class Actions(QDialog): 
    """ 
    Simple dialog that consists of a Progress Bar and a Button. 
    Clicking on the button results in the start of a timer and 
    updates the progress bar. 
    """ 
    def __init__(self): 
     super().__init__() 
     self.initUI() 

    def initUI(self): 
     self.setWindowTitle('Progress Bar') 
     self.progress = QProgressBar(self) 
     self.progress.setGeometry(0, 0, 300, 25) 
     self.progress.setMaximum(100) 
     self.button = QPushButton('Start', self) 
     self.button.move(0, 30) 
     self.show() 

     self.button.clicked.connect(self.onButtonClick) 

    def onButtonClick(self): 
     count = 0 
     while count < TIME_LIMIT: 
      count += 1 
      time.sleep(1) 
      self.progress.setValue(count) 

if __name__ == "__main__": 
    app = QApplication(sys.argv) 
    window = Actions() 
    sys.exit(app.exec_()) 

プログレスバーが最初ようfrom PyQt5.QtWidgets import QProgressBar

ようにインポートされそしてラインself.progress.setGeometry(0, 0, 300, 25)方法ダイアログと幅と高さにx,y位置を定義QtWidgets

に他のウィジェットのように初期化されます進行状況バーの

次に、.move()を使用して30pxをボタンを下に移動し、2つのウィジェットの間に隙間があるようにします(5px)。

ここではself.progress.setValue(count)が進捗の更新に使用されます。 .setMaximum()を使用して最大値を設定すると、自動的に値が計算されます。たとえば、最大値が50と設定されている場合、TIME_LIMITが100であるため、毎秒0〜1〜2ではなく0〜2〜4%の範囲でホップします。 .setMinimum()を使用して最小値を設定して、プログレスバーを強制的に所定の値から開始することもできます。

このプログラムを実行すると、これに類似したGUIが生成されます。

Progress Bar Dialog Not Responding

あなたが見ることができるようにカウンターがTIME_LIMIT条件を満たすまで、GUIは、ほとんど間違いなくフリーズして反応しなくなります。これは、time.sleepが、OSにプログラムが無限ループに陥っていると信じさせるためです。

QThread

、どのように我々はこの問題を克服していますか? PyQt5が提供するスレッドクラスを使用できます。

import sys 
import time 

from PyQt5.QtCore import QThread, pyqtSignal 
from PyQt5.QtWidgets import (QApplication, QDialog, 
          QProgressBar, QPushButton) 

TIME_LIMIT = 100 

class External(QThread): 
    """ 
    Runs a counter thread. 
    """ 
    countChanged = pyqtSignal(int) 

    def run(self): 
     count = 0 
     while count < TIME_LIMIT: 
      count +=1 
      time.sleep(1) 
      self.countChanged.emit(count) 

class Actions(QDialog): 
    """ 
    Simple dialog that consists of a Progress Bar and a Button. 
    Clicking on the button results in the start of a timer and 
    updates the progress bar. 
    """ 
    def __init__(self): 
     super().__init__() 
     self.initUI() 

    def initUI(self): 
     self.setWindowTitle('Progress Bar') 
     self.progress = QProgressBar(self) 
     self.progress.setGeometry(0, 0, 300, 25) 
     self.progress.setMaximum(100) 
     self.button = QPushButton('Start', self) 
     self.button.move(0, 30) 
     self.show() 

     self.button.clicked.connect(self.onButtonClick) 

    def onButtonClick(self): 
     self.calc = External() 
     self.calc.countChanged.connect(self.onCountChanged) 
     self.calc.start() 

    def onCountChanged(self, value): 
     self.progress.setValue(value) 

if __name__ == "__main__": 
    app = QApplication(sys.argv) 
    window = Actions() 
    sys.exit(app.exec_()) 

これらの変更を分解しましょう。分割していくつかの部分を実行するPyQt5実装で

from PyQt5.QtCore import QThread, pyqtSignal 

このラインの輸入Qthread:バックグラウンドでのプログラムの(例えば、機能、クラスは、)(マルチスレッドとしても知られます)。これらの部分はスレッドとも呼ばれます。すべてのPyQt5プログラムは、メインプログラムを維持したまま、デフォルトでメインスレッドを持ち、他のスレッド(ワーカースレッド)は余分な時間がかかり、処理が集中するタスクをバックグラウンドにオフロードするために使用されます。

第2のインポートpyqtSignalは、ワーカーとメインスレッド間でデータ(信号)を送信するために使用されます。この例では、メインスレッドにプログレスバーを更新するように指示するために使用します。

今度はカウンタのwhileループをExternalという別のクラスに移動しました。我々は本質的に別のスレッドで実行することができるクラスにExternalを変換するQThreadをサブクラス化することによって

class External(QThread): 
    """ 
    Runs a counter thread. 
    """ 
    countChanged = pyqtSignal(int) 

    def run(self): 
     count = 0 
     while count < TIME_LIMIT: 
      count +=1 
      time.sleep(1) 
      self.countChanged.emit(count) 

。スレッドは、いつでもそのスレッドの利点を追加して開始または停止することができます。

ここではcountChangedが現在の進行状況であり、pyqtSignal(int)は、ワーカースレッドに、送信される信号がタイプintであることを通知します。一方、self.countChanged.emit(count)は、メインスレッドの任意の接続に信号を送信するだけです(通常、他のワーカースレッドと通信するためにも使用できます)。

def onButtonClick(self): 
     self.calc = External() 
     self.calc.countChanged.connect(self.onCountChanged) 
     self.calc.start() 

def onCountChanged(self, value): 
    self.progress.setValue(value) 

ボタンをクリックするとself.onButtonClickを実行しても、スレッドを開始します。スレッドは.start()で開始されます。先に作成したself.calc.countChangedという信号を、プログレスバー値を更新するために使用したメソッドに接続したことにも注意してください。 External::run::countが更新されるたびにint値もonCountChangedに送信されます。

これは、これらの変更を行った後のGUIの外観です。

QThread Progress Bar

また、はるかに応答感じるべきと凍結しません。

+0

PyQt4を使っていますが、これは本当に助けになりました –

+0

@JuanCarlosAsuncion^_ ^ – daegontaven

1

私自身の質問に対する答え。クラスを介して変数をスレッド化して渡すという概念を理解できれば、それほど難しいことではありません。私の最初の間違いは実際にWorkerスレッドについての知識の欠如でした.2度目は私があなたがThreadと宣言すると、メインクラスの中で関数を実行するように呼び出す必要があることを意味していました。それを実装し、私は間違っていると思った。ロングdef runの下にサブクラス化QThreadにする必要がありますし、あなたのclass Window(QtGui.QMainWindow):またはメインループ内で呼び出されると、これは私のコードは今

class Window(QtGui.QMainWindow): 
    def __init__(self): 
     super(Window, self).__init__() 
     self.setGeometry(750, 450, 400, 200) 
     self.setFixedSize(self.size()) 
     btn1 = QtGui.QPushButton("Convert", self) 
     btn1.move(210,171) 
     btn1.clicked.connect(self.progbar) 

    def progbar (self): 
     self.prog_win = QDialog() 
     self.prog_win.resize(400, 100) 
     self.prog_win.setFixedSize(self.prog_win.size()) 
     self.prog_win.setWindowTitle("Processing request") 
     self.lbl = QLabel(self.prog_win) 
     self.lbl.setText("Please Wait. . .") 
     self.lbl.move(15,18) 
     self.progressBar = QtGui.QProgressBar(self.prog_win) 
     self.progressBar.resize(410, 25) 
     self.progressBar.move(15, 40) 
     self.progressBar.setRange(0,1) 

     self.myLongTask = TaskThread(var = DataYouWantToPass) #initializing and passing data to QThread 
     self.prog_win.show() 
     self.onStart() #Start your very very long computation/process 
     self.myLongTask.taskFinished.connect(self.onFinished) #this won't be read until QThread send a signal i think 

    def onStart(self): 
     self.progressBar.setRange(0,0) 
     self.myLongTask.start() 

    #added this function to close the progress bar 
    def onFinished(self): 
     self.progressBar.setRange(0,1) 
     self.prog_win.close() 


#My Thread 
class TaskThread(QtCore.QThread): 
    taskFinished = QtCore.pyqtSignal() 

    #I also added this so that I can pass data between classes 
    def __init__(self, var, parent=None): 
     QThread.__init__(self, parent) 
     self.var = var 

    def run(self): 
     #very long process to convert a txt file to excel 

def run(): 
    app = QtGui.QApplication(sys.argv) 
    GUI = Window() 
    app.exec_() 
run() 
どのように見えるかであるべきである処理/すべてのハード

ソリューション

この回答の内容が間違っている場合は、それを多かれ少なかれ理解してくれると助かります。

関連する問題