PySide GUI アプリケーションでかなり一般的なことをしようとしています。CPU を集中的に使用するタスクをバックグラウンド スレッドに委任して、GUI の応答性を維持し、計算の進行中に進行状況インジケーターを表示することもできます。
これが私がやっていることです(私はPython 2.7、Linux x86_64でPySide 1.1.1を使用しています):
import sys
import time
from PySide.QtGui import QMainWindow, QPushButton, QApplication, QWidget
from PySide.QtCore import QThread, QObject, Signal, Slot
class Worker(QObject):
done_signal = Signal()
def __init__(self, parent = None):
QObject.__init__(self, parent)
@Slot()
def do_stuff(self):
print "[thread %x] computation started" % self.thread().currentThreadId()
for i in range(30):
# time.sleep(0.2)
x = 1000000
y = 100**x
print "[thread %x] computation ended" % self.thread().currentThreadId()
self.done_signal.emit()
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
self.work_thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.work_thread)
self.work_thread.started.connect(self.worker.do_stuff)
self.worker.done_signal.connect(self.work_done)
def initUI(self):
self.btn = QPushButton('Do stuff', self)
self.btn.resize(self.btn.sizeHint())
self.btn.move(50, 50)
self.btn.clicked.connect(self.execute_thread)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Test')
self.show()
def execute_thread(self):
self.btn.setEnabled(False)
self.btn.setText('Waiting...')
self.work_thread.start()
print "[main %x] started" % (self.thread().currentThreadId())
def work_done(self):
self.btn.setText('Do stuff')
self.btn.setEnabled(True)
self.work_thread.exit()
print "[main %x] ended" % (self.thread().currentThreadId())
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
アプリケーションは、ボタン付きの 1 つのウィンドウを表示します。ボタンが押されると、計算の実行中にボタンが無効になると思います。次に、ボタンを再度有効にする必要があります。
代わりに、ボタンを押すと、計算中にウィンドウ全体がフリーズし、計算が終了すると、アプリケーションの制御を取り戻すことができます。ボタンが無効になっているようには見えません。私が気づいた面白いことは、CPU 集中型の計算をdo_stuff()
単純な time.sleep() に置き換えると、プログラムが期待どおりに動作することです。
何が起こっているのか正確にはわかりませんが、2 番目のスレッドの優先度が高すぎて、実際には GUI スレッドがスケジュールされないようになっているようです。2 番目のスレッドが BLOCKED 状態になると (a で発生するようにsleep()
)、GUI が実際に実行され、期待どおりにインターフェイスが更新される可能性があります。ワーカースレッドの優先度を変更しようとしましたが、Linux ではできないようです。
また、スレッド ID を出力しようとしましたが、正しく実行されているかどうかわかりません。そうであれば、スレッド アフィニティは正しいようです。
私も PyQt でプログラムを試してみましたが、動作はまったく同じなので、タグとタイトルです。PySide の代わりに PyQt4 で実行できる場合は、アプリケーション全体を PyQt4 に切り替えることができます