Python GUIs: Using QThr
March 29, 2026
Planet Python
eadPool.start() with a Simple Function in PySide6 Subtitle: Run background tasks without creating a QRunnable by passing a callable directly to QThreadPool Date: 2021-07-06 09:00 Authors: Martin Fitzman Patrick Description: Learn how to use QThreadPool.start() with a plain Python function in PySide6, avoiding the need to subclass QRunnable for simple background tasks.
SeoTitle: PySide6 QThreadPool start with function - simple threading without QRunnable Tags: PySide6,QThreadPool,Threading,Concurrency,Python SourceCode: In PyQt6, QThreadPool.start() accepts either a QRunnable or a plain Python callable. Does PySide6 support passing a callable directly to QThreadPool.start(), so you don't have to create a QRunnable subclass for simple background tasks? Good news — PySide6 now supports passing a plain Python function (or any callable) directly to QThreadPool.start(). This means you can run background tasks without the extra boilerplate of subclassing QRunnable. Let's walk through how it works and when you might still want to use QRunnable. QThreadPool.start() Signatures QThreadPool.start() accepts two types of argument: A QRunnable instance — the traditional approach where you subclass QRunnable and implement the run() method. A Python callable — any function, method, or callable object with no arguments. Both signatures also accept an optional priority parameter (default 0), which controls the order in which tasks are picked up from the pool's queue. python Signature 1: QRunnable QThreadPool.globalInstance().start(my_runnable, priority=0) Signature 2: Callable QThreadPool.globalInstance().start(my_function, priority=0) The callable signature is perfect for simple, fire-and-forget background work where you don't need the full structure of a QRunnable subclass. Passing a Function to QThreadPool.start() Here's a minimal example that runs a function in the background using QThreadPool: python import sys import time from PySide6.QtCore import QThreadPool from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget def long_running_task(): Simulate a task that takes a few seconds. print(Task started) time.sleep(3) print(Task finished) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowName(QThreadPool Function Demo) self.thread_pool = QThreadPool.globalInstance() print(fMax thread count: {self.thread_pool.maxThreadCount()}) button = QPushButton(Run background task) button.clicked.connect(self.start_task) layout = QVBoxLayout() layout.addWidget(button) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) def start_task(self): self.thread_pool.start(long_running_task) app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec()) Click the button, and long_running_task runs on a background thread — no QRunnable needed. The UI stays responsive while the task sleeps for three seconds. Using a Lambda or Method Because start() accepts any callable with no arguments, you can also pass a lambda or a bound method: python Using a lambda self.thread_pool.start(lambda: print(Hello from a thread!)) Using a bound method self.thread_pool.start(self.do_work) If your function needs arguments, wrap it in a lambda: python def greet(name): print(fHello, {name}!) self.thread_pool.start(lambda: greet(World)) When to Still Use QRunnable Passing a plain function is convenient, but there are situations where subclassing QRunnable is the better choice: You need to emit signals — QRunnable doesn't inherit from QObject, but you can attach a signals object to it and emit signals to send progress updates or results back to the main thread. You want to manage task lifecycle — QRunnable has setAutoDelete() and other methods that give you finer control. You're reusing the same task pattern — wrapping the logic in a QRunnable subclass keeps things organized when you have multiple task types. For a full walkthrough of using QRunnable with QThreadPool including signal handling and progress reporting, see the Multithreading PySide6 applications with QThreadPool tutorial. Here's a quick comparison to illustrate: python from PySide6.QtCore import QRunnable, QThreadPool class MyTask(QRunnable): def run(self): print(Running via QRunnable) Using QRunnable pool = QThreadPool.globalInstance() pool.start(MyTask()) Using a plain function — same result, less code pool.start(lambda: print(Running via callable)) Complete Working Example Here's a more complete example that demonstrates running multiple background tasks by passing functions to QThreadPool.start(). Each click queues a new task, and you can see them executing concurrently: python import sys import time from PySide6.QtCore import QThreadPool from PySide6.QtWidgets import ( QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget, ) def background_task(task_id): Simulate work with a unique task ID. print(fTask {task_id}: started) time.sleep(3) print(fTask {task_id}: finished) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(QThreadPool Callable Demo) self.resize(300, 150) self.thread_pool = QThreadPool.globalInstance() self.task_counter = 0 self.label = QLabel(Click the button to queue tasks) self.label.setStyleSheet(padding: 10px;) button = QPushButton(Queue background task) button.clicked.connect(self.queue_task) layout = QVBoxLayout() layout.addWidget(self.label) layout.addWidget(button) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) def queue_task(self): self.task_counter += 1 task_id = self.task_counter self.label.setText(fQueued task {task_id}) Pass a callable directly — no QRunnable subclass needed self.thread_pool.start(lambda tid=task_id: background_task(tid)) app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec()) Notice the tid=task_id default argument in the lambda. This captures the current value of task_id at the time the lambda is created, rather than sharing a reference to the variable that keeps changing as you click. This is a common Python pattern when creating closures in a loop or repeated callback. Run this example, click the button a few times in quick succession, and watch the terminal output — you'll see tasks starting and finishing concurrently, all managed by the thread pool, and all without writing a single QRunnable subclass. If you're building a full PySide6 application, you may also want to set up your window with layouts and widgets to provide a richer interface around your background tasks. For an in-depth guide to building Python GUIs with PySide6 see my book, Create GUI Applications with Python Qt6.
Planet Python
Coverage and analysis from United States of America. All insights are generated by our AI narrative analysis engine.