Skip to content

Commit b4ba0f7

Browse files
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.
1 parent 992446d commit b4ba0f7

File tree

6 files changed

+106
-21
lines changed

6 files changed

+106
-21
lines changed

Doc/whatsnew/3.13.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,15 @@ tkinter
421421
:meth:`!tk_busy_current`, and :meth:`!tk_busy_status`.
422422
(Contributed by Miguel, klappnase and Serhiy Storchaka in :gh:`72684`.)
423423

424+
* The :mod:`tkinter` widget method :meth:`!wm_attributes` now accepts
425+
the attribute name without the minus prefix to get window attributes,
426+
e.g. ``w.wm_attributes('alpha')`` and allows to specify attributes and
427+
values to set as keyword arguments, e.g. ``w.wm_attributes(alpha=0.5)``.
428+
Add new optional keyword-only parameter *return_python_dict*: calling
429+
``w.wm_attributes(return_python_dict=True)`` returns the attributes as
430+
a dict instead of a tuple.
431+
(Contributed by Serhiy Storchaka in :gh:`43457`.)
432+
424433
* Add support of the "vsapi" element type in
425434
the :meth:`~tkinter.ttk.Style.element_create` method of
426435
:class:`tkinter.ttk.Style`.

Lib/test/test_tkinter/support.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def setUpClass(cls):
1414
# Some window managers can maximize new windows.
1515
cls.root.wm_state('normal')
1616
try:
17-
cls.root.wm_attributes('-zoomed', False)
17+
cls.root.wm_attributes(zoomed=False)
1818
except tkinter.TclError:
1919
pass
2020

Lib/test/test_tkinter/test_misc.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,61 @@ def test_info_patchlevel(self):
437437
self.assertTrue(str(vi).startswith(f'{vi.major}.{vi.minor}'))
438438

439439

440+
class WmTest(AbstractTkTest, unittest.TestCase):
441+
442+
def test_wm_attribute(self):
443+
w = self.root
444+
attributes = w.wm_attributes(return_python_dict=True)
445+
self.assertIsInstance(attributes, dict)
446+
attributes2 = w.wm_attributes()
447+
self.assertIsInstance(attributes2, tuple)
448+
self.assertEqual(attributes2[::2],
449+
tuple('-' + k for k in attributes))
450+
self.assertEqual(attributes2[1::2], tuple(attributes.values()))
451+
# silently deprecated
452+
attributes3 = w.wm_attributes(None)
453+
if self.wantobjects:
454+
self.assertEqual(attributes3, attributes2)
455+
else:
456+
self.assertIsInstance(attributes3, str)
457+
458+
for name in attributes:
459+
self.assertEqual(w.wm_attributes(name), attributes[name])
460+
# silently deprecated
461+
for name in attributes:
462+
self.assertEqual(w.wm_attributes('-' + name), attributes[name])
463+
464+
self.assertIn('alpha', attributes)
465+
self.assertIn('fullscreen', attributes)
466+
self.assertIn('topmost', attributes)
467+
if w._windowingsystem == "win32":
468+
self.assertIn('disabled', attributes)
469+
self.assertIn('toolwindow', attributes)
470+
self.assertIn('transparentcolor', attributes)
471+
if w._windowingsystem == "aqua":
472+
self.assertIn('modified', attributes)
473+
self.assertIn('notify', attributes)
474+
self.assertIn('titlepath', attributes)
475+
self.assertIn('transparent', attributes)
476+
if w._windowingsystem == "x11":
477+
self.assertIn('type', attributes)
478+
self.assertIn('zoomed', attributes)
479+
480+
w.wm_attributes(alpha=0.5)
481+
self.assertEqual(w.wm_attributes('alpha'),
482+
0.5 if self.wantobjects else '0.5')
483+
w.wm_attributes(alpha=1.0)
484+
self.assertEqual(w.wm_attributes('alpha'),
485+
1.0 if self.wantobjects else '1.0')
486+
# silently deprecated
487+
w.wm_attributes('-alpha', 0.5)
488+
self.assertEqual(w.wm_attributes('alpha'),
489+
0.5 if self.wantobjects else '0.5')
490+
w.wm_attributes(alpha=1.0)
491+
self.assertEqual(w.wm_attributes('alpha'),
492+
1.0 if self.wantobjects else '1.0')
493+
494+
440495
class BindTest(AbstractTkTest, unittest.TestCase):
441496

442497
def setUp(self):

Lib/tkinter/__init__.py

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2108,26 +2108,39 @@ def wm_aspect(self,
21082108

21092109
aspect = wm_aspect
21102110

2111-
def wm_attributes(self, *args):
2112-
"""This subcommand returns or sets platform specific attributes
2113-
2114-
The first form returns a list of the platform specific flags and
2115-
their values. The second form returns the value for the specific
2116-
option. The third form sets one or more of the values. The values
2117-
are as follows:
2118-
2119-
On Windows, -disabled gets or sets whether the window is in a
2120-
disabled state. -toolwindow gets or sets the style of the window
2121-
to toolwindow (as defined in the MSDN). -topmost gets or sets
2122-
whether this is a topmost window (displays above all other
2123-
windows).
2124-
2125-
On Macintosh, XXXXX
2126-
2127-
On Unix, there are currently no special attribute values.
2111+
def wm_attributes(self, *args, return_python_dict=False, **kwargs):
2112+
"""Return or sets platform specific attributes.
2113+
2114+
When called with a single argument return_python_dict=True,
2115+
return a dict of the platform specific attributes and their values.
2116+
When called without arguments or with a single argument
2117+
return_python_dict=False, return a tuple containing intermixed
2118+
attribute names with the minus prefix and their values.
2119+
2120+
When called with a single string value, return the value for the
2121+
specific option. When called with keyword arguments, set the
2122+
corresponding attributes.
21282123
"""
2129-
args = ('wm', 'attributes', self._w) + args
2130-
return self.tk.call(args)
2124+
if not kwargs:
2125+
if not args:
2126+
res = self.tk.call('wm', 'attributes', self._w)
2127+
if return_python_dict:
2128+
return _splitdict(self.tk, res)
2129+
else:
2130+
return self.tk.splitlist(res)
2131+
if len(args) == 1 and args[0] is not None:
2132+
option = args[0]
2133+
if option[0] == '-':
2134+
# TODO: deprecate
2135+
option = option[1:]
2136+
return self.tk.call('wm', 'attributes', self._w, '-' + option)
2137+
# TODO: deprecate
2138+
return self.tk.call('wm', 'attributes', self._w, *args)
2139+
elif args:
2140+
raise TypeError('wm_attribute() options have been specified as '
2141+
'positional and keyword arguments')
2142+
else:
2143+
self.tk.call('wm', 'attributes', self._w, *self._options(kwargs))
21312144

21322145
attributes = wm_attributes
21332146

Lib/tkinter/simpledialog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ def _setup_dialog(w):
262262
w.tk.call("::tk::unsupported::MacWindowStyle", "style",
263263
w, "moveableModal", "")
264264
elif w._windowingsystem == "x11":
265-
w.wm_attributes("-type", "dialog")
265+
w.wm_attributes(type="dialog")
266266

267267
# --------------------------------------------------------------------
268268
# convenience dialogues
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Fix the :mod:`tkinter` widget method :meth:`!wm_attributes`. It now
2+
accepts the attribute name without the minus prefix to get window attributes
3+
and allows to specify attributes and values to set as keyword arguments.
4+
Add new optional keyword argument *return_python_dict*: calling
5+
``w.wm_attributes(return_python_dict=True)`` returns the attributes as
6+
a dict instead of a tuple.
7+
Calling ``w.wm_attributes()`` now returns a tuple instead of string if
8+
*wantobjects* was set to 0.

0 commit comments

Comments
 (0)