msg397199 - (view) |
Author: Akuli (Akuli) |
Date: 2021-07-09 15:14 |
The purpose of focus_get() is to return the widget that currently has the focus. It tries to convert its result to a tkinter widget, which can fail, because not all Tk widgets are known to tkinter. Consider this, for example:
import tkinter
def print_focused_widget():
print(repr(root.focus_get()))
root.after(1000, print_focused_widget)
root = tkinter.Tk()
menu = root["menu"] = tkinter.Menu()
menu.add_cascade(label="Click here", menu=tkinter.Menu())
print_focused_widget()
tkinter.mainloop()
Output, with menu clicked after a couple seconds (on Linux):
None
<tkinter.Tk object .>
<tkinter.Tk object .>
Exception in Tkinter callback
Traceback (most recent call last):
File "/home/akuli/.local/lib/python3.10/tkinter/__init__.py", line 1916, in __call__
return self.func(*args)
File "/home/akuli/.local/lib/python3.10/tkinter/__init__.py", line 838, in callit
func(*args)
File "/home/akuli/porcu/foo.py", line 4, in print_focused_widget
print(repr(root.focus_get()))
File "/home/akuli/.local/lib/python3.10/tkinter/__init__.py", line 782, in focus_get
return self._nametowidget(name)
File "/home/akuli/.local/lib/python3.10/tkinter/__init__.py", line 1531, in nametowidget
w = w.children[n]
KeyError: '#!menu'
Some nametowidget() calls in tkinter/__init__.py already handle this correctly. Consider winfo_children(), for example:
try:
# Tcl sometimes returns extra windows, e.g. for
# menus; those need to be skipped
result.append(self._nametowidget(child))
except KeyError:
pass
|
msg397200 - (view) |
Author: Akuli (Akuli) |
Date: 2021-07-09 15:16 |
Forgot to mention: The correct fix IMO would be to return None when a KeyError occurs. This way code like `focus_get() == some_tkinter_widget` would always do the right thing, for example.
|
msg397211 - (view) |
Author: E. Paine (epaine) * |
Date: 2021-07-09 19:44 |
I agree with Akuli that raising a KeyError is not expected behaviour (combined with the fact this is caught elsewhere), and therefore is probably a regression.
While we could use `winfo class` to determine the type of Tk widget, this would probably require a reasonably sized refactor of tkinter (and we would still need to support cases when it's a type we don't know). Therefore, I think returning `None` is the best solution.
Akuli, would you like to create a pull request for this?
|
msg397213 - (view) |
Author: E. Paine (epaine) * |
Date: 2021-07-09 19:50 |
Sorry, I should specify that we would use `winfo class` in order to then return a new tkinter object for the existing Tk widget (which currently cannot be done)
|
msg397216 - (view) |
Author: Serhiy Storchaka (serhiy.storchaka) *  |
Date: 2021-07-09 20:27 |
It is a duplicate of issue734176.
|
msg397217 - (view) |
Author: Akuli (Akuli) |
Date: 2021-07-09 20:33 |
I found issue734176 before I created this. It is NOT a duplicate. While issue734176 is about menus, this one is about focus_get(), and not necessarily related to menus. In fact, I initially noticed this with an "open file" dialog, not with a menu.
I'm not putting my address into your CLA, thank you very much.
|
msg397220 - (view) |
Author: Terry J. Reedy (terry.reedy) *  |
Date: 2021-07-09 20:46 |
Akuli, what tk widgets do you think are not known to tkinter? In any case, tk menu is known to tkinter.
I cannot reproduce when running on Windows with 3.10.0b3: Add "print(root.children)" (after add_cascade) results in {'!menu': <tkinter.Menu object .!menu>, '!menu2': <tkinter.Menu object .!menu2>}. The names are created in tkinter.py lines 2564-2573.
I then see 'None' once and then '<tkinter.Tk object .>' indefinitely even while hovering over and clicking 'click me' and the dropdown. If I click outside the tk box, the print returns to 'None'.
Maybe there is an OS difference in what is considered to have 'focus'.
Key '#!menu' looks like '!menu' with '#' prepended. Someone could try changing the tkinter code referenced above and see if the change appears in the bad key. Also check the contents of root.children.
|
msg397222 - (view) |
Author: Terry J. Reedy (terry.reedy) *  |
Date: 2021-07-09 21:02 |
I am not quite convinced that this is a duplicate of #734176. The latter is about tearoff clones and nothing is cloned here. But I do notice that number 'names were also prefixed with '#'. What happens if 'tearoff=0' is added to the cascade so that it is not even clonable. The tkinter naming of instances after the class was added less than 10 years ago.
|
msg397224 - (view) |
Author: Akuli (Akuli) |
Date: 2021-07-09 21:03 |
Unfortunately I don't know any real-world examples of this on Windows. The open file dialog works very differently on Windows: it uses the native Windows dialog, whereas on Linux, it's implemented in Tcl.
Meanwhile, here's a platform-independent toy example:
import tkinter
root = tkinter.Tk()
root.tk.eval("""
entry .e
pack .e
focus .e
""")
root.after(500, root.focus_get)
root.mainloop()
Also, thanks for reopening!
|
msg397681 - (view) |
Author: Terry J. Reedy (terry.reedy) *  |
Date: 2021-07-17 02:25 |
Traceback (most recent call last):
File "C:\Programs\Python310\lib\tkinter\__init__.py", line 1921, in __call__
return self.func(*args)
File "C:\Programs\Python310\lib\tkinter\__init__.py", line 839, in callit
func(*args)
File "C:\Programs\Python310\lib\tkinter\__init__.py", line 783, in focus_get
return self._nametowidget(name)
File "C:\Programs\Python310\lib\tkinter\__init__.py", line 1536, in nametowidget
w = w.children[n]
KeyError: 'e'
Is catching KeyError in the following
try:
# Tcl sometimes returns extra windows, e.g. for
# menus; those need to be skipped
result.append(self._nametowidget(child))
except KeyError:
pass
really correct? It appears to skip things that *can* get focus by key or mouse action. But what choice is there?
Silently failing when asked to focus on something is even less obviously correct. For 'widget = root.focus_get' to assign None to widget is not obviously useful as it likely just delays the error.
|
msg397726 - (view) |
Author: Akuli (Akuli) |
Date: 2021-07-17 18:17 |
Here are the options:
- Do nothing. My program will error in some corner cases.
- Change it to return `None`, so `widget.focus_get() is not None` no longer means "this application doesn't have focus", but rather "this application doesn't have focus or the focused widget was not created in tkinter". Note that `focus_get()` can already return None, and we would just add one more situation where it does so.
- Change tkinter so that it doesn't matter whether a widget was created in tkinter or not. This doesn't seem to be easy.
|
msg397727 - (view) |
Author: Akuli (Akuli) |
Date: 2021-07-17 18:19 |
Typo in previous message: I meant `widget.focus_get() is None`. It currently means "this application doesn't have focus", while `is not None` currently means "this application has focus".
|
|
Date |
User |
Action |
Args |
2022-04-11 14:59:47 | admin | set | github: 88758 |
2021-07-17 18:19:16 | Akuli | set | messages:
+ msg397727 |
2021-07-17 18:17:08 | Akuli | set | messages:
+ msg397726 |
2021-07-17 02:25:48 | terry.reedy | set | messages:
+ msg397681 |
2021-07-09 21:03:13 | Akuli | set | status: closed -> open resolution: duplicate -> messages:
+ msg397224
|
2021-07-09 21:02:12 | terry.reedy | set | status: open -> closed superseder: Make Tkinter.py's nametowidget work with cloned menu widgets messages:
+ msg397222
resolution: duplicate stage: resolved |
2021-07-09 20:46:54 | terry.reedy | set | status: closed -> open
superseder: Make Tkinter.py's nametowidget work with cloned menu widgets -> (no value)
nosy:
+ terry.reedy messages:
+ msg397220 resolution: duplicate -> (no value) stage: resolved -> (no value) |
2021-07-09 20:33:16 | Akuli | set | messages:
+ msg397217 |
2021-07-09 20:27:25 | serhiy.storchaka | set | status: open -> closed superseder: Make Tkinter.py's nametowidget work with cloned menu widgets messages:
+ msg397216
resolution: duplicate stage: resolved |
2021-07-09 19:50:59 | epaine | set | messages:
+ msg397213 |
2021-07-09 19:44:29 | epaine | set | nosy:
+ serhiy.storchaka, epaine
messages:
+ msg397211 versions:
+ Python 3.9, Python 3.11 |
2021-07-09 15:16:04 | Akuli | set | messages:
+ msg397200 |
2021-07-09 15:14:18 | Akuli | create | |