2010-05-18 48 views
3

使用Python 2.5和PyQt 4.4.3,我無法在Python中找到任何此特定問題,所以對不起,如果我重複下面的其他Qt引用問題,但我不容易理解那C代碼。PyQt線程和信號 - 如何正確檢索值

我有兩個班,一個GUI and a thread,我試圖get return values從線程。我用the link in here作爲編寫my code的基礎,它工作的很好。爲了概括起來,在這裏說明在代碼中的問題(我不認爲這個代碼將在自身運行):

#!/usr/bin/python2.5 

# this is a testing module, and almost everything in here is just there to make the script work 
# the relevant issues for the testing are noted in comments 

from PyQt4 import QtCore, QtGui 
import sys, time 

class MainWindow (QtGui.QWidget): 
    def __init__(self, parent=None): 
     QtGui.QWidget.__init__(self, parent) 

     self.buttonDaemon = QtGui.QPushButton(self) 
     self.layout = QtGui.QVBoxLayout(self) 
     self.layout.addWidget(self.buttonDaemon) 
     self.setLayout(self.layout) 

     self.thread = Worker() 
     self.connect(self.thread, QtCore.SIGNAL('finished()'), self.unfreezeUi) 
     self.connect(self.thread, QtCore.SIGNAL('terminated()'), self.unfreezeUi) 
     #self.thread.stop.connect(self.stopped) # part of proposed solution 
     self.connect(self.thread, QtCore.SIGNAL('stopped(int)'), self.stopped) #adapted from proposed solution 

     self.connect(self.buttonDaemon, QtCore.SIGNAL('clicked()'), self.pressDaemon) 

    def unfreezeUi (self): 
     self.buttonDaemon.setEnabled(True) 

    # the problem begins below: I'm not using signals, or queue, or whatever, while I believe I should for StopSignal and DaemonRunning 
    def pressDaemon (self): 
     self.buttonDaemon.setEnabled(False) 
     if self.thread.isDaemonRunning(): 
      self.thread.setDaemonStopSignal(True) 
      self.buttonDaemon.setText('Daemon - run code every %s sec'% 1) 
     else: 
      self.thread.startDaemon() 
      self.buttonDaemon.setText('Stop Daemon') 
      self.buttonDaemon.setEnabled(True) 

    # part of proposed solution 
    def stopped (self, val): 
     print 'stopped ' + str(val) 

class Worker (QtCore.QThread): 
    daemonIsRunning = False 
    daemonStopSignal = False 
    daemonCurrentDelay = 0 

    def isDaemonRunning (self): return self.daemonIsRunning 
    def setDaemonStopSignal (self, bool): self.daemonStopSignal = bool 

    def __init__ (self, parent = None): 
     QtCore.QThread.__init__(self, parent) 
     self.exiting = False 
     self.thread_to_run = None 

    def __del__ (self): 
     self.exiting = True 
     self.thread_to_run = None 
     self.wait() 

    def run (self): 
     if self.thread_to_run != None: 
      self.thread_to_run(mode='continue') 

    #stop = QtCore.pyqtSignal(int) # part of proposed solution 

    def startDaemon (self, mode = 'run'): 
     if mode == 'run': 
      self.thread_to_run = self.startDaemon # I'd love to be able to just pass this as an argument on start() below 
      return self.start() # this will begin the thread 

     # this is where the thread actually begins 
     self.daemonIsRunning = True 
     print 'Daemon started' 
     self.daemonStopSignal = False 
     sleepStep = 0.1 # don't know how to interrupt while sleeping - so the less sleepStep, the faster StopSignal will work 

     # begins the daemon in an "infinite" loop 
     while self.daemonStopSignal == False and not self.exiting: 
      print 'Daemon running' 
      # here, do any kind of daemon service 

      delay = 0 
      while self.daemonStopSignal == False and not self.exiting and delay < 1: 
       print 'Daemon sleeping' 
       time.sleep(sleepStep) # delay is actually set by while, but this holds for 'sleepStep' seconds 
       delay += sleepStep 

     # daemon stopped, reseting everything 
     self.daemonIsRunning = False 
     print 'Daemon stopped' 
     #self.stop.emit(self.daemonIsRunning) # part of proposed solution 
     self.emit(QtCore.SIGNAL('stopped(int)'), self.daemonIsRunning) # adapted from proposed solution 
     self.emit(QtCore.SIGNAL('terminated')) 

def main (args): 
    app = QtGui.QApplication(args) 
    win = MainWindow() 
    win.show() 
    sys.exit(app.exec_()) 

if __name__ == "__main__": 
    main(sys.argv) 

芹苴這是相當大的,我希望這是很清楚的。主要觀點在def pressDaemon。特別是全部3 self.thread調用。最後一個,self.thread.startDaemon()就好了,就像例子一樣。我懷疑這代表了任何問題。

問題是能夠設置守護進程停止信號並檢索值,如果它正在運行。我不確定是否可以在QtCore.QtThread上設置停止信號,因爲我嘗試了相同的方法,但無效。但我很確定從emit檢索return結果是不可能的。

所以,它就是這樣。我正在使用直接調用線程類,並且我幾乎肯定這不是一個好設計,並且在壓力下運行時可能會失敗。我閱讀了關於該隊列的內容,但我不確定這是否是正確的解決方案,或者我應該完全使用Qt,因爲這是Python。只是也許我在做的事情沒有任何問題。

編輯:建議的解決方案之後,我用它來使代碼可以運行。再次感謝Max,爲此!而且我也對這個建議留下了評論,讓它清楚地說明它不利於解決問題。

回答

3

如果要從一個線程向另一個線程發送值,可以聲明該信號。

下面是你的例子,稍作修改,因爲它可以作爲唯一的腳本執行。當你點擊停止守護程序的按鈕並且'MainWindow'的'停止'插槽執行並打印已經成功從一個線程傳送到另一個線程的值時,'停止'信號觸發。

Qt中有一些類型的信號:Queued,Blocking f.x. (RTM,它非常清楚),你可以嚴格聲明它們。但是,帶有省略參數的默認信號聲明會自動識別,如果連接的實體(插槽或其他信號)在另一個線程中且工作正常,則只需使用簡單的聲明方式。

有關信號和插槽的更多信息,請參閱關於Riverbankcomputing的PyQt文檔,或許我的另一個答案就是這個。

from sys import argv, exit 
from PyQt4 import QtCore, QtGui, uic 
import PyQt4 
import time 


class MainWindow (QtGui.QWidget): 
    # this is just a reference and not really relevant to the question 
    def __init__ (self, args): 
     self.app = MainApp(args) 
     QtGui.QWidget.__init__(self) 
     self.buttonDaemon = QtGui.QPushButton(self) 
     self.buttonConvert = QtGui.QPushButton(self) 
     self.layout = QtGui.QVBoxLayout(self) 
     self.layout.addWidget(self.buttonDaemon) 
     self.layout.addWidget(self.buttonConvert) 
     self.setLayout(self.layout) 

     self.thread = Worker() # this does not begin a thread - look at "Worker.run" for mor details 
     self.connect(self.thread, QtCore.SIGNAL('finished()'), self.unfreezeUi) 
     self.connect(self.thread, QtCore.SIGNAL('terminated()'), self.unfreezeUi) 
     self.thread.stop.connect(self.stopped) 

     self.connect(self.buttonDaemon, QtCore.SIGNAL('clicked()'), self.pressDaemon) 

    # the problem begins below: I'm not using signals, or queue, or whatever, while I believe I should 
    def pressDaemon (self): 
     self.buttonDaemon.setEnabled(False) 
     if self.thread.isDaemonRunning(): 
      self.thread.setDaemonStopSignal(True) 
      self.buttonDaemon.setText('Daemon - converts every %s sec'% 1) 
     else: 
      self.buttonConvert.setEnabled(False) 
      self.thread.startDaemon() 
      self.buttonDaemon.setText('Stop Daemon') 
      self.buttonDaemon.setEnabled(True) 

    def unfreezeUi(self): 
     print '!!' 

    def stopped(self, val): 
     print 'stopped ' + str(val) 

# this whole class is just another reference 
class Worker (QtCore.QThread): 
    daemonIsRunning = False 
    daemonStopSignal = False 
    daemonCurrentDelay = 0 

    def isDaemonRunning (self): return self.daemonIsRunning 
    def setDaemonStopSignal (self, bool): self.daemonStopSignal = bool 

    def __init__ (self, parent = None): 
     QtCore.QThread.__init__(self, parent) 
     self.exiting = False 
     self.thread_to_run = None # which def will be running 

    def __del__ (self): 
     self.exiting = True 
     self.thread_to_run = None 
     self.wait() 

    def run (self): 
     if self.thread_to_run != None: 
      self.thread_to_run(mode='continue') 

    stop = QtCore.pyqtSignal(int) 

    def startDaemon (self, mode = 'run'): 
     if mode == 'run': 
      self.thread_to_run = self.startDaemon # I'd love to be able to just pass this as an argument on start() below 
      return self.start() # this will begin the thread 

     # this is where the thread actually begins 
     self.daemonIsRunning = True 
     self.daemonStopSignal = False 
     sleepStep = 0.1 # don't know how to interrupt while sleeping - so the less sleepStep, the faster StopSignal will work 

     # begins the daemon in an "infinite" loop 
     while self.daemonStopSignal == False and not self.exiting: 
      # here, do any kind of daemon service 

      delay = 0 
      while self.daemonStopSignal == False and not self.exiting and delay < 1: 
       time.sleep(sleepStep) # delay is actually set by while, but this holds for N second 
       delay += sleepStep 

     # daemon stopped, reseting everything 
     self.daemonIsRunning = False 
     self.stop.emit(self.daemonIsRunning) 
     self.emit(QtCore.SIGNAL('terminated')) 

class MainApp(QtGui.QApplication): 
    def __init__(self, args): 
     QtGui.QApplication.__init__(self, args) 

if __name__ == "__main__": 
    main = MainWindow(argv) 
    main.show() 
    exit(main.app.exec_()) 
+0

我真的很感激你所有的時間來審查代碼,甚至寫在它的頂部! :)但我無法使用它。我實際上首先認爲這是解決方案,直到能夠嘗試它! :(據我所知,你只是使用新的樣式(http://tinyurl.com/ycdrypv)添加了一個信號,這對我不起作用。我需要**從MainWindow發送一個信號到Thread **,而不是我已經可以做的另一種方式,但我也不確定我應該試着發送這個信號,或者使用其他技術。再次,感謝您的時間,並且確保我會閱讀所有的內容!:) – cregox 2010-05-18 16:31:28

+0

哦,對不起。我誤解了你。 你見過這個話題嗎? http://stackoverflow.com/questions/638251/how-to-emit-cross-thread-signal-in-qt – 2010-05-19 07:07:56

+0

好吧,以前沒見過,但它對我的幫助仍然不大。它不僅僅是C,而且它只是在一個地方發送信號。它確實給了我一個新的'語法猜測'的想法...我會嘗試它並讓你知道。附:在SO/SE網站中,我只收到我的問題的答案,我的帖子的評論(答案或問題)以及我評論的帖子中的提及。在這種情況下,如果您通過提及我或者像我一樣意外通知我,我只會閱讀您的「答案」。 – cregox 2010-05-19 17:17:32