Skip to main content

Async I/O bridge for wxPython — run asyncio coroutines with wx GUI

Project description

aiowx

Bridge wxPython with Python asyncio — run async/await coroutines in your wx GUI.

PyPI Python CI

Installation

As a dependency

uv add aiowx
# or with pip:
pip install aiowx

From source

git clone https://github.com/Row0902/aiowx.git
cd aiowx
uv sync          # install deps + dev deps
uv build         # build wheel + source dist

Requires Python 3.12+, wxPython ≥ 4.2.5, and uv.


Quick start

import asyncio
import wx
from aiowx import AsyncBind, WxAsyncApp, StartCoroutine


class CounterFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title="aiowx counter")
        self.label = wx.StaticText(self, label="0")
        btn = wx.Button(self, label="+1")

        AsyncBind(wx.EVT_BUTTON, self.on_click, btn)
        StartCoroutine(self.tick, self)

    async def on_click(self, _event):
        self.label.SetLabel(str(int(self.label.GetLabel()) + 1))

    async def tick(self):
        while True:
            self.label.SetLabel(str(int(self.label.GetLabel()) + 1))
            await asyncio.sleep(1)


async def main():
    app = WxAsyncApp()
    frame = CounterFrame()
    frame.Show()
    app.SetTopWindow(frame)
    await app.MainLoop()


asyncio.run(main())

API

Function Purpose Async
WxAsyncApp() Drop-in replacement for wx.App
await app.MainLoop() Start the event loop
AsyncBind(event, coro, widget) Bind a wx event to a coroutine
StartCoroutine(coro, window) Fire a coroutine attached to a window
await AsyncShowDialog(dlg) Show a dialog modally (modless path)
await AsyncShowDialogModal(dlg) Show an OS dialog safely

WxAsyncApp

Create instead of wx.App. Interleaves the wx event loop with asyncio so both GUI events and coroutines run cooperatively.

app = WxAsyncApp()
await app.MainLoop()

AsyncBind

Bind a wx event to an async callback. Works alongside regular wx.Bind.

AsyncBind(wx.EVT_BUTTON, on_submit, submit_btn)

async def on_submit(event):
    result = await fetch_data()       # non-blocking I/O
    display.SetLabel(f"Got: {result}")

StartCoroutine

Run a coroutine immediately, attached to a wx.Window. It is automatically cancelled when the window is destroyed — no manual cleanup.

task = StartCoroutine(poll_sensor(sensor), panel)
# ...
task.cancel()   # optional early cancel

AsyncShowDialog / AsyncShowDialogModal

Show dialogs without blocking the event loop.

  • AsyncShowDialog: works with custom dialogs (modless path).
  • AsyncShowDialogModal: for OS-native dialogs (wx.FileDialog, wx.DirDialog, etc.). Runs on the wx main thread (thread-safe).
async def pick_file(parent):
    dlg = wx.FileDialog(parent)
    result = await AsyncShowDialogModal(dlg)
    if result == wx.ID_OK:
        path.SetLabel(dlg.GetPath())

Note: the asyncio event loop is blocked while the modal dialog is open. This is expected desktop GUI behavior — identical to how ShowModal works in a traditional wx app.


Patterns

Background task with cancellation

import asyncio
from aiowx import StartCoroutine


class MonitorFrame(wx.Frame):
    def __init__(self):
        super().__init__(None)
        self.running = True
        self.task = StartCoroutine(self.watchdog, self)

    async def watchdog(self):
        while self.running:
            await asyncio.sleep(5)
            if not self.check_health():
                wx.MessageBox("Connection lost")
                break

Dialog with await

async def save_file(parent):
    dlg = wx.FileDialog(
        parent,
        style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
    )
    if await AsyncShowDialogModal(dlg) == wx.ID_OK:
        with open(dlg.GetPath(), "w") as f:
            f.write(data)

Performance

Values measured on Windows (Core i7-7700K @ 4.2 GHz):

Scenario Latency Latency at max throughput Max throughput
asyncio only (reference) 0 ms 17 ms 571 325 msg/s
wx only (reference) 0 ms 19 ms 94 591 msg/s
aiowx (GUI) 5 ms 19 ms 52 304 msg/s
aiowx (GUI + asyncio) 5 ms / 0 ms 24 ms / 12 ms 40 302 + 134 000 msg/s

CPU usage at idle: 0% on Windows, ~1–2% on macOS.


Requirements

  • Python ≥ 3.12
  • wxPython ≥ 4.2.5 (4.2.5+ includes Python 3.14 wheels)

Repository

https://github.com/Row0902/aiowx

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aiowx-0.2.1.tar.gz (6.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aiowx-0.2.1-py3-none-any.whl (6.9 kB view details)

Uploaded Python 3

File details

Details for the file aiowx-0.2.1.tar.gz.

File metadata

  • Download URL: aiowx-0.2.1.tar.gz
  • Upload date:
  • Size: 6.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.23 {"installer":{"name":"uv","version":"0.11.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for aiowx-0.2.1.tar.gz
Algorithm Hash digest
SHA256 f51665174b869847062bf2eb28d5dd2aeff6f3e36fa9e81b77ba0d2d28994307
MD5 a1fb0cadd31c075152b4958868652142
BLAKE2b-256 e9b19ec1c175575bd2849edbf3ff09f78ff59711eaedfdd2380e394f3767304b

See more details on using hashes here.

File details

Details for the file aiowx-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: aiowx-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 6.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.23 {"installer":{"name":"uv","version":"0.11.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for aiowx-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 91526520c5994c598867698784a3e8b6ab06e97fdb437c96131848df3fc53280
MD5 941c672bdb5ad4a16e223e955974dc92
BLAKE2b-256 363283fd8c394c4c8289e3c73a2b41ea0be058a274eeb8a0e14cd9dbfd3dbf4b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page