2010-05-18 49 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