]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-151874: Add tkinter wm_stackorder, wm_iconbadge and winfo_isdark (GH-151875)
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 22 Jun 2026 12:13:50 +0000 (15:13 +0300)
committerGitHub <noreply@github.com>
Mon, 22 Jun 2026 12:13:50 +0000 (12:13 +0000)
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>
Doc/library/tkinter.rst
Doc/whatsnew/3.16.rst
Lib/test/test_tkinter/test_misc.py
Lib/tkinter/__init__.py
Misc/NEWS.d/next/Library/2026-06-22-00-46-57.gh-issue-151874.8VtTrq.rst [new file with mode: 0644]

index 222a4d0128bd8d5ea7868ca445472792b2b4d238..fcace71329b345e516927e870cc5fda4804abb2b 100644 (file)
@@ -2134,6 +2134,16 @@ Base and mixin classes
       window; otherwise it refers to the display of the application's main
       window.
 
+   .. method:: winfo_isdark()
+
+      On macOS and Windows, return ``True`` if the widget is in "dark mode",
+      and ``False`` otherwise.
+      Always return ``False`` on X11.
+
+      .. versionadded:: next
+
+         Requires Tk 9.1 or newer.
+
    .. method:: winfo_ismapped()
 
       Return ``1`` if the widget is currently mapped, ``0`` otherwise.
@@ -2605,6 +2615,24 @@ Base and mixin classes
       empty string.
       :meth:`wm_group` is an alias of :meth:`!group`.
 
+   .. method:: wm_iconbadge(badge)
+      :no-typesetting:
+
+   .. method:: iconbadge(badge)
+
+      Set a badge for the window's icon, intended for display in the Dock
+      (macOS), taskbar (Windows) or app panel (X11).
+      *badge* may be a positive integer (for example a count of unread
+      messages) or an exclamation point to denote that attention is needed;
+      an empty string removes the badge.
+      On X11 the variable ``::tk::icons::base_icon(window)`` must be set to the
+      window's icon image for the badge to appear.
+      :meth:`wm_iconbadge` is an alias of :meth:`!iconbadge`.
+
+      .. versionadded:: next
+
+         Requires Tk 9.0 or newer.
+
    .. method:: wm_iconbitmap(bitmap=None, default=None)
       :no-typesetting:
 
@@ -2814,6 +2842,22 @@ Base and mixin classes
       has been set.
       :meth:`wm_sizefrom` is an alias of :meth:`!sizefrom`.
 
+   .. method:: wm_stackorder(relation=None, window=None)
+      :no-typesetting:
+
+   .. method:: stackorder(relation=None, window=None)
+
+      Query the stacking order of top-level windows.
+      With no arguments, return a list of the mapped top-level widgets in
+      stacking order, from lowest to highest, recursively including this
+      window's top-level children.
+      If *relation* is ``'isabove'`` or ``'isbelow'`` and *window* is another
+      top-level, return ``True`` if this window is respectively above or below
+      *window* in the stacking order, and ``False`` otherwise.
+      :meth:`wm_stackorder` is an alias of :meth:`!stackorder`.
+
+      .. versionadded:: next
+
    .. method:: wm_state(newstate=None)
       :no-typesetting:
 
index 24a767c56e83978b74672ed4e64717dfb13a5627..8abc4d0af8d19fc8a9f03474b3ff66a0587ef3c2 100644 (file)
@@ -163,6 +163,11 @@ tkinter
   synchronization of the displayed view with the underlying text.
   (Contributed by Serhiy Storchaka in :gh:`151675`.)
 
+* 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).
+  (Contributed by Serhiy Storchaka in :gh:`151874`.)
+
 
 xml
 ---
index b4cb5aaae1b1e5b401b5ca8b9d44c39b98dcf740..77bf84304e78ef096687f95915ee5f4c6b4e306b 100644 (file)
@@ -751,6 +751,12 @@ class WinfoTest(AbstractTkTest, unittest.TestCase):
         self.root.update()
         self.assertTrue(f.winfo_viewable())
 
+    @requires_tk(9, 1)
+    def test_winfo_isdark(self):
+        self.assertIsInstance(self.root.winfo_isdark(), bool)
+        if self.root._windowingsystem == 'x11':
+            self.assertFalse(self.root.winfo_isdark())
+
     def test_winfo_atom(self):
         atom = self.root.winfo_atom('PRIMARY')
         self.assertIsInstance(atom, int)
@@ -1057,6 +1063,37 @@ class WmTest(AbstractTkTest, unittest.TestCase):
         t.transient(self.root)
         self.assertEqual(str(t.transient()), str(self.root))
 
+    def test_wm_stackorder(self):
+        t1 = tkinter.Toplevel(self.root)
+        t2 = tkinter.Toplevel(self.root)
+        t1.deiconify()
+        t2.deiconify()
+        self.root.update()
+        t1.lift(t2)  # Raise t1 above t2.
+        self.root.update()
+        order = self.root.wm_stackorder()
+        self.assertIsInstance(order, list)
+        self.assertTrue(all(isinstance(w, tkinter.Misc) for w in order))
+        names = [str(w) for w in order]
+        self.assertIn(str(t1), names)
+        self.assertIn(str(t2), names)
+        # The list is ordered from lowest to highest, consistently with the
+        # isabove/isbelow queries.
+        self.assertGreater(names.index(str(t1)), names.index(str(t2)))
+        self.assertIs(t1.wm_stackorder('isabove', t2), True)
+        self.assertIs(t1.wm_stackorder('isbelow', t2), False)
+        self.assertIs(t2.wm_stackorder('isbelow', t1), True)
+
+    @requires_tk(9, 0)
+    def test_wm_iconbadge(self):
+        if self.root._windowingsystem == 'x11':
+            # On X11 the badge requires ::tk::icons::base_icon to be set.
+            self.skipTest('iconbadge needs a base icon on X11')
+        # The badge is not queryable, so just check the call does not fail.
+        self.root.wm_iconbadge('3')
+        self.root.wm_iconbadge('!')
+        self.root.wm_iconbadge('')
+
 
 class EventTest(AbstractTkTest, unittest.TestCase):
 
index 8bdf7cc1e2d96b8dfc01f364d254f292e5beefe3..24d229ed74d9489cd30ecba51e8b2f138d7e00cb 100644 (file)
@@ -1275,6 +1275,15 @@ class Misc:
         args = ('winfo', 'interps') + self._displayof(displayof)
         return self.tk.splitlist(self.tk.call(args))
 
+    def winfo_isdark(self): # new in Tk 9.1
+        """Return whether this widget is in "dark mode".
+
+        On macOS and Windows return True if the widget is in "dark mode",
+        and False otherwise.  Always return False on X11.
+        """
+        return self.tk.getboolean(
+            self.tk.call('winfo', 'isdark', self._w))
+
     def winfo_ismapped(self):
         """Return true if this widget is mapped."""
         return self.tk.getint(
@@ -2330,6 +2339,22 @@ class Wm:
 
     group = wm_group
 
+    def wm_iconbadge(self, badge): # new in Tk 9.0
+        """Set a badge for the icon of this widget.
+
+        BADGE can be a positive integer number, for instance the number of
+        new or unread messages, or an exclamation point denoting attention
+        needed. If BADGE is an empty string, the badge image is removed from
+        the application icon.
+
+        The badge is intended for display in the Dock (macOS), taskbar
+        (Windows) or app panel (X11). On X11, the variable
+        ::tk::icons::base_icon(WINDOW) must be set to the image used for the
+        window icon for this command to work."""
+        return self.tk.call('wm', 'iconbadge', self._w, badge)
+
+    iconbadge = wm_iconbadge
+
     def wm_iconbitmap(self, bitmap=None, default=None):
         """Set bitmap for the iconified widget to BITMAP. Return
         the bitmap if None is given.
@@ -2479,6 +2504,25 @@ class Wm:
 
     sizefrom = wm_sizefrom
 
+    def wm_stackorder(self, relation=None, window=None):
+        """Query the stacking order of toplevel windows.
+
+        If called with no arguments, return a list of toplevel widgets in
+        stacking order, from lowest to highest.  The list recursively
+        includes all of this window's children that are toplevels; only
+        toplevels currently mapped to the screen are included.
+
+        If RELATION is "isabove" or "isbelow" and WINDOW is another toplevel,
+        return True if this window is respectively above or below WINDOW in
+        the stacking order, and False otherwise."""
+        if relation is None:
+            return [self._nametowidget(x) for x in self.tk.splitlist(
+                self.tk.call('wm', 'stackorder', self._w))]
+        return self.tk.getboolean(
+            self.tk.call('wm', 'stackorder', self._w, relation, window))
+
+    stackorder = wm_stackorder
+
     def wm_state(self, newstate=None):
         """Query or set the state of this widget as one of normal, icon,
         iconic (see wm_iconwindow), withdrawn, or zoomed (Windows only)."""
diff --git a/Misc/NEWS.d/next/Library/2026-06-22-00-46-57.gh-issue-151874.8VtTrq.rst b/Misc/NEWS.d/next/Library/2026-06-22-00-46-57.gh-issue-151874.8VtTrq.rst
new file mode 100644 (file)
index 0000000..50a33f5
--- /dev/null
@@ -0,0 +1,3 @@
+Add the :mod:`tkinter` methods :meth:`!Misc.winfo_isdark`,
+:meth:`!Wm.wm_iconbadge` and :meth:`!Wm.wm_stackorder`, wrapping the
+``winfo isdark``, ``wm iconbadge`` and ``wm stackorder`` Tk commands.