Skip to main content

Qt Thread Updater

Python Qt thread updater to update GUI items using a separate thread.

This library allows you to efficiently update Qt GUI elements from a separate thread. Qt GUI elements are not thread safe. Method calls like Label.setText do not work in a separate thread. This library solves that problem.

Utilities

The ThreadUpdater offers several utilities to help with updating a widget’s value.

  • call_latest - Call the given function with the most recent value in the main thread using the timer.

    • It is safe to call this many times with the same function.

    • If the given function is called multiple times it is only called once with the most recent value.

  • call_in_main - Call the given function in the main thread using the timer.

    • Every time you call this function the given function will be called in the main thread

    • If the given function is called multiple times it will be called every time in the main thread.

    • If this function is called too many times it could slow down the main event loop.

  • register_continuous - Register a function to be called every time the ThreadUpdater.update method is called.

    • The timeout variable (in seconds) indicates how often the registered functions will be called.

  • delay - Call a function after the given number of seconds has passed.

    • This will not be accurate. Accuracy can be improved by lowering the timeout to increase how often the timer runs.

ThreadUpdater Examples

Below are some examples of how the ThreadUpdater would normally be used.

Simple Thread Example

The example below tells the update to run lbl.setText in the main thread with the latest value.

import time
import threading
from qtpy import QtWidgets
from qt_thread_updater import get_updater

app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])

lbl = QtWidgets.QLabel("Latest Count: 0")
lbl.resize(300, 300)
lbl.show()

data = {'counter': 0}

def run(is_alive):
    is_alive.set()
    while is_alive.is_set():
        text = 'Latest Count: {}'.format(data['counter'])
        get_updater().call_latest(lbl.setText, text)
        data['counter'] += 1
        time.sleep(0.001)  # Not needed (still good to have some delay to release the thread)

alive = threading.Event()
th = threading.Thread(target=run, args=(alive,))
th.start()

app.exec_()
alive.clear()

Continuous Update Example

The example below continuously runs the update function every time ThreadUpdater.update() is called from the timer. This may be inefficient if there is no new data to update the label with.

import time
import threading
from qtpy import QtWidgets
import qt_thread_updater
# from qt_thread_updater import get_updater

app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])

lbl = QtWidgets.QLabel("Continuous Count: 0")
lbl.resize(300, 300)
lbl.show()

data = {'counter': 0}

qt_thread_updater.set_updater(qt_thread_updater.ThreadUpdater(1/60))

@qt_thread_updater.register_continuous
def update():
    """Update the label with the current value."""
    lbl.setText('Continuous Count: {}'.format(data['counter']))

# get_updater().register_continuous(update)

def run(is_alive):
    is_alive.set()
    while is_alive.is_set():
        data['counter'] += 1
        # time.sleep(0.001)  # Not needed (still good to have some delay to release the thread)

alive = threading.Event()
th = threading.Thread(target=run, args=(alive,))
th.start()

qt_thread_updater.delay(5, app.quit)  # Quit after 5 seconds
app.exec_()
alive.clear()
cleanup_app()

Call In Main Example

The example below calls the append function every time. It may not be efficient.

import time
import threading
from qtpy import QtWidgets
from qt_thread_updater import get_updater

app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])

text_edit = QtWidgets.QTextEdit()
text_edit.resize(300, 300)
text_edit.setReadOnly(True)
text_edit.show()

data = {'counter': 0}

def run(is_alive):
    is_alive.set()
    while is_alive.is_set():
        text = 'Main Count: {}'.format(data['counter'])
        get_updater().call_in_main(text_edit.append, text)
        data['counter'] += 1
        time.sleep(0.01)  # Some delay/waiting is required

alive = threading.Event()
th = threading.Thread(target=run, args=(alive,))
th.start()

app.exec_()
alive.clear()

Delay Example

The example below calls the append function after X number of seconds has passed. The delay function will not be accurate, but guarantees that the function is called after X number of seconds. To increase accuracy give the ThreadUpdater a smaller timeout for it to run at a faster rate.

import time
import threading
from qtpy import QtWidgets
from qt_thread_updater import get_updater

app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])

text_edit = QtWidgets.QTextEdit()
text_edit.resize(300, 300)
text_edit.setReadOnly(True)
text_edit.show()

now = time.time()

def update_text(set_time):
    text_edit.append('Requested {:.04f} Updated {:.04f}'.format(set_time, time.time() - now))

# Lower the timeout so it runs at a faster rate.
get_updater().timeout = 0  # 0.0001  # Qt runs in milliseconds

get_updater().delay(0.5, update_text, 0.5)
get_updater().delay(1, update_text, 1)
get_updater().delay(1.5, update_text, 1.5)
get_updater().delay(2, update_text, 2)
get_updater().delay(2.5, update_text, 2.5)
get_updater().delay(3, update_text, 3)

app.exec_()

Widgets

I’ve decdied to include a couple of useful Qt Widgets with this library.

  • QuickPlainTextEdit - Used to display fast streams of data

  • QuickTextEdit - Display fast streams of data with color.

QuickPlainTextEdit

Quickly display data from a separate thread.

import time
import threading
from qtpy import QtWidgets
from qt_thread_updater.widgets.quick_text_edit import QuickPlainTextEdit

app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])

text_edit = QuickPlainTextEdit()
text_edit.resize(300, 300)
text_edit.show()

data = {'counter': 0}

def run(is_alive):
    is_alive.set()
    while is_alive.is_set():
        text = 'Main Count: {}\n'.format(data['counter'])
        text_edit.write(text)
        data['counter'] += 1
        time.sleep(0.0001)  # Some delay is usually required to let the Qt event loop run (not needed if IO used)

alive = threading.Event()
th = threading.Thread(target=run, args=(alive,))
th.start()

app.exec_()
alive.clear()

QuickTextEdit

Quickly display data from a separate thread using color.

import time
import threading
from qtpy import QtWidgets
from qt_thread_updater.widgets.quick_text_edit import QuickTextEdit

app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])

text_edit = QuickTextEdit()
text_edit.resize(300, 300)
text_edit.show()

data = {'counter': 0}

def run(is_alive):
    is_alive.set()
    while is_alive.is_set():
        text = 'Main Count: {}\n'.format(data['counter'])
        text_edit.write(text, 'blue')
        data['counter'] += 1
        time.sleep(0.0001)  # Some delay is usually required to let the Qt event loop run (not needed if IO used)

alive = threading.Event()
th = threading.Thread(target=run, args=(alive,))
th.start()

app.exec_()
alive.clear()

QuickTextEdit Redirect

Display print (stdout and stderr) in a QTextEdit with color.

import sys
import time
import threading
from qtpy import QtWidgets
from qt_thread_updater.widgets.quick_text_edit import QuickTextEdit

app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])

text_edit = QuickTextEdit()
text_edit.resize(300, 300)
text_edit.show()

sys.stdout = text_edit.redirect(sys.__stdout__, color='blue')
sys.stderr = text_edit.redirect(sys.__stderr__, color='red')

data = {'counter': 0}

def run(is_alive):
    is_alive.set()
    while is_alive.is_set():
        stdout_text = 'Main Count: {}'.format(data['counter'])  # Print gives \n automatically
        error_text = 'Error Count: {}'.format(data['counter'])  # Print gives \n automatically

        # Print automatically give '\n' with the "end" keyword argument.
        print(stdout_text)  # Print will write to sys.stdout where the rediect will write to text_edit and stdout
        print(error_text, file=sys.stderr)  # Print to sys.stderr. Rediect will write to text_edit and stderr

        data['counter'] += 1

        # Some delay is usually desired. print/sys.__stdout__ uses IO which gives time for Qt's event loop.
        # time.sleep(0.0001)

alive = threading.Event()
th = threading.Thread(target=run, args=(alive,))
th.start()

app.exec_()
alive.clear()

Metadata

Release files for qt-thread-updater 1.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for qt-thread-updater 1.1.6
File Size Uploaded
qt_thread_updater-1.1.6.tar.gz 14.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for qt-thread-updater 1.1.6
File Interpreter ABI Platform
qt_thread_updater-1.1.6-py3-none-any.whl Python 3 none any Details

Total release size: 55.1 kB

Release files / qt_thread_updater-1.1.6.tar.gz

Download URL qt_thread_updater-1.1.6.tar.gz
Size 14.7 kB
Tags Source
SHA-256 checksum
How to use checksums
57870272d1a75b31f34396b433811589feecd8baba152641fabb75ecb6727fb9
BLAKE2b-256 checksum
How to use checksums
823cda9c327ef5eb17f21fb463e46672bbb8c86ceaf3447b584dbaf565a13fdb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/47.1.1 requests-toolbelt/0.9.1 tqdm/4.46.1 CPython/3.8.8

Release files / qt_thread_updater-1.1.6-py3-none-any.whl

Download URL qt_thread_updater-1.1.6-py3-none-any.whl
Size 40.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
365aa902928617f8ef5ba7bf9e2299201bab2a271b36808d525bb1cd620fda04
BLAKE2b-256 checksum
How to use checksums
e561394f954c577a26c564e24ee387a21fd96a222ee4d8d0443c94d79a4a10a9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/47.1.1 requests-toolbelt/0.9.1 tqdm/4.46.1 CPython/3.8.8

Release history Release notifications | RSS feed

This release

1.1.6 This release

2 release files

1.1.5

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

1 release file

1.0.0

1 release file

0.0.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page