mugetsu
mugetsu

Reputation: 4398

QtCore.QTimer.singleShot lags when calling a function iteratively

I have pyqt gui with some text display that is updated periodically via function updateTelemetry()

Here is how my code works. When the user clicks the button, the buttonHandler is called. And updateTelemetry is called iteratively every 10s.:

def buttonHandler(self):
    self.monitor=true
    self.updateTelemetry()

def updateTelemetry(self):
   try:
        #update values on gui
   finally:
       if self.monitor:
          QtCore.QTimer.singleShot(10000, self.updateTelemetry)

This approach works, and lets me get an update around every 10s. However, every 10s, the entire gui freezes for a few seconds and then updates. Something that I'm doing seems to be blocking. I'm not sure how that is happening. I thought that qtimer.singleshot would create a separate thread?

Is there a better way to do what I'm doing?

Upvotes: 0

Views: 1351

Answers (1)

three_pineapples
three_pineapples

Reputation: 11849

A QTimer does not create a separate thread. QTimer simply emits a signal in the main thread after a given timeout. As such, everything (including your device reading code) is still running in the main thread.

You should move the code that reads from your device to a QThread and emit a signal from the QThread to the main thread where you can update your GUI. GUI updates must only be done from the main thread. Only signal emission is thread safe!

The following is a rough implementation of a QThread.

class MyThread(QObject):
    send_data_to_gui = pyqtSignal(str)

    def __init__(self,*args,**kwargs):
        QObject.__init__(self,*args,**kwargs)

    @pyqtSlot()
    def run(self):
        while True: 
            # get data from device
            self.send_data_to_gui.emit(data_from_device)
            time.sleep(10) 

...

thread = QThread()
my_thread = MyThread()
my_thread.send_data_to_gui.connect(my_slot)
my_thread.moveToThread(thread)
thread.started.connect(my_thread.run)
thread.start()

Note that in it's current form, the QThread actually has it's own event loop, so you can do all sorts of complicated things (however the while True is blocking the event loop at the moment). As the thread has an independent event loop, you could replace the while True with a QTimer, configured to fire every 10 seconds (make sure the QTimer is actually created in the thread though. MyThread.__init__ still runs in the main thread but the contents of MyThread.run will be executed in the new thread). You also might want to add the ability to shutdown the thread, or add other functionality I don't know about!

If you have trouble with your implementation, feel free to post a follow-up question on stack overflow!

Upvotes: 1

Related Questions