gh-43457: Tkinter: fix design flaws in wm_attributes() (GH-111404)

* When called with a single argument to get a value, it allow to omit
  the minus prefix.
* It can be called with keyword arguments to set attributes.
* w.wm_attributes(return_python_dict=True) returns a dict instead of 
  a tuple (it will be the default in future).
* Setting wantobjects to 0 no longer affects the result.
This commit is contained in:
Serhiy Storchaka 2024-02-05 18:24:54 +02:00 committed by GitHub
parent 992446dd5b
commit b4ba0f73d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 104 additions and 19 deletions

View File

@ -421,6 +421,15 @@ tkinter
:meth:`!tk_busy_current`, and :meth:`!tk_busy_status`.
(Contributed by Miguel, klappnase and Serhiy Storchaka in :gh:`72684`.)
* The :mod:`tkinter` widget method :meth:`!wm_attributes` now accepts
the attribute name without the minus prefix to get window attributes,
e.g. ``w.wm_attributes('alpha')`` and allows to specify attributes and
values to set as keyword arguments, e.g. ``w.wm_attributes(alpha=0.5)``.
Add new optional keyword-only parameter *return_python_dict*: calling
``w.wm_attributes(return_python_dict=True)`` returns the attributes as
a dict instead of a tuple.
(Contributed by Serhiy Storchaka in :gh:`43457`.)
* Add support of the "vsapi" element type in
the :meth:`~tkinter.ttk.Style.element_create` method of
:class:`tkinter.ttk.Style`.

View File

@ -14,7 +14,7 @@ class AbstractTkTest:
# Some window managers can maximize new windows.
cls.root.wm_state('normal')
try:
cls.root.wm_attributes('-zoomed', False)
cls.root.wm_attributes(zoomed=False)
except tkinter.TclError:
pass

View File

@ -437,6 +437,61 @@ class MiscTest(AbstractTkTest, unittest.TestCase):
self.assertTrue(str(vi).startswith(f'{vi.major}.{vi.minor}'))
class WmTest(AbstractTkTest, unittest.TestCase):
def test_wm_attribute(self):
w = self.root
attributes = w.wm_attributes(return_python_dict=True)
self.assertIsInstance(attributes, dict)
attributes2 = w.wm_attributes()
self.assertIsInstance(attributes2, tuple)
self.assertEqual(attributes2[::2],
tuple('-' + k for k in attributes))
self.assertEqual(attributes2[1::2], tuple(attributes.values()))
# silently deprecated
attributes3 = w.wm_attributes(None)
if self.wantobjects:
self.assertEqual(attributes3, attributes2)
else:
self.assertIsInstance(attributes3, str)
for name in attributes:
self.assertEqual(w.wm_attributes(name), attributes[name])
# silently deprecated
for name in attributes:
self.assertEqual(w.wm_attributes('-' + name), attributes[name])
self.assertIn('alpha', attributes)
self.assertIn('fullscreen', attributes)
self.assertIn('topmost', attributes)
if w._windowingsystem == "win32":
self.assertIn('disabled', attributes)
self.assertIn('toolwindow', attributes)
self.assertIn('transparentcolor', attributes)
if w._windowingsystem == "aqua":
self.assertIn('modified', attributes)
self.assertIn('notify', attributes)
self.assertIn('titlepath', attributes)
self.assertIn('transparent', attributes)
if w._windowingsystem == "x11":
self.assertIn('type', attributes)
self.assertIn('zoomed', attributes)
w.wm_attributes(alpha=0.5)
self.assertEqual(w.wm_attributes('alpha'),
0.5 if self.wantobjects else '0.5')
w.wm_attributes(alpha=1.0)
self.assertEqual(w.wm_attributes('alpha'),
1.0 if self.wantobjects else '1.0')
# silently deprecated
w.wm_attributes('-alpha', 0.5)
self.assertEqual(w.wm_attributes('alpha'),
0.5 if self.wantobjects else '0.5')
w.wm_attributes(alpha=1.0)
self.assertEqual(w.wm_attributes('alpha'),
1.0 if self.wantobjects else '1.0')
class BindTest(AbstractTkTest, unittest.TestCase):
def setUp(self):

View File

@ -2108,26 +2108,39 @@ class Wm:
aspect = wm_aspect
def wm_attributes(self, *args):
"""This subcommand returns or sets platform specific attributes
def wm_attributes(self, *args, return_python_dict=False, **kwargs):
"""Return or sets platform specific attributes.
The first form returns a list of the platform specific flags and
their values. The second form returns the value for the specific
option. The third form sets one or more of the values. The values
are as follows:
When called with a single argument return_python_dict=True,
return a dict of the platform specific attributes and their values.
When called without arguments or with a single argument
return_python_dict=False, return a tuple containing intermixed
attribute names with the minus prefix and their values.
On Windows, -disabled gets or sets whether the window is in a
disabled state. -toolwindow gets or sets the style of the window
to toolwindow (as defined in the MSDN). -topmost gets or sets
whether this is a topmost window (displays above all other
windows).
On Macintosh, XXXXX
On Unix, there are currently no special attribute values.
When called with a single string value, return the value for the
specific option. When called with keyword arguments, set the
corresponding attributes.
"""
args = ('wm', 'attributes', self._w) + args
return self.tk.call(args)
if not kwargs:
if not args:
res = self.tk.call('wm', 'attributes', self._w)
if return_python_dict:
return _splitdict(self.tk, res)
else:
return self.tk.splitlist(res)
if len(args) == 1 and args[0] is not None:
option = args[0]
if option[0] == '-':
# TODO: deprecate
option = option[1:]
return self.tk.call('wm', 'attributes', self._w, '-' + option)
# TODO: deprecate
return self.tk.call('wm', 'attributes', self._w, *args)
elif args:
raise TypeError('wm_attribute() options have been specified as '
'positional and keyword arguments')
else:
self.tk.call('wm', 'attributes', self._w, *self._options(kwargs))
attributes = wm_attributes

View File

@ -262,7 +262,7 @@ def _setup_dialog(w):
w.tk.call("::tk::unsupported::MacWindowStyle", "style",
w, "moveableModal", "")
elif w._windowingsystem == "x11":
w.wm_attributes("-type", "dialog")
w.wm_attributes(type="dialog")
# --------------------------------------------------------------------
# convenience dialogues

View File

@ -0,0 +1,8 @@
Fix the :mod:`tkinter` widget method :meth:`!wm_attributes`. It now
accepts the attribute name without the minus prefix to get window attributes
and allows to specify attributes and values to set as keyword arguments.
Add new optional keyword argument *return_python_dict*: calling
``w.wm_attributes(return_python_dict=True)`` returns the attributes as
a dict instead of a tuple.
Calling ``w.wm_attributes()`` now returns a tuple instead of string if
*wantobjects* was set to 0.