2015-10-12 127 views
9

我正在用PyQt4編寫我的第一個GUI應用程序,我遇到了一個似乎非常基本的問題我似乎沒有找到一個好的答案:在PyQt中,在主窗口和線程之間共享數據的最佳方式是什麼

我正在使用線程來連續執行重複任務而不阻塞主窗口。線程需要來自主窗口的一些信息(例如,spinbox的當前值),這些信息在線程的運行時期間也可以更改。在這種情況下,在主窗口和線程之間共享這些數據的正確方法是什麼?

天真,我能想出了以下可能性:

  1. 傳遞引用到主機窗口的線程,並用它來檢索問題(見下面的例子)變量的當前值。
  2. 在線程中保留一個變量的副本,並通過每當它發生變化時發出信號使其與主窗口保持同步。
  3. 使用全局變量。

所有三個選項很可能適用於我的特殊用例(雖然2會有點複雜),但我有一種感覺應該有更好/更Pythonic /更Qt的方式。

以下是說明什麼,我想用選項1做的,在這種情況下,最低工作例如:

from PyQt4 import QtGui, QtCore 
import time, sys 

class MainWindow(QtGui.QWidget): 
    def __init__(self): 
     super(MainWindow, self).__init__() 
     self.layout = QtGui.QVBoxLayout(self) 
     self.spinbox = QtGui.QSpinBox(self) 
     self.spinbox.setValue(1) 
     self.layout.addWidget(self.spinbox) 
     self.output = QtGui.QLCDNumber(self) 
     self.layout.addWidget(self.output) 

     self.worker = Worker(self) 
     self.connect(self.worker, QtCore.SIGNAL('beep'), self.update) 
     self.worker.start() 

    def update(self, number): 
     self.output.display(number) 


class Worker(QtCore.QThread): 
    def __init__(self, host_window): 
     super(Worker, self).__init__() 
     self.host = host_window 
     self.running = False 

    def run(self): 
     self.running = True 
     i = 0 
     while self.running: 
      i += 1 
      self.emit(QtCore.SIGNAL('beep'), i) 
      sleep_time = self.host.spinbox.value() 
      time.sleep(sleep_time) 

    def stop(self): 
     self.running = False 


app = QtGui.QApplication(sys.argv) 
window = MainWindow() 
window.show() 
app.exec_() 

PS:因爲我完全沒有經驗與PyQt的這不是不可能的,也有其他問題代碼或問題不清楚。在這種情況下,請隨時評論或編輯問題。

+0

儘管我的答案,你的代碼似乎正常工作,所以也許我米錯或有其他我不明白的東西。 – Mel

+1

@tmoreau。在這種特殊情況下,工作線程只需訪問旋轉框中的值。如果它試圖改變這個值,那很可能會導致一個問題(因爲它可能導致GUI更新)。但是,調用'value()'仍然不是線程安全的,因爲用戶可以同時更改旋轉框值,並且這種更改可能不是* atomic *操作。即使如此,可能發生的最糟糕的情況是,工作線程可能會從旋轉框中獲得「陳舊」值。 – ekhumoro

回答

8

Widget是不是線程安全的,請參閱Threads and QObjects

儘管QObject的是可重入的GUI類,特別是QWidget的和 所有它的子類,是不可重入。它們只能從 主線程使用。

而且看到更多的定義在這裏:Reentrancy and Thread-Safety


您應該只使用小部件在主線程,並使用信號和插槽與其它線程通信。

我不認爲一個全局變量會起作用,但我真的不知道爲什麼。


如何使用信號在這個例子:

#in main 
self.worker = Worker(self.spinbox.value()) 
self.worker.beep.connect(self.update) 
self.spinbox.valueChanged.connect(self.worker.update_value) 

class Worker(QtCore.QThread): 
    beep=QtCore.pyqtSignal(int) 

    def __init__(self,sleep_time): 
     super(Worker, self).__init__() 
     self.running = False 
     self.sleep_time=sleep_time 

    def run(self): 
     self.running = True 
     i = 0 
     while self.running: 
      i += 1 
      self.beep.emit(i) 
      time.sleep(self.sleep_time) 

    def stop(self): 
     self.running = False 

    def update_value(self,value): 
     self.sleep_time=value 

注:我用的是新型信號和插槽

+0

非常感謝您的參考。所以你說選項1和3不應該被使用。你能否提供一些提示,說明只使用線程之間的信號如何達到期望的行爲?對我來說,這似乎是一個相當基本任務的糟糕複雜情況(至少在我處理我想分享的許多不同信息時)。 – Emil

+0

嗯,我覺得使用線程從來都不是基本的。但是我仍然感到驚訝的是,上面的代碼工作得很好,當我找到的每個文檔都重複「不會改變/訪問小部件而不是主線程」。無論如何,請參閱編輯信號示例。 – Mel

+0

再次感謝您的額外示例!這看起來不錯。 :)我仍然認爲在兩個地方保留兩份相同的信息對我來說似乎很陌生,但現在我終於做了這件事。我還更改了代碼,以便在主線程中儘可能多地處理信息,這樣我只需要在線程之間傳遞最小數量的值。 – Emil

相關問題