ループが実行されている間にイベントを処理できるようにする必要があります。これにより、アプリケーションは応答性を保つことができます。
さらに重要なのは、長時間実行するタスクでは、ループが開始されるとループを停止する方法を提供する必要があることです。
これを行う簡単な方法の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の異なる実装への切り替えや、別の言語への切り替え以外の)役立つことはありません。
これを実行すると、GUIウィンドウが「応答していません。強制終了」エラーで停止します。しかし、すべてのタスクが完了するまで待つと、通常のアプリケーションが続行されます。 – Tuim
@Tuim。私のスクリプトはあなたの質問のコードに基づいた単なるデモです。これは、すべての状況で機能する普遍的な解決策ではありません。あなたは何をしようとしているのかについての適切な説明で質問を更新する必要があります。あなたが言いましたこれらの「仕事」は何ですか?それらはCPUバウンドかIOバウンドですか?あなたが自分で書いたタスクを実行するアプリケーションは変更可能なのでしょうか?どの言語が書かれていますか? etcなど – ekhumoro
これらのタスクは、zipファイルの展開などのインストールです。msi/debパッケージをインストールします。しかし、これは事件にあまり関係しません。このアプリケーションは、Pythonでも書かれており、完全にカスタマイズ可能です。 また、私はコピー貼り付け可能な答えを期待していません!私は正しい方向へのヒントを期待しています。あなたが持っているものは、私にとって正しい方向ではないようです。私は試しました。悪気ない。 – Tuim