pywinui 0.1.0a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pywinui-0.1.0a1/.gitattributes +2 -0
- pywinui-0.1.0a1/.github/workflows/tests.yml +41 -0
- pywinui-0.1.0a1/.gitignore +19 -0
- pywinui-0.1.0a1/AGENTS.md +283 -0
- pywinui-0.1.0a1/LICENSE +21 -0
- pywinui-0.1.0a1/PKG-INFO +211 -0
- pywinui-0.1.0a1/README.md +154 -0
- pywinui-0.1.0a1/examples/async_file_read.py +52 -0
- pywinui-0.1.0a1/examples/counter.py +34 -0
- pywinui-0.1.0a1/pyproject.toml +59 -0
- pywinui-0.1.0a1/src/pywinui/__init__.py +553 -0
- pywinui-0.1.0a1/tests/test_pywinui.py +377 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
# The suite is designed to run without Windows or the winui3 wheels, so we
|
|
11
|
+
# prove that claim on Linux as well as running it where the library ships.
|
|
12
|
+
test:
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
os: [windows-latest, ubuntu-latest]
|
|
17
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
18
|
+
runs-on: ${{ matrix.os }}
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
- run: python -m pip install --upgrade pip
|
|
25
|
+
- run: pip install -e ".[dev]"
|
|
26
|
+
- run: pytest -v
|
|
27
|
+
|
|
28
|
+
# Realizing native controls needs a UI thread and the Windows App Runtime,
|
|
29
|
+
# which GitHub's runners don't provide, so the examples are NOT run here.
|
|
30
|
+
# Run them manually on a real Windows box after touching _Native — see
|
|
31
|
+
# AGENTS.md section 5.1.
|
|
32
|
+
build:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
- uses: actions/setup-python@v5
|
|
37
|
+
with:
|
|
38
|
+
python-version: "3.13"
|
|
39
|
+
- run: pip install build twine
|
|
40
|
+
- run: python -m build
|
|
41
|
+
- run: twine check dist/*
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# PyWinUI — Agent Briefing
|
|
2
|
+
|
|
3
|
+
You are picking up **PyWinUI**, a Pythonic wrapper library that lets developers
|
|
4
|
+
build **native WinUI 3 (Windows App SDK) desktop applications in Python**. This
|
|
5
|
+
document explains the goal, the design decisions already made (and *why*), the
|
|
6
|
+
current state of the code, the constraints you must respect, and what remains.
|
|
7
|
+
Read it fully before changing anything.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. The goal
|
|
12
|
+
|
|
13
|
+
Let someone write a modern Windows 11 desktop app — real native WinUI 3 controls,
|
|
14
|
+
not a themed look-alike — using ordinary, idiomatic Python.
|
|
15
|
+
|
|
16
|
+
The guiding principle: **a user should not know they are using WinUI/WinRT
|
|
17
|
+
underneath, except when they reach for advanced features.** The common path feels
|
|
18
|
+
like a normal Python UI library (Flet/Flutter/nicegui-flavored). The WinRT
|
|
19
|
+
machinery only surfaces when someone deliberately goes past the curated surface.
|
|
20
|
+
|
|
21
|
+
This is a wrapper, not a reimplementation. Underneath sit the **PyWinRT `winui3`
|
|
22
|
+
projection packages** (auto-generated Python bindings for the Windows App SDK,
|
|
23
|
+
e.g. `winui3-Microsoft.UI.Xaml`). We wrap those to make them pleasant.
|
|
24
|
+
|
|
25
|
+
### Why this is feasible now (and wasn't before)
|
|
26
|
+
The `winui3` namespace projections were only added to PyWinRT around **March
|
|
27
|
+
2025**. An earlier same-named attempt (github.com/bornacheck/PyWinUI) was archived
|
|
28
|
+
in **May 2023** with essentially no code — at that time no WinUI projections
|
|
29
|
+
existed, so it would have meant hand-rolling raw WinRT. That foundation now exists;
|
|
30
|
+
the remaining work is the ergonomics layer, which is what this project is.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. Non-negotiable design principles
|
|
35
|
+
|
|
36
|
+
These are the spine of the project. Preserve them unless you have a strong,
|
|
37
|
+
explicit reason and you update this document.
|
|
38
|
+
|
|
39
|
+
### 2.1 Isolate all native calls behind one glue layer
|
|
40
|
+
Every `winui3` import and every WinRT-specific call lives in the `_Native` class
|
|
41
|
+
(and a couple of clearly marked helpers). The rest of the library is pure Python
|
|
42
|
+
and never mentions WinRT. This is what makes the code testable off-Windows and
|
|
43
|
+
lets us fix binding changes in one place.
|
|
44
|
+
|
|
45
|
+
### 2.2 The `# VERIFY` convention
|
|
46
|
+
Any line that mirrors the C#/WinRT object model but has **not** been confirmed
|
|
47
|
+
against the real packages on Windows is tagged `# VERIFY`. These are the calls an
|
|
48
|
+
agent on a Windows machine must validate. Do not silently remove the tags; remove
|
|
49
|
+
one only after confirming that specific call works, and say so in the commit.
|
|
50
|
+
|
|
51
|
+
### 2.3 Declaration is separate from realization
|
|
52
|
+
Constructing widgets builds a cheap pure-Python tree. A later `_realize()` pass
|
|
53
|
+
walks that tree and creates the native controls. Native objects can only be
|
|
54
|
+
created after the runtime is bootstrapped and on the UI thread, so this split is
|
|
55
|
+
deliberate — keep it. Never create native controls in `__init__`.
|
|
56
|
+
|
|
57
|
+
### 2.4 Curated surface + escape hatch
|
|
58
|
+
Common properties/events get explicit, Pythonic, validated wrappers. Anything not
|
|
59
|
+
wrapped still works via attribute forwarding, and `widget.native` is the blessed
|
|
60
|
+
way to reach the raw WinUI control for advanced use. The curated layer is where
|
|
61
|
+
type hints, validation, and value conversions live; forwarding covers the long
|
|
62
|
+
tail. Keep both tiers.
|
|
63
|
+
|
|
64
|
+
### 2.5 Two tree-building styles, both first-class
|
|
65
|
+
- **Flutter style:** `Panel(children=[A(), B()])` — the tree is a value.
|
|
66
|
+
- **With style:** `with Panel(): A(); B()` — widgets constructed inside a block
|
|
67
|
+
auto-attach to it (nicegui-style, via a `contextvars` parent stack).
|
|
68
|
+
|
|
69
|
+
They compose (a panel can be seeded with `children=[...]` and extended inside a
|
|
70
|
+
`with`). **With-style is the idiomatic default** (it handles loops, conditionals,
|
|
71
|
+
and local child references far better); **Flutter style is preferred for reusable
|
|
72
|
+
components**, where a function returning a widget is referentially transparent.
|
|
73
|
+
Do not remove either.
|
|
74
|
+
|
|
75
|
+
### 2.6 Structure vs rendering are different axes
|
|
76
|
+
- **Attachment** (structural: "is this widget in the tree?") is a one-time,
|
|
77
|
+
construct-time decision. Controlled by auto-parenting, `attached=False`,
|
|
78
|
+
`detached()`, and manual `panel.add(...)`.
|
|
79
|
+
- **Visibility** (rendering: "is this widget shown?") is a live property,
|
|
80
|
+
`visible: bool`, mapped to WinUI's `Visibility` enum.
|
|
81
|
+
|
|
82
|
+
Never conflate them. Critically: **WinUI enforces one parent per element** —
|
|
83
|
+
adding an already-parented control elsewhere throws. So "hide it and move it
|
|
84
|
+
later" does not work; you must detach/re-add. This constraint is why `attached`
|
|
85
|
+
and `visible` are separate and must stay separate.
|
|
86
|
+
|
|
87
|
+
### 2.7 Pythonic naming, curated conversions
|
|
88
|
+
PyWinRT already projects properties as `snake_case`, so forwarding is mostly a
|
|
89
|
+
`setattr`. A small number of properties need conversion; these live in one place
|
|
90
|
+
(a `_Native` helper + a branch in `_set_native`). Current examples:
|
|
91
|
+
`padding`/`margin` → `Thickness`, `visible` → `Visibility`. New conversions
|
|
92
|
+
(`enabled`, `opacity`, colors, alignment enums) follow the identical pattern.
|
|
93
|
+
|
|
94
|
+
### 2.8 Threads and async are hidden
|
|
95
|
+
WinUI has one UI thread; touching a control from another thread throws, and WinRT
|
|
96
|
+
I/O returns `IAsyncOperation` objects. The library hides this:
|
|
97
|
+
- Event handlers may be `async def`; they are auto-scheduled.
|
|
98
|
+
- Widget writes made off the UI thread are auto-marshalled onto it.
|
|
99
|
+
- WinRT async ops are awaited directly — **PyWinRT 3.2 projects `__await__`**, so
|
|
100
|
+
`await picker.pick_single_file_async()` just works and WinRT failures arrive as
|
|
101
|
+
ordinary Python exceptions (a missing path raises `FileNotFoundError`).
|
|
102
|
+
`as_future(op)` is now a thin `ensure_future` wrapper, kept for when you need a
|
|
103
|
+
real Future (`gather`, `wait_for`, cancellation) — not a hand-rolled bridge.
|
|
104
|
+
- `run_on_ui(fn)` is the explicit escape hatch.
|
|
105
|
+
|
|
106
|
+
Current architecture (v1, simple + robust): asyncio runs on a **separate daemon
|
|
107
|
+
thread** because `Application.start` owns the UI thread's message pump; UI
|
|
108
|
+
mutations hop back via the `DispatcherQueue`. Known limitation: only *writes* are
|
|
109
|
+
auto-marshalled, not *reads*.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 3. Current state
|
|
114
|
+
|
|
115
|
+
Files:
|
|
116
|
+
|
|
117
|
+
| File | What it is |
|
|
118
|
+
|------|-----------|
|
|
119
|
+
| `src/pywinui/__init__.py` | The library. Sole source file. |
|
|
120
|
+
| `examples/counter.py` | Minimal counter app — runs for real on Windows. |
|
|
121
|
+
| `examples/async_file_read.py` | Async handler awaiting real WinRT I/O — also runs. |
|
|
122
|
+
| `tests/test_pywinui.py` | Pytest suite — 31 tests, all passing. |
|
|
123
|
+
| `pyproject.toml` | Packaging + the full winui3 dependency list. |
|
|
124
|
+
| `README.md` / `LICENSE` | Public-facing docs; MIT. |
|
|
125
|
+
| `AGENTS.md` | This document (project root). |
|
|
126
|
+
|
|
127
|
+
It's a package directory holding one module so that splitting `_Native` or the
|
|
128
|
+
controls into submodules later doesn't change the import path or the layout.
|
|
129
|
+
|
|
130
|
+
Both examples have been executed against the real bindings, not just written.
|
|
131
|
+
|
|
132
|
+
What exists in `pywinui.py`: the `_Native` glue layer; a `Widget` base with
|
|
133
|
+
lazy realization, attribute forwarding, the `.native` escape hatch, and the
|
|
134
|
+
container/context-manager protocol; controls `TextBlock`, `TextBox`, `Button`,
|
|
135
|
+
`StackPanel`, `Grid`; `Window`; an `App` lifecycle with bootstrap + background
|
|
136
|
+
asyncio loop; and the dispatcher/async bridge (`run_on_ui`, `as_future`,
|
|
137
|
+
`detached`).
|
|
138
|
+
|
|
139
|
+
### What the tests cover vs. don't
|
|
140
|
+
The suite runs **anywhere** (no Windows needed) by swapping a fake native layer
|
|
141
|
+
in via a fixture. It locks in: both tree-building styles and their composition,
|
|
142
|
+
nesting and context cleanup (including on exceptions), the Window single-content
|
|
143
|
+
slot and leaf rejection, attach/detach/`add`, property queueing and forwarding,
|
|
144
|
+
the `padding→Thickness`, `visible→Visibility` and `content→boxed` conversions,
|
|
145
|
+
`as_future` adaptation and error propagation, and async/thread marshalling in
|
|
146
|
+
both directions.
|
|
147
|
+
|
|
148
|
+
The tests still **cannot** cover the native calls themselves — the fake layer
|
|
149
|
+
mirrors the binding contract, it doesn't prove it. That split is correct, but it
|
|
150
|
+
does mean the fake must be kept honest: when a real binding turns out to behave
|
|
151
|
+
differently (as boxing did), update `FakeNative` to match, or the suite will
|
|
152
|
+
happily keep certifying the wrong contract. Re-run the two examples on Windows
|
|
153
|
+
after any change to `_Native`.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 4. Hard constraints (read before running or editing)
|
|
158
|
+
|
|
159
|
+
1. **Native code only runs on Windows** with the **Windows App Runtime**
|
|
160
|
+
installed. The library imports `winui3` lazily so it can be imported (and
|
|
161
|
+
tested) elsewhere, but anything that realizes controls needs Windows.
|
|
162
|
+
2. **Use python.org Python, not the Microsoft Store build.** The Store build is a
|
|
163
|
+
packaged app and fails the runtime bootstrap with `ERROR_NOT_SUPPORTED`.
|
|
164
|
+
3. **Import paths (resolved).** `winui3.microsoft.ui.xaml` is correct — the docs'
|
|
165
|
+
`winui3.microsoft.windows.ui.xaml` was a typo. **Every WinRT namespace is a
|
|
166
|
+
separate wheel**, and parents do not pull in children: `...Xaml.Controls` and
|
|
167
|
+
`...DynamicDependency.Bootstrap` must be installed explicitly, and
|
|
168
|
+
`winrt-Windows.Foundation` is required for value boxing and event delegates.
|
|
169
|
+
The full list is in the install hint inside `_Native.load()`.
|
|
170
|
+
4. **Keep the pure-Python / native split.** Do not scatter `winui3` imports
|
|
171
|
+
through the codebase. New native touchpoints go in `_Native` and get a
|
|
172
|
+
`# VERIFY` tag.
|
|
173
|
+
5. **Respect the one-parent rule** (see 2.6) in any layout/reparenting code.
|
|
174
|
+
6. **Don't break the two marshalling tests** without intent — they encode the
|
|
175
|
+
current threading contract.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## 5. Open work (roughly prioritized)
|
|
180
|
+
|
|
181
|
+
### 5.1 Verify the `# VERIFY` calls on Windows — mostly DONE
|
|
182
|
+
Verified on Windows 11, winui3 3.2.1, python.org Python 3.13, by launching a
|
|
183
|
+
real window (counter app: native controls realized, click handler mutated them,
|
|
184
|
+
off-thread write marshalled back). Confirmed as originally guessed:
|
|
185
|
+
- **App lifecycle:** `Application.start(callback)` is the right unpackaged entry
|
|
186
|
+
point — no `Application` subclass needed. `Window.activate()`, `.title`,
|
|
187
|
+
`.content` all correct.
|
|
188
|
+
- **Bootstrap:** `bootstrap.initialize(options=...)` as a context manager, with
|
|
189
|
+
`InitializeOptions.ON_NO_MATCH_SHOW_UI`.
|
|
190
|
+
- **Dispatcher:** `get_for_current_thread()` and `try_enqueue(fn)` both work, and
|
|
191
|
+
a plain Python callable *does* satisfy the delegate.
|
|
192
|
+
- **Layout:** `Panel.children.append(...)` works.
|
|
193
|
+
- **Values:** `Thickness(l,t,r,b)` correct; `Visibility.VISIBLE`/`COLLAPSED`
|
|
194
|
+
correct. Ints are coerced to double automatically (`font_size=20` → `20.0`).
|
|
195
|
+
- **Events:** `add_<event>` / `remove_<event>` with `(sender, args)` is correct.
|
|
196
|
+
|
|
197
|
+
Two things were **not** as assumed, and are now fixed in `_Native`:
|
|
198
|
+
- **Object-typed properties need boxing.** `Button.content = "Go"` raises
|
|
199
|
+
`TypeError: not a System.Object`. Strings/numbers must go through
|
|
200
|
+
`PropertyValue.create_*`; this is the new `_Native.box()` helper, wired into
|
|
201
|
+
`_set_native` for `content`. Any future object-typed property (`Tag`,
|
|
202
|
+
`ContentControl.Content`, ...) needs the same treatment.
|
|
203
|
+
- **Namespaces are per-wheel** (see §4.3) — the missing `winrt-Windows.Foundation`
|
|
204
|
+
package was what made event subscription look broken.
|
|
205
|
+
|
|
206
|
+
The **async bridge is now verified too** (`example_async.py`, driven end-to-end):
|
|
207
|
+
an `async def` handler returned in 0.1ms without blocking the UI thread, awaited
|
|
208
|
+
two chained WinRT async ops, and its result landed in a native `TextBlock` via
|
|
209
|
+
the DispatcherQueue. The originally-assumed mechanism (settable `Completed` +
|
|
210
|
+
`get_results()`) does work — it was verified directly — but it turned out to be
|
|
211
|
+
unnecessary, hence the `as_future` simplification in §2.8.
|
|
212
|
+
|
|
213
|
+
**No `# VERIFY` tags remain.** `TextBox` and `Grid` are the only controls never
|
|
214
|
+
instantiated; they follow the same pattern as verified ones.
|
|
215
|
+
|
|
216
|
+
Also observed: queued `run_on_ui` work cannot run while a handler is blocking the
|
|
217
|
+
UI thread — it only drains once the handler returns and the pump resumes. Obvious
|
|
218
|
+
in hindsight, but it makes any blocking wait inside a handler a deadlock risk.
|
|
219
|
+
|
|
220
|
+
### 5.2 DispatcherQueue-driven single event loop
|
|
221
|
+
Retire the two-thread split by running one event loop driven by the
|
|
222
|
+
`DispatcherQueue`, so handlers run *on* the UI thread and marshalling largely
|
|
223
|
+
disappears (this also fixes the read-marshalling gap). Bigger job. When you do it,
|
|
224
|
+
the two marshalling tests are the contract to preserve or deliberately update.
|
|
225
|
+
|
|
226
|
+
### 5.3 More controls + attached properties
|
|
227
|
+
Add the common controls. **Grid** needs a helper for attached properties
|
|
228
|
+
(`Grid.Row`/`Grid.Column`) — they don't fit the plain-property model and are
|
|
229
|
+
currently stubbed. Design that helper before adding grid-heavy layouts.
|
|
230
|
+
|
|
231
|
+
### 5.4 More curated conversions
|
|
232
|
+
`enabled`, `opacity`, colors/brushes, alignment enums — each follows the
|
|
233
|
+
`padding`/`visible` template.
|
|
234
|
+
|
|
235
|
+
### 5.5 Packaging
|
|
236
|
+
Shipping a double-clickable app means bundling Python + the Windows App Runtime,
|
|
237
|
+
ideally as MSIX, and reconciling that with the dynamic-dependency bootstrap. This
|
|
238
|
+
is the least-trodden path and worth prototyping early.
|
|
239
|
+
|
|
240
|
+
### 5.6 No declarative XAML (by design, for now)
|
|
241
|
+
The tree is built imperatively. There is no XAML markup path, hot-reload, or
|
|
242
|
+
designer. A declarative DSL over the bindings would be future work, not a current
|
|
243
|
+
goal.
|
|
244
|
+
|
|
245
|
+
### 5.7 Naming / PyPI
|
|
246
|
+
`pywinui` is currently unclaimed on PyPI — register a placeholder to hold the name.
|
|
247
|
+
Note the archived GPL-3.0 repo of the same name exists (no code); pick a license
|
|
248
|
+
deliberately.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## 6. Reference notes for translating WinUI docs
|
|
253
|
+
|
|
254
|
+
There are no Python API docs for the bindings (they're generated). Use Microsoft's
|
|
255
|
+
Windows App SDK / WinRT reference and translate names with PyWinRT conventions:
|
|
256
|
+
- Namespaces → lowercase, no underscores.
|
|
257
|
+
- Type names → stay `CapitalizedWords`.
|
|
258
|
+
- Methods/properties/fields/events → `snake_case`.
|
|
259
|
+
- Enum members → `UPPER_CASE`.
|
|
260
|
+
- Many methods are async and return `IAsyncOperation`-family types (see 2.8).
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## 7. How to run the tests
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
pip install -e ".[dev]"
|
|
268
|
+
pytest
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
`pythonpath = ["src"]` in `pyproject.toml` means the suite also runs straight
|
|
272
|
+
from a checkout without installing. To exercise the native layer you must be on
|
|
273
|
+
Windows and run the examples by hand:
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
pip install -e ".[examples,dev]"
|
|
277
|
+
python examples/counter.py
|
|
278
|
+
python examples/async_file_read.py
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
They pass on any OS. If you touch the tree model, attach/detach, property
|
|
282
|
+
forwarding, conversions, or marshalling, keep them green — that suite is the
|
|
283
|
+
guardrail protecting the design decisions above.
|
pywinui-0.1.0a1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Israel Dryer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pywinui-0.1.0a1/PKG-INFO
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pywinui
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: Build native WinUI 3 (Windows App SDK) desktop apps in idiomatic Python
|
|
5
|
+
Project-URL: Homepage, https://github.com/israel-dryer/pywinui
|
|
6
|
+
Project-URL: Repository, https://github.com/israel-dryer/pywinui
|
|
7
|
+
Project-URL: Issues, https://github.com/israel-dryer/pywinui/issues
|
|
8
|
+
Author-email: Israel Dryer <israel.dryer@gmail.com>
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 Israel Dryer
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: desktop,gui,windows,windows-app-sdk,winrt,winui,winui3
|
|
32
|
+
Classifier: Development Status :: 3 - Alpha
|
|
33
|
+
Classifier: Environment :: Win32 (MS Windows)
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 10
|
|
37
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
43
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
44
|
+
Classifier: Typing :: Typed
|
|
45
|
+
Requires-Python: >=3.10
|
|
46
|
+
Requires-Dist: winrt-windows-foundation<4,>=3.2; sys_platform == 'win32'
|
|
47
|
+
Requires-Dist: winui3-microsoft-ui-dispatching<4,>=3.2; sys_platform == 'win32'
|
|
48
|
+
Requires-Dist: winui3-microsoft-ui-xaml-controls<4,>=3.2; sys_platform == 'win32'
|
|
49
|
+
Requires-Dist: winui3-microsoft-ui-xaml<4,>=3.2; sys_platform == 'win32'
|
|
50
|
+
Requires-Dist: winui3-microsoft-windows-applicationmodel-dynamicdependency-bootstrap<4,>=3.2; sys_platform == 'win32'
|
|
51
|
+
Requires-Dist: winui3-microsoft-windows-applicationmodel-dynamicdependency<4,>=3.2; sys_platform == 'win32'
|
|
52
|
+
Provides-Extra: dev
|
|
53
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
54
|
+
Provides-Extra: examples
|
|
55
|
+
Requires-Dist: winrt-windows-storage<4,>=3.2; (sys_platform == 'win32') and extra == 'examples'
|
|
56
|
+
Description-Content-Type: text/markdown
|
|
57
|
+
|
|
58
|
+
# PyWinUI
|
|
59
|
+
|
|
60
|
+
Build **native WinUI 3** (Windows App SDK) desktop applications in idiomatic Python.
|
|
61
|
+
|
|
62
|
+
Not a themed look-alike and not a reimplementation — these are real WinUI 3
|
|
63
|
+
controls, wrapped so that you never have to touch WinRT unless you want to.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
import pywinui as ui
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class CounterApp(ui.App):
|
|
70
|
+
def build(self):
|
|
71
|
+
count = ui.TextBlock("0", font_size=32)
|
|
72
|
+
|
|
73
|
+
def bump(sender, args):
|
|
74
|
+
count.text = str(int(count.text) + 1)
|
|
75
|
+
|
|
76
|
+
return ui.Window(
|
|
77
|
+
title="PyWinUI Counter",
|
|
78
|
+
content=ui.StackPanel(
|
|
79
|
+
spacing=12, padding=24,
|
|
80
|
+
children=[
|
|
81
|
+
ui.TextBlock("Counter", font_size=20),
|
|
82
|
+
count,
|
|
83
|
+
ui.Button("Increment", on_click=bump),
|
|
84
|
+
],
|
|
85
|
+
),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
CounterApp().run()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
> **Status: early alpha.** The architecture is verified end-to-end against the
|
|
93
|
+
> real bindings on Windows 11, but only five controls are wrapped so far. The API
|
|
94
|
+
> may change. See [Current scope](#current-scope) before depending on it.
|
|
95
|
+
|
|
96
|
+
## Why now
|
|
97
|
+
|
|
98
|
+
The WinUI 3 projections for Python ([PyWinRT](https://github.com/pywinrt/pywinrt))
|
|
99
|
+
only landed in March 2025. Before that, a Python WinUI 3 app meant hand-rolling
|
|
100
|
+
raw WinRT. The bindings now exist and work; PyWinUI is the ergonomics layer on
|
|
101
|
+
top of them.
|
|
102
|
+
|
|
103
|
+
## Requirements
|
|
104
|
+
|
|
105
|
+
- **Windows 10/11** with the [Windows App Runtime](https://aka.ms/windowsappsdk/runtime)
|
|
106
|
+
- **python.org Python 3.10+** — *not* the Microsoft Store build, which is a
|
|
107
|
+
packaged app and fails the runtime bootstrap with `ERROR_NOT_SUPPORTED`
|
|
108
|
+
|
|
109
|
+
## Install
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
pip install pywinui
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The `winui3-*` dependencies are Windows-only and install automatically there.
|
|
116
|
+
On other platforms the package still installs and imports (the native layer is
|
|
117
|
+
loaded lazily), so the test suite and editor tooling work anywhere — but
|
|
118
|
+
anything that realizes a control needs Windows.
|
|
119
|
+
|
|
120
|
+
## Two ways to build a tree
|
|
121
|
+
|
|
122
|
+
Both are first-class and they compose.
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
# Flutter style — the tree is a value. Best for reusable components.
|
|
126
|
+
ui.StackPanel(children=[ui.TextBlock("a"), ui.TextBlock("b")])
|
|
127
|
+
|
|
128
|
+
# With style — handles loops, conditionals and local references far better.
|
|
129
|
+
with ui.StackPanel() as panel:
|
|
130
|
+
ui.TextBlock("Items")
|
|
131
|
+
for name in items:
|
|
132
|
+
ui.Button(name, on_click=make_handler(name))
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Async without the ceremony
|
|
136
|
+
|
|
137
|
+
Handlers may be `async def`. They're scheduled off the UI thread automatically,
|
|
138
|
+
WinRT async operations are awaited like any coroutine, and writes back to
|
|
139
|
+
widgets are marshalled onto the UI thread for you.
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
async def read_file(sender, args):
|
|
143
|
+
status.text = "Reading..."
|
|
144
|
+
file = await StorageFile.get_file_from_path_async(path)
|
|
145
|
+
text = await FileIO.read_text_async(file) # real WinRT I/O
|
|
146
|
+
status.text = f"{len(text)} chars" # marshalled back to the UI
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Failures arrive as ordinary Python exceptions — a missing path raises
|
|
150
|
+
`FileNotFoundError`.
|
|
151
|
+
|
|
152
|
+
## The escape hatch
|
|
153
|
+
|
|
154
|
+
The curated surface covers the common path with type hints, validation and value
|
|
155
|
+
conversions. Anything not wrapped still forwards by name, and `widget.native`
|
|
156
|
+
gives you the raw WinUI control:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
btn = ui.Button("Save")
|
|
160
|
+
btn.native.background = some_brush # raw WinUI, fully supported
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Current scope
|
|
164
|
+
|
|
165
|
+
| Working | Not yet |
|
|
166
|
+
|---|---|
|
|
167
|
+
| `TextBlock`, `TextBox`, `Button`, `StackPanel`, `Grid`, `Window` | The other ~100 WinUI controls |
|
|
168
|
+
| Both tree-building styles, attach/detach | Data binding, styles, resource dictionaries |
|
|
169
|
+
| `async def` handlers, awaiting WinRT ops | Control templates, virtualized lists |
|
|
170
|
+
| Off-thread writes auto-marshalled | Off-thread *reads* (wrap in `run_on_ui`) |
|
|
171
|
+
| `padding`/`margin`, `visible`, content boxing | `Grid.Row`/`Grid.Column` attached properties |
|
|
172
|
+
| Running from a Python environment | Packaging a double-clickable app (MSIX) |
|
|
173
|
+
|
|
174
|
+
**Packaging is unproven.** Shipping an app to end users means bundling Python and
|
|
175
|
+
the Windows App Runtime, likely as MSIX, and that path has not been prototyped.
|
|
176
|
+
Today this is a library for developers who already have Python installed.
|
|
177
|
+
|
|
178
|
+
## Troubleshooting
|
|
179
|
+
|
|
180
|
+
**`DLL load failed ... The filename or extension is too long`** — your virtual
|
|
181
|
+
environment path is too deep. The winui3 extension modules have very long file
|
|
182
|
+
names (`_winui3_microsoft_windows_applicationmodel_dynamicdependency_bootstrap`),
|
|
183
|
+
and a nested venv can push them past Windows' 260-character `MAX_PATH`. Use a
|
|
184
|
+
shorter path, or [enable long paths](https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation).
|
|
185
|
+
|
|
186
|
+
**`ERROR_NOT_SUPPORTED` during bootstrap** — you're on the Microsoft Store build
|
|
187
|
+
of Python, which is itself a packaged app. Use the python.org installer.
|
|
188
|
+
|
|
189
|
+
## Examples
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
pip install -e ".[examples,dev]"
|
|
193
|
+
python examples/counter.py
|
|
194
|
+
python examples/async_file_read.py
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Tests
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
pytest
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The suite runs **anywhere** — no Windows required. It swaps in a fake native
|
|
204
|
+
layer to lock down the tree model, both building styles, attach/detach, property
|
|
205
|
+
forwarding, value conversions and thread marshalling. What it deliberately
|
|
206
|
+
cannot cover is the binding boundary itself; that is verified by running the
|
|
207
|
+
examples on Windows.
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT
|