Skip to main content

tk-uia

tests License: MIT Ruff

Make Tkinter applications fully accessible to Windows screen readers and UI Automation: named, correctly typed, and activatable.

Tk 8.6 exposes widgets to Windows accessibility with no names and mostly wrong control types: buttons are unnamed, labels read as images, and every themed ttk widget is an anonymous pane. tk_uia.enable(root) fixes both halves. Each widget is annotated through MSAA for legacy clients, and answers UI Automation for itself: Invoke genuinely presses, Value genuinely types, Toggle, SelectionItem and RangeValue genuinely act, and names and states are read live from the widget at the moment a client asks. Screen readers such as NVDA, and UIA tools such as Inspect.exe or pytest-uia, see controls they can both read and operate. No runtime dependencies, no C extension, no visible change to the window.

Install

pip install tk-uia

Windows only. On other platforms enable() returns UNSUPPORTED and does nothing, so cross-platform code can call it unconditionally.

Usage

import tkinter as tk
import tk_uia

root = tk.Tk()
root.title("Tasks")
tk.Label(root, text="Task list").pack()
tk.Button(root, text="New Task", command=create).pack()

tk_uia.enable(root)
root.mainloop()

enable() comes first: every other call (set_acc_name, label_for, the bind_* family) refuses until it has run.

Widget Bare Tk After enable()
tk.Button(text="New Task") ButtonControl, no name, Invoke does nothing ButtonControl, Name='New Task', Invoke presses it
tk.Label(text="Task list") ImageControl, no name TextControl, name read live
tk.Checkbutton(text="Done") ButtonControl, no name CheckBoxControl, Toggle works, live ToggleState
tk.Entry(textvariable=var) PaneControl, no ValuePattern EditControl, SetValue types into it
ttk.Button(text="Save") anonymous PaneControl ButtonControl, named, Invoke presses it
tk.Scale(from_=0, to=10) PaneControl SliderControl, RangeValue moves it

Measured from a separate process. COVERAGE.md has the table for every widget class in both toolkits. COOKBOOK.md builds a real form end to end.

Features

  • One call covers the whole application, including windows opened later.
  • Working patterns per class: Invoke for buttons, Toggle for checkbuttons, SelectionItem for radiobuttons and notebook tabs, Value for entries, spinboxes, comboboxes and Text, RangeValue for scales and progressbars.
  • Names, values, enabled state, help and description are pulled live at the moment a client asks; name and value changes are raised as UIA property-changed events.
  • Names inferred from -text, correct control types for classic tk and ttk.
  • A declared textvariable is followed automatically: the name or value stays current with no further code.
  • label_for(label, entry) records which caption names which field. infer_names_from_layout(root) retrofits an existing dialog in one call and reports every name it chose.
  • describe(root) returns an audit you can print(): what a client gets and what is missing, with a reason and a fix per widget. Usable as data for CI gating.
  • Listbox rows and Treeview items are real elements: named, nested under their branches, selected for real (the application hears its own <<ListboxSelect>>/<<TreeviewSelect>>), joined to and removed from a selection where selectmode takes more than one, scrolled into view through ScrollItem, and a tree branch opens through ExpandCollapse. Selection changes are raised as UIA events whenever the widget's own select event fires: a user's choice, a client's, and any treeview change.
  • Notebook tabs become real tab controls a client can switch without a click.
  • annotate_only(root) keeps the previous annotation-only behaviour, and leave_to_the_proxy(widget) opts a single widget out.
  • Detects Tk 9.1's native accessibility (TIP 733) and stands down.

Limitations

  • In-process only. You can make your own application accessible, not someone else's.
  • On a single-select container (selectmode browse or single), AddToSelection and RemoveFromSelection are refused; Select is the one selection there is.
  • A combobox has no ExpandCollapse yet, so a client cannot open its dropdown; choosing goes through ValuePattern.SetValue, which on a readonly combobox takes exactly the values a user could pick and fires the same <<ComboboxSelected>> a dropdown choice would.
  • infer_names_from_layout is a guess by convention: rows are frames, or grid rows within a frame, read across their columns. It returns every name it chose; read the guess before shipping it.
  • A widget left to the MSAA proxy advertises an InvokePattern that does nothing; that is the proxy's own behaviour, and describe() says which widgets it applies to.
  • Verified against the UI Automation tree, which is what screen readers consume. Not yet verified against NVDA speech output; that is what 1.0 means on the roadmap.

Documentation

COOKBOOK.md Your first accessible form, in ten minutes.
docs/GUIDE.md Full API, how it works, every caveat and measurement.
COVERAGE.md Every widget class, measured bare and after enable().
ROADMAP.md What is next and what is out of scope.
CHANGELOG.md Release history.

License

MIT

Release files for tk-uia 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tk-uia 0.8.0
File Size Uploaded
tk_uia-0.8.0.tar.gz 193.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tk-uia 0.8.0
File Interpreter ABI Platform
tk_uia-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 257.4 kB

Release files / tk_uia-0.8.0.tar.gz

Download URL tk_uia-0.8.0.tar.gz
Size 193.6 kB
Tags Source
SHA-256 checksum
How to use checksums
29888611eb833365f1f7410367a4f8904eb619cb4663310d27a61ada7934dc4f
BLAKE2b-256 checksum
How to use checksums
0c4ff6cdd94af3439337b7807f393f1d9cc03d8f03649a54c8dcf0b5d4461c42
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 29, 2026.

Transparency log

Release files / tk_uia-0.8.0-py3-none-any.whl

Download URL tk_uia-0.8.0-py3-none-any.whl
Size 63.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c7c95571605822ea46dbb319fe646a115d571ba135de16f1bc656be25ab4ddd2
BLAKE2b-256 checksum
How to use checksums
6f16a783039a747fff192768ff164b5f156bcfa6866a07fe9fe497ce2d35e281
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 29, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.8.0 This release

2 release files

0.7.0

2 release files

0.6.3

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page