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`. :meth:`!tk_busy_current`, and :meth:`!tk_busy_status`.
(Contributed by Miguel, klappnase and Serhiy Storchaka in :gh:`72684`.) (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 * Add support of the "vsapi" element type in
the :meth:`~tkinter.ttk.Style.element_create` method of the :meth:`~tkinter.ttk.Style.element_create` method of
:class:`tkinter.ttk.Style`. :class:`tkinter.ttk.Style`.

View file

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

View file

@ -437,6 +437,61 @@ class MiscTest(AbstractTkTest, unittest.TestCase):
self.assertTrue(str(vi).startswith(f'{vi.major}.{vi.minor}')) 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): class BindTest(AbstractTkTest, unittest.TestCase):
def setUp(self): def setUp(self):

View file

@ -2108,26 +2108,39 @@ class Wm:
aspect = wm_aspect aspect = wm_aspect
def wm_attributes(self, *args): def wm_attributes(self, *args, return_python_dict=False, **kwargs):
"""This subcommand returns or sets platform specific attributes """Return or sets platform specific attributes.
The first form returns a list of the platform specific flags and When called with a single argument return_python_dict=True,
their values. The second form returns the value for the specific return a dict of the platform specific attributes and their values.
option. The third form sets one or more of the values. The values When called without arguments or with a single argument
are as follows: 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 When called with a single string value, return the value for the
disabled state. -toolwindow gets or sets the style of the window specific option. When called with keyword arguments, set the
to toolwindow (as defined in the MSDN). -topmost gets or sets corresponding attributes.
whether this is a topmost window (displays above all other
windows).
On Macintosh, XXXXX
On Unix, there are currently no special attribute values.
""" """
args = ('wm', 'attributes', self._w) + args if not kwargs:
return self.tk.call(args) 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 attributes = wm_attributes

View file

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