Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

PyWinUI

Build native WinUI 3 (Windows App SDK) desktop applications in idiomatic Python.

Not a themed look-alike and not a reimplementation — these are real WinUI 3 controls, wrapped so that you never have to touch WinRT unless you want to.

import pywinui as ui


class CounterApp(ui.App):
    def build(self):
        count = ui.TextBlock("0", font_size=32)

        def bump(sender, args):
            count.text = str(int(count.text) + 1)

        return ui.Window(
            title="PyWinUI Counter",
            content=ui.StackPanel(
                spacing=12, padding=24,
                children=[
                    ui.TextBlock("Counter", font_size=20),
                    count,
                    ui.Button("Increment", on_click=bump),
                ],
            ),
        )


CounterApp().run()

Status: early alpha. The architecture is verified end-to-end against the real bindings on Windows 11, but only five controls are wrapped so far. The API may change. See Current scope before depending on it.

Why now

The WinUI 3 projections for Python (PyWinRT) only landed in March 2025. Before that, a Python WinUI 3 app meant hand-rolling raw WinRT. The bindings now exist and work; PyWinUI is the ergonomics layer on top of them.

Requirements

  • Windows 10/11 with the Windows App Runtime
  • python.org Python 3.10+not the Microsoft Store build, which is a packaged app and fails the runtime bootstrap with ERROR_NOT_SUPPORTED

Install

pip install pywinui

The winui3-* dependencies are Windows-only and install automatically there. On other platforms the package still installs and imports (the native layer is loaded lazily), so the test suite and editor tooling work anywhere — but anything that realizes a control needs Windows.

Two ways to build a tree

Both are first-class and they compose.

# Flutter style — the tree is a value. Best for reusable components.
ui.StackPanel(children=[ui.TextBlock("a"), ui.TextBlock("b")])

# With style — handles loops, conditionals and local references far better.
with ui.StackPanel() as panel:
    ui.TextBlock("Items")
    for name in items:
        ui.Button(name, on_click=make_handler(name))

Async without the ceremony

Handlers may be async def. They're scheduled off the UI thread automatically, WinRT async operations are awaited like any coroutine, and writes back to widgets are marshalled onto the UI thread for you.

async def read_file(sender, args):
    status.text = "Reading..."
    file = await StorageFile.get_file_from_path_async(path)
    text = await FileIO.read_text_async(file)      # real WinRT I/O
    status.text = f"{len(text)} chars"             # marshalled back to the UI

Failures arrive as ordinary Python exceptions — a missing path raises FileNotFoundError.

The escape hatch

The curated surface covers the common path with type hints, validation and value conversions. Anything not wrapped still forwards by name, and widget.native gives you the raw WinUI control:

btn = ui.Button("Save")
btn.native.background = some_brush    # raw WinUI, fully supported

Current scope

Working Not yet
TextBlock, TextBox, Button, StackPanel, Grid, Window The other ~100 WinUI controls
Both tree-building styles, attach/detach Data binding, styles, resource dictionaries
async def handlers, awaiting WinRT ops Control templates, virtualized lists
Off-thread writes auto-marshalled Off-thread reads (wrap in run_on_ui)
padding/margin, visible, content boxing Grid.Row/Grid.Column attached properties
Running from a Python environment Packaging a double-clickable app (MSIX)

Packaging is unproven. Shipping an app to end users means bundling Python and the Windows App Runtime, likely as MSIX, and that path has not been prototyped. Today this is a library for developers who already have Python installed.

Troubleshooting

DLL load failed ... The filename or extension is too long — your virtual environment path is too deep. The winui3 extension modules have very long file names (_winui3_microsoft_windows_applicationmodel_dynamicdependency_bootstrap), and a nested venv can push them past Windows' 260-character MAX_PATH. Use a shorter path, or enable long paths.

ERROR_NOT_SUPPORTED during bootstrap — you're on the Microsoft Store build of Python, which is itself a packaged app. Use the python.org installer.

Examples

pip install -e ".[examples,dev]"
python examples/counter.py
python examples/async_file_read.py

Tests

pytest

The suite runs anywhere — no Windows required. It swaps in a fake native layer to lock down the tree model, both building styles, attach/detach, property forwarding, value conversions and thread marshalling. What it deliberately cannot cover is the binding boundary itself; that is verified by running the examples on Windows.

License

MIT

Download files

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

Source Distribution

pywinui-0.1.0a1.tar.gz (21.8 kB view details)

Uploaded Source

Built Distribution

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

pywinui-0.1.0a1-py3-none-any.whl (13.1 kB view details)

Uploaded Python 3

File details

Details for the file pywinui-0.1.0a1.tar.gz.

File metadata

  • Download URL: pywinui-0.1.0a1.tar.gz
  • Upload date:
  • Size: 21.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for pywinui-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 8fd33c5e17a9dd44e6c22eebca6343d5b818fc67c7b98687891085673baca95f
MD5 3371702c0421fdc57b34590973fef792
BLAKE2b-256 e353e7201b0911f5e5d650ea92e275589f662cdd67e2fc1e41bbe7987260db24

See more details on using hashes here.

File details

Details for the file pywinui-0.1.0a1-py3-none-any.whl.

File metadata

  • Download URL: pywinui-0.1.0a1-py3-none-any.whl
  • Upload date:
  • Size: 13.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for pywinui-0.1.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 6d87ca6a574f13aa87a9eeb8a02365b0783af5480bc965f25a5d2c62e719254f
MD5 479ff8039bc3960788dbda144651bd21
BLAKE2b-256 32d1c0709b6ebb8261ffb6c1082239963cdf2070dfe8cf646f187283e91bfb49

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