How to schedule a function to run on the main UI thread?
-
wrote on 11 Mar 2022, 04:15 last edited by relent95 3 Nov 2022, 09:00
I want a worker thread created by the standard threading module to interact with the main UI thread, by scheduling a function on the worker thread, where that function manipulates widgets and will be run on the main UI thread.(Like Gdk.threads_add_idle() in GTK or runOnUiThread() in Android)
There are plenty of articles on QThread and they suggest defining a class with custom signals, customized for each specific problem. But I don't want that pattern. I want a reusable pattern, which separates the worker and UI implementations as much as possible.
I came to a solution like the following, that sends dummy signals to a global dummy QObject.
import sys import time import threading from PySide2.QtCore import * from PySide2.QtWidgets import * class IdleRunner(QObject): run = Signal(object, tuple, float) def __init__(self): super().__init__() self.run.connect(self.on_run) def on_run(self, func, args, delay): if delay: QTimer.singleShot(delay * 1000, lambda: func(*args)) else: func(*args) _idle_runner = IdleRunner() def run_on_idle(func, *args, delay = 0): _idle_runner.run.emit(func, args, delay) class MainWindow(QMainWindow): def __init__(self): super().__init__(windowTitle = sys.argv[0]) self.resize(400, 300) self.setAttribute(Qt.WA_DeleteOnClose, True) self.label = label = QLabel('aaa', alignment = Qt.AlignCenter) self.setCentralWidget(label) def thread_entry(): time.sleep(1) run_on_idle(lambda: self.label.setText('bbb')) run_on_idle(self.close, delay = 1) self.thread = thread = threading.Thread(target = thread_entry) thread.start() def close(self): self.thread.join() super().close() app = QApplication() main_window = MainWindow() main_window.show() app.exec_()
I have two questions.
- What is the best solution to this?(I expects a more elegant one in a Qt way.)
- What are the possible problems of this solution? (Such as a memory leak or a runtime overhead).
-
I want a worker thread created by the standard threading module to interact with the main UI thread, by scheduling a function on the worker thread, where that function manipulates widgets and will be run on the main UI thread.(Like Gdk.threads_add_idle() in GTK or runOnUiThread() in Android)
There are plenty of articles on QThread and they suggest defining a class with custom signals, customized for each specific problem. But I don't want that pattern. I want a reusable pattern, which separates the worker and UI implementations as much as possible.
I came to a solution like the following, that sends dummy signals to a global dummy QObject.
import sys import time import threading from PySide2.QtCore import * from PySide2.QtWidgets import * class IdleRunner(QObject): run = Signal(object, tuple, float) def __init__(self): super().__init__() self.run.connect(self.on_run) def on_run(self, func, args, delay): if delay: QTimer.singleShot(delay * 1000, lambda: func(*args)) else: func(*args) _idle_runner = IdleRunner() def run_on_idle(func, *args, delay = 0): _idle_runner.run.emit(func, args, delay) class MainWindow(QMainWindow): def __init__(self): super().__init__(windowTitle = sys.argv[0]) self.resize(400, 300) self.setAttribute(Qt.WA_DeleteOnClose, True) self.label = label = QLabel('aaa', alignment = Qt.AlignCenter) self.setCentralWidget(label) def thread_entry(): time.sleep(1) run_on_idle(lambda: self.label.setText('bbb')) run_on_idle(self.close, delay = 1) self.thread = thread = threading.Thread(target = thread_entry) thread.start() def close(self): self.thread.join() super().close() app = QApplication() main_window = MainWindow() main_window.show() app.exec_()
I have two questions.
- What is the best solution to this?(I expects a more elegant one in a Qt way.)
- What are the possible problems of this solution? (Such as a memory leak or a runtime overhead).
@relent95 said in How to schedule a function to run on the main UI thread?:
by scheduling a function manipulating widgets inside the worker thread entry
Manipulating UI from different threads than GUI thread is not supported!
Normal way in Qt to communicate between threads are signals/slots. If you want to notify main thread from another thread implement and emit a signal in that other thread. Connect this signal to a slot in main thread. -
@relent95 said in How to schedule a function to run on the main UI thread?:
by scheduling a function manipulating widgets inside the worker thread entry
Manipulating UI from different threads than GUI thread is not supported!
Normal way in Qt to communicate between threads are signals/slots. If you want to notify main thread from another thread implement and emit a signal in that other thread. Connect this signal to a slot in main thread.
1/3