Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions Doc/library/tkinter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1894,6 +1894,75 @@ Base and mixin classes

.. versionadded:: next

The following methods support assistive technologies such as screen
readers.
They require Tk 9.1 or newer.
Tk automatically exposes the standard widgets to the platform
accessibility API;
these methods allow overriding the exposed attributes and registering
custom widgets.
If no assistive technology is active when the application starts,
all these calls (except :meth:`!tk_check_screenreader`)
have no effect and return ``0``.

.. method:: tk_check_screenreader()

Return whether a screen reader is currently running.

.. versionadded:: next

.. method:: tk_set_acc_role(value)
tk_set_acc_name(value)
tk_set_acc_description(value)
tk_set_acc_value(value)
tk_set_acc_state(value)
tk_set_acc_action(value)
tk_set_acc_help(value)

Set an accessible attribute of the widget.
The supported roles are ``Button``, ``Canvas``, ``Checkbutton``,
``Combobox``, ``Entry``, ``Label``, ``Listbox``, ``Menu``,
``Notebook``, ``Progressbar``, ``Scale``, ``Scrollbar``,
``Spinbox``, ``Table``, ``Text`` and ``Tree``.

.. versionadded:: next

.. method:: tk_get_acc_role()
tk_get_acc_name()
tk_get_acc_description()
tk_get_acc_value()
tk_get_acc_state()
tk_get_acc_action()
tk_get_acc_help()

Return an accessible attribute of the widget.
Raise :exc:`~tkinter.TclError` if the attribute has not been set.

.. versionadded:: next

.. method:: tk_add_acc_object()

Register the widget with the platform accessibility API.
This is only needed for custom widgets;
the standard widgets are registered automatically.

.. versionadded:: next

.. method:: tk_emit_selection_change()

Notify the platform accessibility API that the selection in the
widget has changed.

.. versionadded:: next

.. method:: tk_emit_focus_change()

Notify the platform accessibility API that the widget has received
focus.
Not available on macOS.

.. versionadded:: next

The methods with the ``busy_`` prefix manage the busy state of a window,
which shows a busy cursor and ignores user input.

Expand Down
12 changes: 12 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,18 @@ tkinter
report the user idle time.
(Contributed by Serhiy Storchaka in :gh:`151881`.)

* Added :mod:`tkinter` support for the Tk 9.1 accessibility API: the
:meth:`~tkinter.Misc.tk_set_acc_role` and
:meth:`~tkinter.Misc.tk_get_acc_role` families of methods which set and
return the accessible attributes of a widget, the
:meth:`~tkinter.Misc.tk_add_acc_object`,
:meth:`~tkinter.Misc.tk_emit_selection_change` and
:meth:`~tkinter.Misc.tk_emit_focus_change` methods which register custom
widgets with the platform accessibility API, and the
:meth:`~tkinter.Misc.tk_check_screenreader` method which reports whether
a screen reader is running.
(Contributed by Serhiy Storchaka in :gh:`145578`.)

* Added new window-management methods :meth:`~tkinter.Misc.winfo_isdark`
(dark mode detection), :meth:`~tkinter.Wm.wm_iconbadge` (application icon
badge) and :meth:`~tkinter.Wm.wm_stackorder` (toplevel stacking order).
Expand Down
29 changes: 29 additions & 0 deletions Lib/test/test_tkinter/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,35 @@ def test_tk_inactive(self):
# Resetting the timer returns None and does not raise.
self.assertIsNone(self.root.tk_inactive(reset=True))

@requires_tk(9, 1)
def test_accessibility(self):
# Without an active screen reader every "tk accessible" call is
# a no-op returning 0, so only test that the calls do not raise
# and check the values when a screen reader is active.
try:
sr = self.root.tk_check_screenreader()
except tkinter.TclError:
# Tk 9.1a0 or Tk compiled without accessibility support
# (e.g. without ATK on X11).
self.skipTest('the "tk accessible" command is not supported')
self.assertIsInstance(sr, bool)
f = tkinter.Frame(self.root)
for attr in ('name', 'description', 'value', 'state',
'action', 'help'):
with self.subTest(attr=attr):
getattr(f, f'tk_set_acc_{attr}')('spam')
result = getattr(f, f'tk_get_acc_{attr}')()
if sr:
self.assertEqual(result, 'spam')
f.tk_set_acc_role('Label')
result = f.tk_get_acc_role()
if sr:
self.assertEqual(result, 'Label')
f.tk_add_acc_object()
f.tk_emit_selection_change()
if self.root._windowingsystem != 'aqua':
f.tk_emit_focus_change()

def test_wait_variable(self):
var = tkinter.StringVar(self.root)
self.assertEqual(self.root.waitvar, self.root.wait_variable)
Expand Down
86 changes: 86 additions & 0 deletions Lib/tkinter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,92 @@ def tk_inactive(self, reset=False, *, displayof=0):
else:
return self.tk.getint(self.tk.call(args))

# Accessibility support for assistive technologies such as screen
# readers. Requires Tk 9.1 or newer. If no assistive technology
# is active when the application starts, all these calls (except
# tk_check_screenreader()) have no effect and return 0.

def tk_check_screenreader(self):
"""Return whether a screen reader is currently running.

Requires Tk 9.1 or newer."""
return self.tk.getboolean(self.tk.call(
'tk', 'accessible', 'check_screenreader'))

def tk_set_acc_role(self, value):
"""Set the accessible role of the widget."""
self.tk.call('tk', 'accessible', 'set_acc_role', self._w, value)

def tk_get_acc_role(self):
"""Return the accessible role of the widget."""
return self.tk.call('tk', 'accessible', 'get_acc_role', self._w)

def tk_set_acc_name(self, value):
"""Set the accessible name of the widget."""
self.tk.call('tk', 'accessible', 'set_acc_name', self._w, value)

def tk_get_acc_name(self):
"""Return the accessible name of the widget."""
return self.tk.call('tk', 'accessible', 'get_acc_name', self._w)

def tk_set_acc_description(self, value):
"""Set the accessible description of the widget."""
self.tk.call('tk', 'accessible', 'set_acc_description',
self._w, value)

def tk_get_acc_description(self):
"""Return the accessible description of the widget."""
return self.tk.call('tk', 'accessible', 'get_acc_description',
self._w)

def tk_set_acc_value(self, value):
"""Set the accessible value of the widget."""
self.tk.call('tk', 'accessible', 'set_acc_value', self._w, value)

def tk_get_acc_value(self):
"""Return the accessible value of the widget."""
return self.tk.call('tk', 'accessible', 'get_acc_value', self._w)

def tk_set_acc_state(self, value):
"""Set the accessible state of the widget."""
self.tk.call('tk', 'accessible', 'set_acc_state', self._w, value)

def tk_get_acc_state(self):
"""Return the accessible state of the widget."""
return self.tk.call('tk', 'accessible', 'get_acc_state', self._w)

def tk_set_acc_action(self, value):
"""Set the accessible action of the widget."""
self.tk.call('tk', 'accessible', 'set_acc_action', self._w, value)

def tk_get_acc_action(self):
"""Return the accessible action of the widget."""
return self.tk.call('tk', 'accessible', 'get_acc_action', self._w)

def tk_set_acc_help(self, value):
"""Set the accessible help text of the widget."""
self.tk.call('tk', 'accessible', 'set_acc_help', self._w, value)

def tk_get_acc_help(self):
"""Return the accessible help text of the widget."""
return self.tk.call('tk', 'accessible', 'get_acc_help', self._w)

def tk_add_acc_object(self):
"""Register the widget with the platform accessibility API."""
self.tk.call('tk', 'accessible', 'add_acc_object', self._w)

def tk_emit_selection_change(self):
"""Notify the platform accessibility API that the selection
in the widget has changed."""
self.tk.call('tk', 'accessible', 'emit_selection_change', self._w)

def tk_emit_focus_change(self):
"""Notify the platform accessibility API that the widget has
received focus.

Not available on macOS."""
self.tk.call('tk', 'accessible', 'emit_focus_change', self._w)

def wait_variable(self, name):
"""Wait until the variable is modified.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Added :mod:`tkinter` support for the Tk 9.1 accessibility API: the
``tk_set_acc_*`` and ``tk_get_acc_*`` families of methods which set and
return the accessible attributes of a widget, the
:meth:`~tkinter.Misc.tk_add_acc_object`,
:meth:`~tkinter.Misc.tk_emit_selection_change` and
:meth:`~tkinter.Misc.tk_emit_focus_change` methods which register custom
widgets with the platform accessibility API, and the
:meth:`~tkinter.Misc.tk_check_screenreader` method which reports whether a
screen reader is running.
Loading