when tk().event_generate('<Button-1>', x, y) on a top level frame/window does the click get applied to the lower level widgets e.g. Button?
Question:
I have access to the top level tkinter window, but when I do:
self._root = tk.Tk()
<snip>
x = 10 # in screen coordinates
y = 20 # in screen coordinates
self._root.event_generate('<Button-1>', x=x, y=y)
self._root.event_generate('<ButtonRelease-1>', x=x, y=y)
I expect the button click to be applied to the widget underneath location x,y on the window. In this example a Button.
My understanding is event_generate places an event on the message queue inside tkinter just like a real mouse click would do. Normally clicking anywhere inside a window or a frame, causes the click to go through the top-level panes until it finds a widget with a bind() associated with it, i.e. a Button.
And so using that I should be able to simulate a button click anywhere on the window without moving the actual mouse.
But it doesn’t do anything, no click, no error, no anything.
What am I missing?
Answers:
event_generate
is not the same as simulating a click by the user. Instead, you’re explicitly specifying the window that is to receive the event.
If you need to simulate the actions of a user, you can use the method winfo_containing
which will return the widget at a given coordinate. You can then pass that widget to event_generate
.
With @Bryan Oakley’s excellent help, I was able to get this to work:
# the top level window
self._root = tk.Tk()
<snip>
x = 10 # in screen coordinates
y = 20 # in screen coordinates
# get a reference to the widget in the root window that is
# underneath coordinate x,y
w = self._root.winfo_containing(x, y)
# Note: the <Enter> is necessary. Without it the
# button events don't do anything.
w.event_generate('<Enter>', x=x, y=y)
w.event_generate('<Button-1>', x=x, y=y)
w.event_generate('<ButtonRelease-1>', x=x, y=y)
# The invoke() also works.
# w.invoke()
Note: the widget in this sample is a Button. Here’s a snippet of code for an Entry widget:
x = 30
y = 40
w = self._root.winfo_containing(x, y)
print(f'DBG {w.winfo_class()}') # should be 'Entry'
msg = 'abcd'
w.focus_set()
for ch in msg:
w.event_generate(f'<KeyPress-{ch}>')
w.update()
There is much more to be done here e.g. checking how special characters are done e.g. backspace, tab, enter key, ctrl and alt keys etc. But it’s a start.
I have access to the top level tkinter window, but when I do:
self._root = tk.Tk()
<snip>
x = 10 # in screen coordinates
y = 20 # in screen coordinates
self._root.event_generate('<Button-1>', x=x, y=y)
self._root.event_generate('<ButtonRelease-1>', x=x, y=y)
I expect the button click to be applied to the widget underneath location x,y on the window. In this example a Button.
My understanding is event_generate places an event on the message queue inside tkinter just like a real mouse click would do. Normally clicking anywhere inside a window or a frame, causes the click to go through the top-level panes until it finds a widget with a bind() associated with it, i.e. a Button.
And so using that I should be able to simulate a button click anywhere on the window without moving the actual mouse.
But it doesn’t do anything, no click, no error, no anything.
What am I missing?
event_generate
is not the same as simulating a click by the user. Instead, you’re explicitly specifying the window that is to receive the event.
If you need to simulate the actions of a user, you can use the method winfo_containing
which will return the widget at a given coordinate. You can then pass that widget to event_generate
.
With @Bryan Oakley’s excellent help, I was able to get this to work:
# the top level window
self._root = tk.Tk()
<snip>
x = 10 # in screen coordinates
y = 20 # in screen coordinates
# get a reference to the widget in the root window that is
# underneath coordinate x,y
w = self._root.winfo_containing(x, y)
# Note: the <Enter> is necessary. Without it the
# button events don't do anything.
w.event_generate('<Enter>', x=x, y=y)
w.event_generate('<Button-1>', x=x, y=y)
w.event_generate('<ButtonRelease-1>', x=x, y=y)
# The invoke() also works.
# w.invoke()
Note: the widget in this sample is a Button. Here’s a snippet of code for an Entry widget:
x = 30
y = 40
w = self._root.winfo_containing(x, y)
print(f'DBG {w.winfo_class()}') # should be 'Entry'
msg = 'abcd'
w.focus_set()
for ch in msg:
w.event_generate(f'<KeyPress-{ch}>')
w.update()
There is much more to be done here e.g. checking how special characters are done e.g. backspace, tab, enter key, ctrl and alt keys etc. But it’s a start.