[PyQt] Simple polling monitor application

Eric Frederich eric.frederich at gmail.com
Fri Jul 19 18:12:50 BST 2013


So from that do_stuff method would I be able to emit signals that are
connected to to slots of objects that would execute in the main GUI
thread?
Is there a way to get a thread id or something so I could put print
statements to verify that things are happening in the correct place?

On Fri, Jul 19, 2013 at 10:37 AM, David Hoese <dhoese at gmail.com> wrote:
> 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