[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