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?

Asked By: JohnA

||

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.

Answered By: Bryan Oakley

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.

Answered By: JohnA
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.