Skip to content

Commit 6f52390

Browse files
gh-151874: Add tkinter wm_stackorder, wm_iconbadge and winfo_isdark
Wrap the Tk commands "wm stackorder" (toplevel stacking order, since Tk 8.4), "wm iconbadge" (application icon badge, new in Tk 9.0) and "winfo isdark" (dark mode detection, new in Tk 9.0) as the methods Wm.wm_stackorder(), Wm.wm_iconbadge() and Misc.winfo_isdark(), with the usual stackorder and iconbadge short aliases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWevzas4XVpjzedzR9gKVo
1 parent f28ef85 commit 6f52390

5 files changed

Lines changed: 133 additions & 0 deletions

File tree

Doc/library/tkinter.rst

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,6 +2134,16 @@ Base and mixin classes
21342134
window; otherwise it refers to the display of the application's main
21352135
window.
21362136

2137+
.. method:: winfo_isdark()
2138+
2139+
On macOS and Windows, return ``True`` if the widget is in "dark mode",
2140+
and ``False`` otherwise.
2141+
Always return ``False`` on X11.
2142+
2143+
.. versionadded:: next
2144+
2145+
Requires Tk 9.0 or newer.
2146+
21372147
.. method:: winfo_ismapped()
21382148

21392149
Return ``1`` if the widget is currently mapped, ``0`` otherwise.
@@ -2605,6 +2615,24 @@ Base and mixin classes
26052615
empty string.
26062616
:meth:`wm_group` is an alias of :meth:`!group`.
26072617

2618+
.. method:: wm_iconbadge(badge)
2619+
:no-typesetting:
2620+
2621+
.. method:: iconbadge(badge)
2622+
2623+
Set a badge for the window's icon, intended for display in the Dock
2624+
(macOS), taskbar (Windows) or app panel (X11).
2625+
*badge* may be a positive integer (for example a count of unread
2626+
messages) or an exclamation point to denote that attention is needed;
2627+
an empty string removes the badge.
2628+
On X11 the variable ``::tk::icons::base_icon(window)`` must be set to the
2629+
window's icon image for the badge to appear.
2630+
:meth:`wm_iconbadge` is an alias of :meth:`!iconbadge`.
2631+
2632+
.. versionadded:: next
2633+
2634+
Requires Tk 9.0 or newer.
2635+
26082636
.. method:: wm_iconbitmap(bitmap=None, default=None)
26092637
:no-typesetting:
26102638

@@ -2814,6 +2842,22 @@ Base and mixin classes
28142842
has been set.
28152843
:meth:`wm_sizefrom` is an alias of :meth:`!sizefrom`.
28162844

2845+
.. method:: wm_stackorder(relation=None, window=None)
2846+
:no-typesetting:
2847+
2848+
.. method:: stackorder(relation=None, window=None)
2849+
2850+
Query the stacking order of top-level windows.
2851+
With no arguments, return a list of the mapped top-level widgets in
2852+
stacking order, from lowest to highest, recursively including this
2853+
window's top-level children.
2854+
If *relation* is ``'isabove'`` or ``'isbelow'`` and *window* is another
2855+
top-level, return ``True`` if this window is respectively above or below
2856+
*window* in the stacking order, and ``False`` otherwise.
2857+
:meth:`wm_stackorder` is an alias of :meth:`!stackorder`.
2858+
2859+
.. versionadded:: next
2860+
28172861
.. method:: wm_state(newstate=None)
28182862
:no-typesetting:
28192863

Doc/whatsnew/3.16.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ tkinter
157157
synchronization of the displayed view with the underlying text.
158158
(Contributed by Serhiy Storchaka in :gh:`151675`.)
159159

160+
* Added new window-management methods :meth:`~tkinter.Misc.winfo_isdark`
161+
(dark mode detection), :meth:`~tkinter.Wm.wm_iconbadge` (application icon
162+
badge) and :meth:`~tkinter.Wm.wm_stackorder` (toplevel stacking order).
163+
(Contributed by Serhiy Storchaka in :gh:`151874`.)
164+
160165
xml
161166
---
162167

Lib/test/test_tkinter/test_misc.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,12 @@ def test_winfo_viewable(self):
751751
self.root.update()
752752
self.assertTrue(f.winfo_viewable())
753753

754+
@requires_tk(9, 0)
755+
def test_winfo_isdark(self):
756+
self.assertIsInstance(self.root.winfo_isdark(), bool)
757+
if self.root._windowingsystem == 'x11':
758+
self.assertFalse(self.root.winfo_isdark())
759+
754760
def test_winfo_atom(self):
755761
atom = self.root.winfo_atom('PRIMARY')
756762
self.assertIsInstance(atom, int)
@@ -1057,6 +1063,37 @@ def test_wm_transient(self):
10571063
t.transient(self.root)
10581064
self.assertEqual(str(t.transient()), str(self.root))
10591065

1066+
def test_wm_stackorder(self):
1067+
t1 = tkinter.Toplevel(self.root)
1068+
t2 = tkinter.Toplevel(self.root)
1069+
t1.deiconify()
1070+
t2.deiconify()
1071+
self.root.update()
1072+
t1.lift(t2) # Raise t1 above t2.
1073+
self.root.update()
1074+
order = self.root.wm_stackorder()
1075+
self.assertIsInstance(order, list)
1076+
self.assertTrue(all(isinstance(w, tkinter.Misc) for w in order))
1077+
names = [str(w) for w in order]
1078+
self.assertIn(str(t1), names)
1079+
self.assertIn(str(t2), names)
1080+
# The list is ordered from lowest to highest, consistently with the
1081+
# isabove/isbelow queries.
1082+
self.assertGreater(names.index(str(t1)), names.index(str(t2)))
1083+
self.assertIs(t1.wm_stackorder('isabove', t2), True)
1084+
self.assertIs(t1.wm_stackorder('isbelow', t2), False)
1085+
self.assertIs(t2.wm_stackorder('isbelow', t1), True)
1086+
1087+
@requires_tk(9, 0)
1088+
def test_wm_iconbadge(self):
1089+
if self.root._windowingsystem == 'x11':
1090+
# On X11 the badge requires ::tk::icons::base_icon to be set.
1091+
self.skipTest('iconbadge needs a base icon on X11')
1092+
# The badge is not queryable, so just check the call does not fail.
1093+
self.root.wm_iconbadge('3')
1094+
self.root.wm_iconbadge('!')
1095+
self.root.wm_iconbadge('')
1096+
10601097

10611098
class EventTest(AbstractTkTest, unittest.TestCase):
10621099

Lib/tkinter/__init__.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,15 @@ def winfo_interps(self, displayof=0):
12751275
args = ('winfo', 'interps') + self._displayof(displayof)
12761276
return self.tk.splitlist(self.tk.call(args))
12771277

1278+
def winfo_isdark(self): # new in Tk 9.0
1279+
"""Return whether this widget is in "dark mode".
1280+
1281+
On macOS and Windows return True if the widget is in "dark mode",
1282+
and False otherwise. Always return False on X11.
1283+
"""
1284+
return self.tk.getboolean(
1285+
self.tk.call('winfo', 'isdark', self._w))
1286+
12781287
def winfo_ismapped(self):
12791288
"""Return true if this widget is mapped."""
12801289
return self.tk.getint(
@@ -2330,6 +2339,22 @@ def wm_group(self, pathName=None):
23302339

23312340
group = wm_group
23322341

2342+
def wm_iconbadge(self, badge): # new in Tk 9.0
2343+
"""Set a badge for the icon of this widget.
2344+
2345+
BADGE can be a positive integer number, for instance the number of
2346+
new or unread messages, or an exclamation point denoting attention
2347+
needed. If BADGE is an empty string, the badge image is removed from
2348+
the application icon.
2349+
2350+
The badge is intended for display in the Dock (macOS), taskbar
2351+
(Windows) or app panel (X11). On X11, the variable
2352+
::tk::icons::base_icon(WINDOW) must be set to the image used for the
2353+
window icon for this command to work."""
2354+
return self.tk.call('wm', 'iconbadge', self._w, badge)
2355+
2356+
iconbadge = wm_iconbadge
2357+
23332358
def wm_iconbitmap(self, bitmap=None, default=None):
23342359
"""Set bitmap for the iconified widget to BITMAP. Return
23352360
the bitmap if None is given.
@@ -2479,6 +2504,25 @@ def wm_sizefrom(self, who=None):
24792504

24802505
sizefrom = wm_sizefrom
24812506

2507+
def wm_stackorder(self, relation=None, window=None):
2508+
"""Query the stacking order of toplevel windows.
2509+
2510+
If called with no arguments, return a list of toplevel widgets in
2511+
stacking order, from lowest to highest. The list recursively
2512+
includes all of this window's children that are toplevels; only
2513+
toplevels currently mapped to the screen are included.
2514+
2515+
If RELATION is "isabove" or "isbelow" and WINDOW is another toplevel,
2516+
return True if this window is respectively above or below WINDOW in
2517+
the stacking order, and False otherwise."""
2518+
if relation is None:
2519+
return [self._nametowidget(x) for x in self.tk.splitlist(
2520+
self.tk.call('wm', 'stackorder', self._w))]
2521+
return self.tk.getboolean(
2522+
self.tk.call('wm', 'stackorder', self._w, relation, window))
2523+
2524+
stackorder = wm_stackorder
2525+
24822526
def wm_state(self, newstate=None):
24832527
"""Query or set the state of this widget as one of normal, icon,
24842528
iconic (see wm_iconwindow), withdrawn, or zoomed (Windows only)."""
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add the :mod:`tkinter` methods :meth:`!Misc.winfo_isdark`,
2+
:meth:`!Wm.wm_iconbadge` and :meth:`!Wm.wm_stackorder`, wrapping the
3+
``winfo isdark``, ``wm iconbadge`` and ``wm stackorder`` Tk commands.

0 commit comments

Comments
 (0)