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.3.0.tar.gz (7.3 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.3.0-py3-none-any.whl (7.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: aiowx-0.3.0.tar.gz
  • Upload date:
  • Size: 7.3 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.3.0.tar.gz
Algorithm Hash digest
SHA256 87b16239f5b7ba32fa1f75efadac334cfa0d9a319bed7f7e4f8d31dc054f9ef8
MD5 d4daa1ecc74f771f79289ecaddb4e35d
BLAKE2b-256 777d8a9d8800eb16a574057327e92a0e06d8016b2273bc38e8034240c46a1a02

See more details on using hashes here.

File details

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

File metadata

  • Download URL: aiowx-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 7.7 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.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 242098ae57b10fa0bc1b93e721ea818a4e6723bbf3829e203ac86920cd0ea124
MD5 e6100c8a2c29dea7dfb6fe0833dc106f
BLAKE2b-256 69bebad889f893b16e36badc863acb5bc8240b124097a025af6f23f89d7f50f8

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