我想在我的PySide GUI應用程序中做一個相當普遍的事情:我想委託一些CPU密集型任務到後臺線程,以便我的GUI保持隨着計算的進行,甚至可以顯示進度指示器。PySide/PyQt - 啓動一個CPU密集線程掛起整個應用程序
下面是我在做什麼(我使用PySide 1.1.1的Python 2.7,Linux的x86_64的):
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()
應用程序顯示一個窗口,一個按鈕。 當按下按鈕時,我期望它在執行計算時自行禁用。然後,該按鈕應該重新啓用。
發生的情況是,當我按下按鈕時,整個窗口在計算結束時凍結,然後當結束時,我重新獲得應用程序的控制權。該按鈕從未顯示爲禁用。 我注意到一件有趣的事情,如果我用簡單的time.sleep()替換do_stuff()
中的CPU密集型計算,則該程序將按預期運行。
我並不完全知道發生了什麼,但看起來第二個線程的優先級太高,實際上阻止了GUI線程被調度。如果第二個線程進入BLOCKED狀態(就像sleep()
那樣),GUI實際上有機會按預期運行和更新接口。我試圖改變工作線程的優先級,但看起來它不能在Linux上完成。
此外,我嘗試打印線程ID,但我不知道如果我正確地做。如果我是,線程親和力似乎是正確的。
我也試過用PyQt程序,行爲完全一樣,因此標籤和標題。如果我可以使用PyQt4而不是PySide運行,我可以將整個應用程序切換到PyQt4
我試着在這個例子中放置'time.sleep(0)'而不是'time.sleep(0.2)'註釋,不幸的是,並沒有解決這個問題。我注意到,像time.sleep(0.05)這樣的值,按鈕的行爲與預期相同。作爲一種解決方法,我可以設置工作人員每秒只報告幾次進度,並進行休眠以讓GUI自行更新。 – user1491306
有線程和GUI的解決方法(例如http://code.activestate.com/recipes/578154)。 –
我最終這樣做了:我在計算開始處放了一個'sleep',並且每次進度指示符更新時,我都會調用'app.processEvents()'以使「Abort」按鈕有效。 – user1491306