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.