2012-11-07 13 views
10

次のコードを使用すると、数秒後にアプリケーションが停止します。 そして、ストールによって私はハングを意味する。私は待っているか強制的に閉じるというWindowsからウィンドウを取得します。Python QT ProgressBar

これは、プログレスバーウィンドウの内側をクリックするか、フォーカスを失うように外側をクリックすると発生します。私が例題を開始して何かに触れないと、それはうまくいくように機能します。

ので、同じようにこれを使用して
from PyQt4 import QtCore 
from PyQt4 import QtGui 


class ProgressBar(QtGui.QWidget): 
    def __init__(self, parent=None, total=20): 
     super(ProgressBar, self).__init__(parent) 
     self.name_line = QtGui.QLineEdit() 

     self.progressbar = QtGui.QProgressBar() 
     self.progressbar.setMinimum(1) 
     self.progressbar.setMaximum(total) 

     main_layout = QtGui.QGridLayout() 
     main_layout.addWidget(self.progressbar, 0, 0) 

     self.setLayout(main_layout) 
     self.setWindowTitle("Progress") 

    def update_progressbar(self, val): 
     self.progressbar.setValue(val) 

:任意の助け

app = QtGui.QApplication(sys.argv) 
bar = ProgressBar(total=101) 
bar.show() 

for i in range(2,100): 
    bar.update_progressbar(i) 
    time.sleep(1) 

感謝。

答えて

12

ループが実行されている間にイベントを処理できるようにする必要があります。これにより、アプリケーションは応答性を保つことができます。

さらに重要なのは、長時間実行するタスクでは、ループが開始されるとループを停止する方法を提供する必要があることです。

これを行う簡単な方法の1つは、タイマーでループを開始してから、ループが実行されている間に定期的にqApp.processEventsを呼び出します。

ここことを行い、デモスクリプトです:

import sys, time 
from PyQt4 import QtGui, QtCore 

class ProgressBar(QtGui.QWidget): 
    def __init__(self, parent=None, total=20): 
     super(ProgressBar, self).__init__(parent) 
     self.progressbar = QtGui.QProgressBar() 
     self.progressbar.setMinimum(1) 
     self.progressbar.setMaximum(total) 
     self.button = QtGui.QPushButton('Start') 
     self.button.clicked.connect(self.handleButton) 
     main_layout = QtGui.QGridLayout() 
     main_layout.addWidget(self.button, 0, 0) 
     main_layout.addWidget(self.progressbar, 0, 1) 
     self.setLayout(main_layout) 
     self.setWindowTitle('Progress') 
     self._active = False 

    def handleButton(self): 
     if not self._active: 
      self._active = True 
      self.button.setText('Stop') 
      if self.progressbar.value() == self.progressbar.maximum(): 
       self.progressbar.reset() 
      QtCore.QTimer.singleShot(0, self.startLoop) 
     else: 
      self._active = False 

    def closeEvent(self, event): 
     self._active = False 

    def startLoop(self): 
     while True: 
      time.sleep(0.05) 
      value = self.progressbar.value() + 1 
      self.progressbar.setValue(value) 
      QtGui.qApp.processEvents() 
      if (not self._active or 
       value >= self.progressbar.maximum()): 
       break 
     self.button.setText('Start') 
     self._active = False 

app = QtGui.QApplication(sys.argv) 
bar = ProgressBar(total=101) 
bar.show() 
sys.exit(app.exec_()) 

UPDATE

は、あなたが(つまりCPythonと)のpythonのC実装を使用している、この問題の解決策は完全にに依存すると仮定するとはは、GUIと同時に実行する必要があるタスクの性質に基づいています。より基本的には、それはGlobal Interpreter Lock (GIL)を持つCPythonによって決定されます。

私はCPythonのGILについて説明するつもりはありません。その代わりに、Dave Beazleyによるこの優れたPyCon videoの視聴を推奨します。バックグラウンドタスクと並行してGUIを実行しようとすると


は一般的に、聞いて最初の質問は:タスクIOバウンドであり、あるいはCPUバウンド?

CPythonはI/O操作用のGILを常にリリースするため、IOバインド(ローカルファイルシステムへのアクセス、インターネットからのダウンロードなど)のソリューションは、通常はかなり簡単です。バックグラウンドタスクは単にasynchronouslyで実行するか、worker threadで実行することができます.GUIの応答性を保つために特別な作業は必要ありません。

CPUバインドタスクの主な問題は、次の質問があります。タスクを一連の小さなステップに分解できますか?

可能であれば、解決策は、現在の保留中のイベントのスタックを処理するためにGUIスレッドに定期的にリクエストを送信することです。上記のデモ・スクリプトは、このテクニックの未知の例です。より一般的には、タスクは別のワーカースレッドで実行され、タスクの各ステップが完了するとgui-updateシグナルが出力されます。 (注:ワーカースレッドが決してGUI関連の操作自体を試みないようにすることが重要です)。

しかし、タスクを小さなステップに分割できない場合は、通常のスレッド型ソリューションは機能しません。 GUIは、スレッドが使用されているかどうかに関係なく、タスクが完了するまでフリーズします。

最後のシナリオでは、唯一の解決策は別のスレッドではなく、別のプロセスを使用することです。すなわち、multiprocessingモジュールを使用します。もちろん、このソリューションは、ターゲットシステムに複数のCPUコアが使用可能な場合にのみ有効です。遊ぶCPUコアが1つしかない場合は、基本的には(Pythonの異なる実装への切り替えや、別の言語への切り替え以外の)役立つことはありません。

+0

これを実行すると、GUIウィンドウが「応答していません。強制終了」エラーで停止します。しかし、すべてのタスクが完了するまで待つと、通常のアプリケーションが続行されます。 – Tuim

+1

@Tuim。私のスクリプトはあなたの質問のコードに基づいた単なるデモです。これは、すべての状況で機能する普遍的な解決策ではありません。あなたは何をしようとしているのかについての適切な説明で質問を更新する必要があります。あなたが言いましたこれらの「仕事」は何ですか?それらはCPUバウンドかIOバウンドですか?あなたが自分で書いたタスクを実行するアプリケーションは変更可能なのでしょうか?どの言語が書かれていますか? etcなど – ekhumoro

+0

これらのタスクは、zipファイルの展開などのインストールです。msi/debパッケージをインストールします。しかし、これは事件にあまり関係しません。このアプリケーションは、Pythonでも書かれており、完全にカスタマイズ可能です。 また、私はコピー貼り付け可能な答えを期待していません!私は正しい方向へのヒントを期待しています。あなたが持っているものは、私にとって正しい方向ではないようです。私は試しました。悪気ない。 – Tuim

0

何らかの形のマルチスレッドを使用する必要があるGUIプログラミングでは、ワーカースレッドとguiを更新し、マウスイベントとキーボードイベントに応答するワーカースレッドが必要になります。これを行うにはいくつかの方法があります。私はこのQProgressBar tutorialを見て、QProgressBarの使い方の優れた実例があることをお勧めします。

チュートリアルでは、QBasicTimerを使用しています.QBasicTimerは、メインスレッドに制御を戻してGUIイベントに応答できるようにする方法です。コード内でtime.sleepを使用すると、実行中の唯一のスレッドが1秒間ブロックされます。

+0

私はそれを認識しています。しかし、彼らは私がこの例のようにスレッド化をこれ以上使用していません。 – Tuim

+1

@Tuim:そうです、 'time.sleep()'は使わないでください。 – Junuxx

+0

@Tuim私はあなたのコードとチュートリアルが同じことをやっていない理由をさらに説明するために答えを更新しました。 –