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.
# 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.
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.