Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Doc/library/tkinter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1954,6 +1954,26 @@ Base and mixin classes
A true *boolean* value enables strict Motif compliance (for example, no
color change when the mouse passes over a slider).
Return the resulting setting.

.. method:: tk_scaling(number=None, *, displayof=0)

Query or set the scaling factor used by Tk to convert between physical
units (such as points, inches or millimeters) and pixels, expressed as
the number of pixels per point (where a point is 1/72 inch).
With no argument, return the current factor; otherwise set it to the
floating-point *number*.

.. versionadded:: next

.. method:: tk_inactive(reset=False, *, displayof=0)

Return the number of milliseconds since the last time the user interacted
with the system, or ``-1`` if the windowing system does not support this.
If *reset* is true, reset the inactivity timer to zero instead and return
``None``.

.. versionadded:: next

.. method:: busy(**kw)
:no-typesetting:

Expand Down Expand Up @@ -4707,6 +4727,15 @@ Widget classes
If the *postcommand* option has been specified, it is evaluated before
the menu is posted.

.. method:: postcascade(index)

Post the submenu associated with the cascade entry given by *index*,
unposting any previously posted submenu.
This has no effect if *index* does not name a cascade entry or if the
menu itself is not posted.

.. versionadded:: next

.. method:: tk_popup(x, y, entry='')

Post the menu as a popup at the root-window coordinates *x* and *y*.
Expand Down
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ tkinter
validation command.
(Contributed by Serhiy Storchaka in :gh:`151878`.)

* Added the :meth:`tkinter.Menu.postcascade` method, and the
:meth:`~tkinter.Misc.tk_scaling` and :meth:`~tkinter.Misc.tk_inactive`
methods which respectively query or set the display scaling factor and
report the user idle time.
(Contributed by Serhiy Storchaka in :gh:`151881`.)

* Added new window-management methods :meth:`~tkinter.Misc.winfo_isdark`
(dark mode detection), :meth:`~tkinter.Wm.wm_iconbadge` (application icon
badge) and :meth:`~tkinter.Wm.wm_stackorder` (toplevel stacking order).
Expand Down
18 changes: 18 additions & 0 deletions Lib/test/test_tkinter/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,24 @@ def test_tk_bisque(self):
self.assertEqual(root['background'], '#ffe4c4')
self.assertRaises(TypeError, root.tk_bisque, 'x')

def test_tk_scaling(self):
old = self.root.tk_scaling()
self.assertIsInstance(old, float)
self.assertGreater(old, 0)
self.addCleanup(self.root.tk_scaling, old)
# Setting the factor is reflected by a subsequent query. Tk may round
# it slightly when converting to and from its internal representation.
self.root.tk_scaling(2.0)
self.assertAlmostEqual(self.root.tk_scaling(), 2.0, delta=0.1)

def test_tk_inactive(self):
ms = self.root.tk_inactive()
self.assertIsInstance(ms, int)
# A count of milliseconds, or -1 if the windowing system lacks support.
self.assertGreaterEqual(ms, -1)
# Resetting the timer returns None and does not raise.
self.assertIsNone(self.root.tk_inactive(reset=True))

def test_wait_variable(self):
var = tkinter.StringVar(self.root)
self.assertEqual(self.root.waitvar, self.root.wait_variable)
Expand Down
29 changes: 29 additions & 0 deletions Lib/test/test_tkinter/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2566,6 +2566,35 @@ def test_post_unpost(self):
m.update()
self.assertFalse(m.winfo_ismapped())

def test_postcascade(self):
m = self.create(tearoff=False)
submenu = tkinter.Menu(m, tearoff=False)
submenu.add_command(label='Item')
m.add_cascade(label='Cascade', menu=submenu)
m.add_command(label='Plain')
# No effect (but no error) when the menu is not posted, when the index
# is not a cascade entry, or when given a label.
m.postcascade(0)
m.postcascade(1)
m.postcascade('Cascade')

with self.subTest('posted menu'):
if m._windowingsystem != 'x11':
# Posting a menu is modal on Windows and uses a native,
# unmapped menu on Aqua, so it cannot be tested synchronously
# there.
self.skipTest('menu posting is not testable on this platform')
m.post(0, 0)
m.update()
m.postcascade('Cascade')
m.update()
self.assertTrue(submenu.winfo_ismapped())
# A non-cascade index unposts the currently posted submenu.
m.postcascade(1)
m.update()
self.assertFalse(submenu.winfo_ismapped())
m.unpost()

def check_entry_option(self, m, index, option, value, expected=None):
if expected is None:
expected = value
Expand Down
34 changes: 34 additions & 0 deletions Lib/tkinter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,32 @@ def tk_setPalette(self, *args, **kw):
self.tk.call(('tk_setPalette',)
+ _flatten(args) + _flatten(list(kw.items())))

def tk_scaling(self, number=None, *, displayof=0):
"""Query or set the scaling factor used by Tk to convert between
physical units and pixels.

The scaling factor is the number of pixels per point on the display,
where a point is 1/72 inch. With no argument, return the current
factor; otherwise set it to the floating-point NUMBER."""
args = ('tk', 'scaling') + self._displayof(displayof)
if number is not None:
self.tk.call(args + (number,))
else:
return self.tk.getdouble(self.tk.call(args))

def tk_inactive(self, reset=False, *, displayof=0):
"""Return the number of milliseconds since the last time the user
interacted with the system, or -1 if the windowing system does not
support this.

If RESET is true, reset the inactivity timer to zero instead and
return None."""
args = ('tk', 'inactive') + self._displayof(displayof)
if reset:
self.tk.call(args + ('reset',))
else:
return self.tk.getint(self.tk.call(args))

def wait_variable(self, name='PY_VAR'):
"""Wait until the variable is modified.

Expand Down Expand Up @@ -3725,6 +3751,14 @@ def post(self, x, y):
"""Display a menu at position X,Y."""
self.tk.call(self._w, 'post', x, y)

def postcascade(self, index):
"""Post the submenu of the cascade entry at INDEX, unposting any
previously posted submenu.

Has no effect if INDEX does not name a cascade entry or if this menu
is not posted."""
self.tk.call(self._w, 'postcascade', index)

def type(self, index):
"""Return the type of the menu item at INDEX."""
return self.tk.call(self._w, 'type', index)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Add the :meth:`tkinter.Menu.postcascade` method and the
:meth:`!tkinter.Misc.tk_scaling` and :meth:`!tkinter.Misc.tk_inactive`
methods, wrapping the ``postcascade``, ``tk scaling`` and ``tk inactive``
Tk commands.
Loading