[Python-checkins] gh-72684: Tkinter: provide interface for "tk busy" subcommands (GH-107684)

serhiy-storchaka webhook-mailer at python.org
Sat Aug 19 07:48:05 EDT 2023


https://github.com/python/cpython/commit/79db9d9a0e8f51ad4ea5caae31d7ae4b29985271
commit: 79db9d9a0e8f51ad4ea5caae31d7ae4b29985271
branch: main
author: Serhiy Storchaka <storchaka at gmail.com>
committer: serhiy-storchaka <storchaka at gmail.com>
date: 2023-08-19T14:48:02+03:00
summary:

gh-72684: Tkinter: provide interface for "tk busy" subcommands (GH-107684)

Add tkinter.Misc methods: tk_busy_hold(), tk_busy_configure(), tk_busy_cget(),
tk_busy_forget(), tk_busy_current(), and tk_busy_status().

files:
A Misc/NEWS.d/next/Library/2023-08-06-10-52-12.gh-issue-72684.Ls2mSf.rst
M Doc/whatsnew/3.13.rst
M Lib/test/test_tkinter/test_misc.py
M Lib/tkinter/__init__.py

diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index 85aa81b4a98d2..47b868bad3192 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -144,6 +144,15 @@ pathlib
   :meth:`~pathlib.Path.is_dir`.
   (Contributed by Barney Gale in :gh:`77609` and :gh:`105793`.)
 
+tkinter
+-------
+
+* Add :mod:`tkinter` widget methods:
+  :meth:`!tk_busy_hold`, :meth:`!tk_busy_configure`,
+  :meth:`!tk_busy_cget`, :meth:`!tk_busy_forget`,
+  :meth:`!tk_busy_current`, and :meth:`!tk_busy_status`.
+  (Contributed by Miguel, klappnase and Serhiy Storchaka in :gh:`72684`.)
+
 traceback
 ---------
 
diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py
index d1aca58d15fbd..0ae27610be607 100644
--- a/Lib/test/test_tkinter/test_misc.py
+++ b/Lib/test/test_tkinter/test_misc.py
@@ -1,9 +1,10 @@
 import functools
 import unittest
 import tkinter
+from tkinter import TclError
 import enum
 from test import support
-from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest
+from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest, requires_tk
 
 support.requires('gui')
 
@@ -36,6 +37,59 @@ def test_generated_names(self):
         for name in str(b).split('.'):
             self.assertFalse(name.isidentifier(), msg=repr(name))
 
+    @requires_tk(8, 6, 6)
+    def test_tk_busy(self):
+        root = self.root
+        f = tkinter.Frame(root, name='myframe')
+        f2 = tkinter.Frame(root)
+        f.pack()
+        f2.pack()
+        b = tkinter.Button(f)
+        b.pack()
+        f.tk_busy_hold()
+        with self.assertRaisesRegex(TclError, 'unknown option "-spam"'):
+            f.tk_busy_configure(spam='eggs')
+        with self.assertRaisesRegex(TclError, 'unknown option "-spam"'):
+            f.tk_busy_cget('spam')
+        with self.assertRaisesRegex(TclError, 'unknown option "-spam"'):
+            f.tk_busy_configure('spam')
+        self.assertIsInstance(f.tk_busy_configure(), dict)
+
+        self.assertTrue(f.tk_busy_status())
+        self.assertFalse(root.tk_busy_status())
+        self.assertFalse(f2.tk_busy_status())
+        self.assertFalse(b.tk_busy_status())
+        self.assertIn(f, f.tk_busy_current())
+        self.assertIn(f, f.tk_busy_current('*.m?f*me'))
+        self.assertNotIn(f, f.tk_busy_current('*spam'))
+
+        f.tk_busy_forget()
+        self.assertFalse(f.tk_busy_status())
+        self.assertFalse(f.tk_busy_current())
+        with self.assertRaisesRegex(TclError, "can't find busy window"):
+            f.tk_busy_configure()
+        with self.assertRaisesRegex(TclError, "can't find busy window"):
+            f.tk_busy_forget()
+
+    @requires_tk(8, 6, 6)
+    def test_tk_busy_with_cursor(self):
+        root = self.root
+        if root._windowingsystem == 'aqua':
+            self.skipTest('the cursor option is not supported on OSX/Aqua')
+        f = tkinter.Frame(root, name='myframe')
+        f.pack()
+        f.tk_busy_hold(cursor='gumby')
+
+        self.assertEqual(f.tk_busy_cget('cursor'), 'gumby')
+        f.tk_busy_configure(cursor='heart')
+        self.assertEqual(f.tk_busy_cget('cursor'), 'heart')
+        self.assertEqual(f.tk_busy_configure()['cursor'][4], 'heart')
+        self.assertEqual(f.tk_busy_configure('cursor')[4], 'heart')
+
+        f.tk_busy_forget()
+        with self.assertRaisesRegex(TclError, "can't find busy window"):
+            f.tk_busy_cget('cursor')
+
     def test_tk_setPalette(self):
         root = self.root
         root.tk_setPalette('black')
diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
index c59f8d11e8a9d..440e7f100c8c4 100644
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -901,6 +901,85 @@ def bell(self, displayof=0):
         """Ring a display's bell."""
         self.tk.call(('bell',) + self._displayof(displayof))
 
+    def tk_busy_cget(self, option):
+        """Return the value of busy configuration option.
+
+        The widget must have been previously made busy by
+        tk_busy_hold().  Option may have any of the values accepted by
+        tk_busy_hold().
+        """
+        return self.tk.call('tk', 'busy', 'cget', self._w, '-'+option)
+    busy_cget = tk_busy_cget
+
+    def tk_busy_configure(self, cnf=None, **kw):
+        """Query or modify the busy configuration options.
+
+        The widget must have been previously made busy by
+        tk_busy_hold().  Options may have any of the values accepted by
+        tk_busy_hold().
+
+        Please note that the option database is referenced by the widget
+        name or class.  For example, if a Frame widget with name "frame"
+        is to be made busy, the busy cursor can be specified for it by
+        either call:
+
+            w.option_add('*frame.busyCursor', 'gumby')
+            w.option_add('*Frame.BusyCursor', 'gumby')
+        """
+        if kw:
+            cnf = _cnfmerge((cnf, kw))
+        elif cnf:
+            cnf = _cnfmerge(cnf)
+        if cnf is None:
+            return self._getconfigure(
+                        'tk', 'busy', 'configure', self._w)
+        if isinstance(cnf, str):
+            return self._getconfigure1(
+                        'tk', 'busy', 'configure', self._w, '-'+cnf)
+        self.tk.call('tk', 'busy', 'configure', self._w, *self._options(cnf))
+    busy_config = busy_configure = tk_busy_config = tk_busy_configure
+
+    def tk_busy_current(self, pattern=None):
+        """Return a list of widgets that are currently busy.
+
+        If a pattern is given, only busy widgets whose path names match
+        a pattern are returned.
+        """
+        return [self._nametowidget(x) for x in
+                self.tk.splitlist(self.tk.call(
+                   'tk', 'busy', 'current', pattern))]
+    busy_current = tk_busy_current
+
+    def tk_busy_forget(self):
+        """Make this widget no longer busy.
+
+        User events will again be received by the widget.
+        """
+        self.tk.call('tk', 'busy', 'forget', self._w)
+    busy_forget = tk_busy_forget
+
+    def tk_busy_hold(self, **kw):
+        """Make this widget appear busy.
+
+        The specified widget and its descendants will be blocked from
+        user interactions.  Normally update() should be called
+        immediately afterward to insure that the hold operation is in
+        effect before the application starts its processing.
+
+        The only supported configuration option is:
+
+            cursor: the cursor to be displayed when the widget is made
+                    busy.
+        """
+        self.tk.call('tk', 'busy', 'hold', self._w, *self._options(kw))
+    busy = busy_hold = tk_busy = tk_busy_hold
+
+    def tk_busy_status(self):
+        """Return True if the widget is busy, False otherwise."""
+        return self.tk.getboolean(self.tk.call(
+                'tk', 'busy', 'status', self._w))
+    busy_status = tk_busy_status
+
     # Clipboard handling:
     def clipboard_get(self, **kw):
         """Retrieve data from the clipboard on window's display.
diff --git a/Misc/NEWS.d/next/Library/2023-08-06-10-52-12.gh-issue-72684.Ls2mSf.rst b/Misc/NEWS.d/next/Library/2023-08-06-10-52-12.gh-issue-72684.Ls2mSf.rst
new file mode 100644
index 0000000000000..1ac0e6d790456
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-08-06-10-52-12.gh-issue-72684.Ls2mSf.rst
@@ -0,0 +1,3 @@
+Add :mod:`tkinter` widget methods: :meth:`!tk_busy_hold`,
+:meth:`!tk_busy_configure`, :meth:`!tk_busy_cget`, :meth:`!tk_busy_forget`,
+:meth:`!tk_busy_current`, and :meth:`!tk_busy_status`.



More information about the Python-checkins mailing list