Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to correctly use multithreading or multiprocessing to update information on a form in real-time? #98

Open
SHI1992 opened this issue May 28, 2024 · 1 comment
Assignees
Labels
enhancement New feature or request

Comments

@SHI1992
Copy link

SHI1992 commented May 28, 2024

I have tried various methods, and all of them have various issues.

  1. Updating the main thread's form in a child thread, obviously, it failed.
from delphifmx import *
import threading
from pynput.mouse import Listener
import time

class GetMousePosition(threading.Thread):
    def __init__(self, form):
        super().__init__()
        self.form = form
    # mouseListener
    def on_move(self, x, y):
        print(f"Mouse is at: X={x}, Y={y}")
        self.position = (x, y)
        self.form.update_mouse_position(x, y)

    def run(self):
        with Listener(on_move=self.on_move) as listener:
            listener.join()

class MyForm(Form):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.prev_position = None
        self.mouse_thread = None
        self.button = Button(self)
        self.button.Text = 'Start Background Task'
        self.button.OnClick = self.onStartButtonClick
        self.label = Label(self)
        self.label.Text = 'Click the button to start the background task'
        self.button.Parent = self
        self.button.Align = "Top"
        self.label.Parent = self
        self.label.Align = "Top"

    def update_mouse_position(self, x, y):
        self.label.Text = f'Mouse Position: ({x}, {y})'
        
    def onStartButtonClick(self, sender):
        self.mouse_thread = GetMousePosition(self)
        self.mouse_thread.start()

if __name__ == '__main__':
    Application.Initialize()
    Application.Title = "Hello DelphiFMX"
    app = MyForm(Application)
    Application.MainForm = app
    app.Show()
    Application.Run()
    app.Destroy()

2.Return data in the child thread, and the main thread uses onTimer to periodically read and update the main thread's form. It works. However, dragging the main interface causes stuttering, and mouse movement seems to be unsmooth as well.

from delphifmx import *
import threading
from pynput.mouse import Listener
import time

class GetMousePosition(threading.Thread):
    def __init__(self):
        super().__init__()
        self.position = ()
        self.position_history = []

    def on_move(self, x, y):
        print(f"Mouse is at: X={x}, Y={y}")
        self.position = (x, y)

    def run(self):
        with Listener(on_move=self.on_move) as listener:
            listener.join()

class MyForm(Form):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.prev_position = None
        self.mouse_thread = None
        self.button = Button(self)
        self.button.Text = 'Start Background Task'
        self.button.OnClick = self.onStartButtonClick
        self.label = Label(self)
        self.label.Text = 'Click the button to start the background task'
        self.button.Parent = self
        self.button.Align = "Top"
        self.label.Parent = self
        self.label.Align = "Top"

        self.timer = Timer(self)
        self.timer.Interval = 1
        self.timer.OnTimer = self.update_ui

    def update_ui(self, sender):

        if self.mouse_thread:
            result = self.mouse_thread.position
            if result != self.prev_position:
                print("result:" + str(result))
                self.prev_position = result
                self.label.Text = f"Mouse Position: " + str(result)

    def onStartButtonClick(self, sender):
        self.mouse_thread = GetMousePosition()
        self.mouse_thread.start()


if __name__ == '__main__':
    Application.Initialize()
    Application.Title = "Hello DelphiFMX"
    app = MyForm(Application)
    Application.MainForm = app
    app.Show()
    Application.Run()
    app.Destroy()

2024-05-28_153207

  1. Using multiprocessing, data is stored in a queue within the child process, and the main thread uses onTimer to periodically read from and update the main thread's form. It also works. However, if the mouse keeps sliding, it will lead to a lot of data in the queue, and when the mouse stops, the form is still queuing to read data from the queue. It looks like the display of the form is somewhat lagging.
from delphifmx import *
from multiprocessing import Process, Queue
from pynput.mouse import Listener

class GetMousePosition(Process):
    def __init__(self,queue):
        super().__init__()
        self.queue = queue

    def on_move(self, x, y):
        print(f"Mouse is at: X={x}, Y={y}")
        self.queue.put([(x, y)])

    def run(self):
        with Listener(on_move=self.on_move) as listener:
            listener.join()

class MyForm(Form):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.queue = None
        self.mouse_thread = None
        self.prev_position = None  # 用于存储上一次的鼠标位置
        self.button = Button(self)
        self.button.Text = 'Start Background Task'
        self.button.OnClick = self.onStartButtonClick
        self.label = Label(self)
        self.label.Text = 'Click the button to start the background task'
        self.button.Parent = self
        self.button.Align = "Top"
        self.label.Parent = self
        self.label.Align = "Top"

        self.timer = Timer(self)
        self.timer.Interval = 1
        self.timer.OnTimer = self.read_process_output

    def onStartButtonClick(self, sender):
        self.queue = Queue()
        self.mouse_thread = GetMousePosition(self.queue)
        self.mouse_thread.start()

    def read_process_output(self, sender):
        if self.queue:
            result = self.queue.get()
            print(result)

            self.label.Text = f"Mouse Position: " + str(result)


if __name__ == '__main__':
    Application.Initialize()
    Application.Title = "Hello DelphiFMX"
    app = MyForm(Application)
    Application.MainForm = app
    app.Show()
    Application.Run()
    app.Destroy()

Animation2

What is the most correct way to use multithreading or multiprocessing in DelphiFMX4Python? Please let me know, thank you.

@lmbelo lmbelo self-assigned this Aug 20, 2024
@lmbelo lmbelo added the enhancement New feature or request label Aug 20, 2024
@lmbelo
Copy link
Member

lmbelo commented Aug 21, 2024

I didn't understand your example very well. Why are you using a thread to listen for mouse movements? There's an event in FMX forms for that purpose. Look for a "OnMouseMove" event.

If you still want to synch GUI operations: use a queue.Queue instance. In your thread you can queue some data and also put an event in the main thread to check the queue periodically.

I will work on a sample.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants