Home > database >  Periodic task in Qt without interrupting events
Periodic task in Qt without interrupting events

Time:11-30

I have a graphic application in python using QWidget. The user can change elements by moving the mouse (using mouseMoveEvent), and the program should periodically (e.g., once per second) compute a function update_forces based on these elements.

Problem: the mouseMoveEvent doesn't trigger as often while update_forces is computed, so the program becomes periodically unresponsive. There are various tutorials online on how to remedy this using threads, but their solutions don't work for me, and I don't know why.

MWE

# Imports
import sys
import random
import time
from PyQt5.QtCore import QThread, pyqtSignal, QObject
from PyQt5.QtWidgets import QWidget, QApplication

# update_forces dummy
def update_forces():
    for i in range(10000000):
        x = i**2

# Worker object:
class Worker(QObject):
    finished = pyqtSignal()
    def run(self):
        while(True):
            update_forces()
            time.sleep(1)

# and the Window class with a dummy mouseEvent that just prints a random number
class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.setMouseTracking(True)

    def initUI(self):
        self.setGeometry(20, 20, 500, 500)
        self.show()
        self.thread = QThread()
        self.worker = Worker()
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.thread.start()


    def mouseMoveEvent(self, e):
        print(random.random())

# head procedure
if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Window()
    w.show()
    sys.exit(app.exec_())

When I run this (Windows 10) and move my cursor around on the screen, I can clearly observe two phases: it goes from printing numbers rapidly (while update_forces is not computed), to printing them much more slowly (while it is computed). It keeps alternating between both.

CodePudding user response:

The solution is to use multiprocessing rather than threading.

from multiprocessing import Process
if __name__ == '__main__':
    p = Process(target=updater, args=())
    p.start()

def updater(queue,test):
    while(True):
        update_forces()
        time.sleep(1)

The problem with this is that no data is shared between processes by default, but that's solvable.

  • Related