[PyQt] Simple polling monitor application

David Hoese dhoese at gmail.com
Fri Jul 19 15:37:13 BST 2013


Ah I forgot one important piece. I structure my QThreads as calling 
methods of QObjects when they start so I missed that you need to call 
"self.exec_()" from the QThread's run method (which should be the 
default in any new-ish versions of PyQt). The exec method starts the 
Threads own event loop so it can actually process signals (like the 
Timer) on its own. I'm not sure if it's possible to do this by only 
using a QThread subclass since the QThread object is just a "handle" to 
the thread, not the thread itself, see the "You're doing it wrong" post 
from before.

So how I've always done this is create a QObject that has methods for 
all of the stuff I need to do, create a QThread, move the QObject to 
that thread, connect up my signals, and start the thread. The blog post 
I linked to before and the documentation 
(http://pyqt.sourceforge.net/Docs/PyQt4/qthread.html) should be helpful 
in understanding this. Here's an example:

class MyObject(QtCore.QObject):
     def run(self):
         # set up timer and start the timer

     def do_stuff(self):
         # ...do stuff

my_thread = QtCore.QThread()
my_obj = MyObject()
my_obj.moveToThread(my_thread)
my_thread.started.connect(my_obj.run)
my_thread.start()

When I was running older version of PyQt I had to subclass QThread to 
explicitly call exec_ from the run method:

class MyThread(QtCore.QThread):
     def run(self):
         self.exec_()

-Dave

On 7/19/2013 9:23 AM, Eric Frederich wrote:
> I thought that signals get processed on the same thread that the GUI runs in.
> So in connecting the timer's timeout signal to self.get_stuff, then
> wouldn't the get_stuff method run on the same GUI thread?
> The point of using another thread was to do the actual long-running
> work in the other thread.
> I think with what you suggested only starting the timer would be done
> in another thread, not the actual work.
>
> Of course I could be entirely wrong.
>
> On Fri, Jul 19, 2013 at 9:37 AM, David Hoese <dhoese at gmail.com> wrote:
>> Hi Eric,
>>
>> One thing that was recommended to me when I started doing monitoring GUIs
>> was to use a QTimer of 0 to call the polling function. To do this for your
>> code, move everything in the "while True" into it's own function and make it
>> run on a per-iteration way. So in your example, "count" would become an
>> instance attribute and I think that's it.
>>
>> Then in the run method you would create a QTimer and set it to call that new
>> per-iteration function:
>>
>>      self.my_timer = QtCore.QTimer()
>>      self.my_timer.timeout.connect(self.get_stuff)
>>      self.my_timer.start(0) # or some amount of delay
>>
>> The 0 delay makes it continuously add the "get_stuff" method to the event
>> loop. This should do what you want. You may run in to trouble when closing
>> the GUI if your polling does any kind of blocking. Once you tell the thread
>> to stop (an event in the event loop) the thread won't get to that event
>> until the blocking operation is complete. Depending on the length of the
>> blocking, your GUI could hang on close for a little bit. There are ways
>> around this, but I haven't found one that I love. Hopefully that made sense.
>>
>> Please CC me in any replies, thanks.
>>
>> -Dave
>>
>> P.S. I'll point you to a blog post about not subclassing QThread as the
>> preferred method (get your stuff working first):
>> http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
>>
>>
>> On 7/19/2013 6:00 AM, eric.frederich at gmail.com wrote:
>>>
>>> Hello,
>>>
>>> I wrote an example monitoring application where I have a separate
>>> thread monitoring something via an external process which could take
>>> 20ms to run or it could take 2 seconds to run.
>>> Right now I have it just doing a time.sleep(1) for the example.
>>>
>>> I want this monitoring to continuously happen.
>>> It is an endless (while True) loop.
>>> I'd prefer to use a QTimer but don't know how to get a QTimer to
>>> trigger something in another thread.
>>> The work needs to be done in another thread to keep the GUI responsive.
>>>
>>> Can someone point me in the right direction or modify the following
>>> code to use a QTimer rather than an infinite loop with a sleep
>>> command?
>>>
>>>
>>> #!/usr/bin/env python
>>>
>>> from PyQt4.QtCore import *
>>> from PyQt4.QtGui import *
>>>
>>> import time
>>> from datetime import datetime
>>>
>>> class MyThread(QThread):
>>>       def __init__(self, parent=None):
>>>           super(MyThread, self).__init__(parent)
>>>
>>>       def run(self):
>>>           count = 0
>>>           while True:
>>>               count += 1
>>>               print 'do_something ', count, 'begin'
>>>               time.sleep(1)
>>>               self.emit(SIGNAL("new_info"), str(count) + ' ' +
>>> str(datetime.now()))
>>>               print 'do_something ', count, 'end'
>>>
>>> class MyWidget(QWidget):
>>>       def __init__(self, parent=None):
>>>           super(MyWidget, self).__init__(parent)
>>>           self.setWindowTitle("Monitor")
>>>           layout = QVBoxLayout()
>>>           self.line = QLineEdit()
>>>           layout.addWidget(self.line)
>>>           self.setLayout(layout)
>>>
>>>           self.mon = MyThread()
>>>           self.connect(self.mon, SIGNAL("new_info"), self.on_new_info)
>>>           self.mon.start()
>>>
>>>       def on_new_info(self, info):
>>>           print 'got new info', info
>>>           self.line.setText(info)
>>>
>>> if __name__ == '__main__':
>>>       import sys
>>>       app = QApplication(sys.argv)
>>>       gm = MyWidget()
>>>       gm.show()
>>>       sys.exit(app.exec_())


More information about the PyQt mailing list