[3.13] gh-88758: Handle non-tkinter widgets in tkinter focus methods (GH-152337) (GH-152348)

focus_get(), focus_displayof(), focus_lastfor() and winfo_containing()
now return None instead of raising KeyError when the focused widget was
not created by tkinter (for example a torn-off menu).
(cherry picked from commit 5fed5ce85d)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Miss Islington (bot) 2026-06-27 01:31:09 +02:00 committed by GitHub
parent 7a835cbbfb
commit cee72326ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 36 additions and 4 deletions

View file

@ -416,6 +416,22 @@ def test_focus_methods(self):
self.root.update()
self.assertIs(self.root.focus_get(), b)
def test_focus_methods_unresolvable(self):
# The focus may be on a widget that tkinter did not create and so
# cannot map to an instance (e.g. a torn-off menu). The focus
# methods return None instead of raising KeyError (gh-88758).
menu = tkinter.Menu(self.root, tearoff=1)
menu.add_command(label='Hello')
tearoff = self.root.tk.call('tk::TearOffMenu', str(menu), 0, 0)
self.addCleanup(self.root.tk.call, 'destroy', tearoff)
self.root.update()
self.assertRaises(KeyError, self.root.nametowidget, tearoff)
self.root.tk.call('focus', '-force', tearoff)
self.root.update()
self.assertIsNone(self.root.focus_get())
self.assertIsNone(self.root.focus_displayof())
def test_grab(self):
f = tkinter.Frame(self.root)
f.pack()

View file

@ -803,7 +803,10 @@ def focus_get(self):
the focus."""
name = self.tk.call('focus')
if name == 'none' or not name: return None
return self._nametowidget(name)
try:
return self._nametowidget(name)
except KeyError:
return None
def focus_displayof(self):
"""Return the widget which has currently the focus on the
@ -812,14 +815,20 @@ def focus_displayof(self):
Return None if the application does not have the focus."""
name = self.tk.call('focus', '-displayof', self._w)
if name == 'none' or not name: return None
return self._nametowidget(name)
try:
return self._nametowidget(name)
except KeyError:
return None
def focus_lastfor(self):
"""Return the widget which would have the focus if top level
for this widget gets the focus from the window manager."""
name = self.tk.call('focus', '-lastfor', self._w)
if name == 'none' or not name: return None
return self._nametowidget(name)
try:
return self._nametowidget(name)
except KeyError:
return None
def tk_focusFollowsMouse(self):
"""The widget under mouse will get automatically focus. Can not
@ -1222,7 +1231,10 @@ def winfo_containing(self, rootX, rootY, displayof=0):
+ self._displayof(displayof) + (rootX, rootY)
name = self.tk.call(args)
if not name: return None
return self._nametowidget(name)
try:
return self._nametowidget(name)
except KeyError:
return None
def winfo_depth(self):
"""Return the number of bits per pixel."""

View file

@ -0,0 +1,4 @@
:meth:`!tkinter.Misc.focus_get`, :meth:`!focus_displayof`,
:meth:`!focus_lastfor` and :meth:`!winfo_containing` now return ``None``
instead of raising :exc:`KeyError` when the widget was not created by
:mod:`tkinter` (for example a torn-off menu).