Unexpected order of tkinter event_generate and update commands

I am trying to get tkinter event_generate to work with update for atomic unittesting.

The following code does not work as I expect. "BackSpace event generated." not printed. I understand that event_generate puts the event in the tkinter event queue, and then update should clear and execute all the events in the queue.

 class UpdWin(tk.Tk): def __init__(self): super().__init__() self.bind('<BackSpace>', lambda event: print(event.keysym, 'event generated.')) app = UpdWin() app.event_generate('<BackSpace>') app.update() # Update doesn't work if placed here 

However, the following code prints the "BackSpace" event. Events in the event queue are cleared and executed during __init__ . After that, the main code places the event in the tkinter queue.

 class UpdWin(tk.Tk): def __init__(self): super().__init__() self.bind('<BackSpace>', lambda event: print(event.keysym, 'event generated.')) self.update() # Update works if placed here app = UpdWin() app.event_generate('<BackSpace>') 

My first thought was that my understanding of the update command should have been wrong. Lundh, Shipman and TclCmd pages have different entries for update .

Process all pending events, call event callbacks, complete any pending geometry control, redraw widgets if necessary, and call all pending inactivity tasks. Lundh (1999)

This method forces the display to be updated. Shipman (New Mexico Tech)

This command is used to bring the application to the "actual" state by entering the event loop several times until all pending events (including callbacks) are processed. TclCmd man page

From this, I suspected an undocumented time issue, and I tried third place for the update team. The following code also works.

 class UpdWin(tk.Tk): def __init__(self): super().__init__() self.bind('<BackSpace>', lambda event: print(event.keysym, 'event generated.')) app = UpdWin() app.update() # Update works if placed here app.event_generate('<BackSpace>') 

Why does tkinter update work when it called before event_generate , but not after?

Edit

The following code will print the message "BackSpace event generated". if app.update is in position 1.
He will print a "FocusIn event." if app.update is in position 2.
NB: Brian Oakley's answer says this effect may be machine dependent.

 import tkinter as tk app = tk.Tk() # app.update() # Position 1 app.bind('<FocusIn>', lambda event: print('FocusIn event generated.')) app.bind('<BackSpace>', lambda event: print(event.keysym, 'event generated.')) app.event_generate('<BackSpace>') app.update() # Position 2 

The following code using when='tail' never prints a "BackSpace event". whether app.update in position 1 or 2.

 import tkinter as tk app = tk.Tk() app.update() # Position 1 app.bind('<FocusIn>', lambda event: print('<FocusIn> event generated.')) app.bind('<BackSpace>', lambda event: print(event.keysym, 'event generated.')) app.event_generate('<BackSpace>', when="tail") # app.update() # Position 2 
+1
source share
1 answer

I can’t duplicate your observations, so I can’t say exactly what is happening. Obviously, there is little code that is missing, and the platform you are working on could also potentially affect this, and how you run the program.

However, I think the problem boils down to three factors:

  • By default, event_generate immediately processes any event handlers for the event (i.e. update and mainloop are optional)
  • Without calling update() before calling event_generate , the window will not be drawn, and when the window is not drawn, the OS or the window manager will not use it to focus on the keyboard.
  • If the application does not have keyboard focus, Tkinter will probably ignore keyboard events.

This may not be entirely true - as you say which window the event should receive, focus may not be the subject of consideration. However, window visibility can still play a role. Maybe the tkinter logic says to ignore events on invisible windows. It is also possible that your window manager / OS may not start the program in priority mode - the keyboard focus may be in some other application until you manually click in the window (for example, this is similar to OSX behavior).

You can try using the when argument to event_generate . This allows you to delay the processing of handlers until any other events in the queue are processed. Do this and then call update() and maybe your code will work. For instance:

 app.event_generate('<Backspace>`, when="tail") app.update() 

You can also try to get the application to the front with a call to app.focus_force() before trying to generate events.

When generating keyboard events, you can also <FocusIn> event first. When you start generating events, you need to make sure that the events you generate behave as user events as possible, which means you need to know about visibility, keyboard focus, and mouse focus. Many years ago, when I went this way, I remember that I had to generate <Enter> and <Leave> events when processing buttons, for example, since the built-in bindings sometimes depended on these settings when the button state was "active".

The final documentation for the event_generate is the Tcl / Tk man page .

+2
source

Source: https://habr.com/ru/post/979980/


All Articles