0

I tried to figure out how the event queue is processed in tkinter. I found this link once and since I played around but I observed different behavior.


The top of my code is:

import tkinter as tk
from tkinter import ttk

def p_trace(*tkargs):
    return print('trace', var.get())
def p_binding(event):
    return print(event.type,var.get())

root = tk.Tk()
var = tk.IntVar(root)
var.trace_add('write',p_trace)

The p_after is on different location in the print order by using 0 ms.

def after_done():
    return print('after',var.get())

check3= ttk.Checkbutton(root,text='event/trace/after',
                       command=lambda t=0:root.after(t,after_done),
                       variable=var)
check3.bind('<Button-1>',p_binding)
check3.pack()

The expected order would be for me, based on the link I have found, like this:

  1. ButtonPress 0
  2. trace 1
  3. after 1
  4. ButtonPress 1
  5. trace 0
  6. after 0

But if you click on the button, like gambling, the actual order can be sometimes:

  1. ButtonPress 1
  2. trace 0
  3. ButtonPress 0
  4. after 0
  5. trace 1
  6. after 1

The p_pseudo should be always on top via when='head' but it isn't, never:

def p_pseudo(*tkargs):
    return print('pseudo',var.get())

check2= ttk.Checkbutton(root,text='event/trace/pseudo',
                       command=lambda:check2.event_generate('<<pseudo_event>>',when='head'),
                       variable=var)
check2.bind('<Button-1>',p_binding)
check2.bind('<<pseudo_event>>',p_pseudo)
check2.pack()

The p_after with after_idle gives also strange output.

def after_done():
    return print('after',var.get())

check4= ttk.Checkbutton(root,text='event/trace/after',
                       command=lambda:root.after_idle(after_done),
                       variable=var)
check4.bind('<Button-1>',p_binding)
check4.pack()

This seems like arbitrary behavior and makes it in some of my cases hard to have the right order of events. I need to slow my program down for secure execution. Is it possible to determinate the event queue of tkinter or its not likely?

enter image description here enter image description here

martineau
  • 112,593
  • 23
  • 157
  • 280
Thingamabobs
  • 4,666
  • 3
  • 8
  • 33
  • It's not really clear what you're asking, partly because none of the code you posted generates the output that you posted, making it hard to correlate the two. That being said, if you're only asking if you can get a list of pending events, the answer is no. – Bryan Oakley Nov 11 '21 at 17:24
  • @BryanOakley when I press the button it occures sometime, reproducable by clicking the button as fast as you can. But its not needed, see images. The event_generate dosent do what I want at all. – Thingamabobs Nov 11 '21 at 17:30
  • _"when I press the button it occures sometime,"_ what is "it" in this sentence? What occurs? – Bryan Oakley Nov 11 '21 at 17:31
  • That the events are in different order, but mostly in a determinated order. So its messed up for some reason. – Thingamabobs Nov 11 '21 at 17:32
  • Disable the button after being clicked until 'everything' is done to be ready again. – Maurice Meyer Nov 11 '21 at 17:52
  • @MauriceMeyer could be a choice and in some cases I do so. But as you can see, it may happens in the first place. See the first picture for instance. – Thingamabobs Nov 11 '21 at 17:58

1 Answers1

2

To order the Event Queue of tkinter one have either to bypass all events and trigger them in the prefered order. Or works with a specific type of event, means a sepecific flag. Otherwise tkinters Notifier will process the order of events like below. Another solution for people of tcl knowlege would be to write a own Notifier like described here.

Tcl_Notifier
  |
  |
  |

Tcl_DoOneEvent ------+----> get_first/oldest from Tcl_WINDOW_EVENTS (processes msg of WM)
  ^                  |
  |                  +----> get_first/oldest from Tcl_FILE_EVENTS (tk.Tk.createfilehandler)
  |                  |
  |                  +----> get_first/oldest from Tcl_TIMER_EVENTS (after)
  |                  |
  |                  +----> get_first/oldest from Tcl_IDLE_EVENTS (after_idle)
  |                  |
  |                  +----> get_first/oldest from Other event sources (virtual events)
  |                  |
  |                  +----> Tcl_SetTimer(time|maxtime) ----> Tcl_WaitForEvent ----+
  |                                                                               |
  +-------------------------------------------------------------------------------+

BTW, there are some additional conclusions:

  • If after maxtime the command Tcl_DoOneEvent cant be invoked there will be a runtime error.
Thingamabobs
  • 4,666
  • 3
  • 8
  • 33