2017-04-03 80 views
2

我想在我的應用程序中有2個工作線程。一加載GUI就應該開始運行,另一個應該稍後由某個信號啓動。假設這是一個按鈕點擊。Python在PyQt5應用程序中崩潰的2個工作線程

當我的Python解釋器在執行第二個線程時崩潰了(如顯示Windows錯誤「Python停止工作」,沒有堆棧跟蹤),我遇到了一個奇怪的行爲。

下面是一個例子,點擊按鈕後會立即崩潰。

class Worker(QtCore.QThread): 
    def __init__(self, method_to_run): 
     super().__init__() 
     self.method = method_to_run 

    def run(self): 
     self.method() 

class Window(QWidget): 
    def __init__(self): 
     super(Window, self).__init__() 
     self.button = QPushButton('Test', self) 
     self.label = QLabel(self) 
     self.button.clicked.connect(self.handleButton) 
     layout = QVBoxLayout(self) 
     layout.addWidget(self.label) 
     layout.addWidget(self.button) 
     self.worker = Worker(self.test_method) 
     self.worker.start() 

    def handleButton(self): 
     self.label.setText('Button Clicked!') 
     worker = Worker(self.test_method) 
     worker.start() 

    @staticmethod 
    def test_method(): 
     res = [i*i for i in range(100500)] 

if __name__ == '__main__': 

    import sys 
    app = QApplication(sys.argv) 
    window = Window() 
    window.show() 
    sys.exit(app.exec_()) 

更奇怪的是,當您出於某種原因調試應用程序時,它不會崩潰。

我在這裏錯過了什麼?

編輯 我可以從crashdump得到很多,因爲我沒有QT的符號。但它看起來像QtCore.dll內發生崩潰

ExceptionAddress: 00000000632d4669 (Qt5Core!QThread::start+0x0000000000000229) 
+0

您可以包含堆棧跟蹤嗎? – tdelaney

+0

好吧,我的Python解釋器崩潰,出現一個Windows錯誤「Python.exe停止工作」我將嘗試分析崩潰轉儲,但它不會有幫助,因爲我沒有調試符號。沒有Python堆棧跟蹤,因爲解釋器崩潰了...... –

+0

注意:如果這是純粹的Py3代碼,爲簡單起見,可以使用no-arg'super'。 'super(Window,self).__ init __()'只在Py2中需要,在Py3中,'super().__ init __()'更乾淨,更快,行爲相同(除非你做了一些糟糕的事情,在定義它之後在全球範圍內)。 – ShadowRanger

回答

1

的問題是,你沒有保存到線程的引用,以便爲您退出handleButton它儘快刪除。如果你保存了一篇參考文獻,那就提出瞭如何處理它的壽命的問題。

QThread不僅僅是一個系統線程的包裝 - 它實現了其他服務,可以將線程連接到您的GUI。您可以使用其finished處理程序在小部件終止執行任何清理時發出信號。

在此示例中,我將工作人員保存爲self.worker2並第二次阻止啓動該工作人員,直到第一個工作完成。

import PyQt5 
import PyQt5.QtCore as QtCore 
from PyQt5.QtWidgets import * 
import time 

class Worker(QtCore.QThread): 
    def __init__(self, method_to_run): 
     super(Worker, self).__init__() 
     self.method = method_to_run 

    def run(self): 
     self.method() 

class Window(QWidget): 
    def __init__(self): 
     super().__init__() 
     self.button = QPushButton('Test', self) 
     self.label = QLabel(self) 
     self.button.clicked.connect(self.handleButton) 
     layout = QVBoxLayout(self) 
     layout.addWidget(self.label) 
     layout.addWidget(self.button) 
     self.worker = Worker(self.test_method) 
     self.worker.start() 
     self.worker2 = None 

    def handleButton(self): 
     self.label.setText('Button Clicked!') 
     # likely better to disable the button instead... but 
     # this shows events in action. 
     if self.worker2: 
      self.label.setText('Worker already running') 
     else: 
      self.worker2 = Worker(self.test_method) 
      self.worker2.finished.connect(self.handle_worker2_done) 
      self.worker2.start() 

    def handle_worker2_done(self): 
     self.worker2 = None 
     self.label.setText('Worker done') 

    @staticmethod 
    def test_method(): 
     #res = [i*i for i in range(100500)] 
     time.sleep(3) 

if __name__ == '__main__': 

    import sys 
    app = QApplication(sys.argv) 
    window = Window() 
    window.show() 
    sys.exit(app.exec_()) 
1

您的點擊觸發線程實際上並沒有試圖在一個線程中工作。您將稱爲該方法,而不是將該方法作爲參數傳遞,因此您試圖使用返回值test_methodNone)作爲運行方法。

變化:

worker = Worker(self.test_method()) # Calls test_method and tries to run None 

到:

worker = Worker(self.test_method) # Don't call test_method 
+0

對不起,這是一個複製粘貼錯誤:)顯然它不在那裏,仍然崩潰。 –