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.0.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.0-py3-none-any.whl (6.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: aiowx-0.2.0.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.0.tar.gz
Algorithm Hash digest
SHA256 b18d97e9233ea69587aceb3f753b1106b59cf43efe461cde42e87d4113183ce6
MD5 6ed7730962d3328523afe6e1013311f8
BLAKE2b-256 e494ddbe8a565fc8fee36df8f53825b4cd8d4dcf6e4c6da8909cf359968b72f0

See more details on using hashes here.

File details

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

File metadata

  • Download URL: aiowx-0.2.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ff8509f3db0b9e271a2697ca6525cbdf87306f97f97e8ee0d3b39f4e626955f8
MD5 86473874c786b5643dc5b5e534409ec0
BLAKE2b-256 7609fb3683ba5a286493c72262c8f6a5a8265953a19919802c03be1794045bd3

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