13

In the given example from this post, it was mentioned that if default bindtags are used then event value will not be visible inside definition (there will be lag by one).

There was some explanation regarding class binding.
I am a beginner, so would like to understand the detailed reason.
Can some please explain why it was not working in first case and was working in second case (when order of bindtags is modified).

import Tkinter

def OnKeyPress(event):
 value = event.widget.get()
 string="value of %s is '%s'" % (event.widget._name, value)
 status.configure(text=string)

root = Tkinter.Tk()

entry1 = Tkinter.Entry(root, name="entry1")
entry2 = Tkinter.Entry(root, name="entry2")
entry3 = Tkinter.Entry(root, name="entry3")

entry1.bindtags(('.entry1', 'Entry', '.', 'all'))
entry2.bindtags(('Entry', '.entry1', '.', 'all'))
entry3.bindtags(('.entry1','Entry','post-class-bindings', '.', 'all'))

btlabel1 = Tkinter.Label(text="bindtags: %s" % " ".join(entry1.bindtags()))
btlabel2 = Tkinter.Label(text="bindtags: %s" % " ".join(entry2.bindtags()))
btlabel3 = Tkinter.Label(text="bindtags: %s" % " ".join(entry3.bindtags()))
status = Tkinter.Label(anchor="w")

entry1.grid(row=0,column=0)
btlabel1.grid(row=0,column=1, padx=10, sticky="w")
entry2.grid(row=1,column=0)
btlabel2.grid(row=1,column=1, padx=10, sticky="w")
entry3.grid(row=2,column=0)
btlabel3.grid(row=2,column=1, padx=10)
status.grid(row=3, columnspan=2, sticky="w")
entry1.bind("<KeyPress>", OnKeyPress)
entry2.bind("<KeyPress>", OnKeyPress)
entry3.bind_class("post-class-bindings", "<KeyPress>", OnKeyPress)

root.mainloop()
Community
  • 1
  • 1
sarbjit
  • 3,786
  • 9
  • 38
  • 60

1 Answers1

28

When you do a binding on a widget, you aren't actually binding to a widget per se. When you do mywidget.bind(...), what is actually happening is that the binding is associated with a bind tag with the same name as the widget.

When an event is detected, Tkinter first figures out which widget intercepted the event. This widget will have a list of zero or more (by default: four) bind tags associated with it. Tkinter will check each tag in order to see if there's a binding that matches the event. If it finds one, it will execute the binding and then continue to the next tag, until it runs out of tags or one of the bound functions returns the string "break".

The sequence looks something like this:

  1. You press the "x" key. At this point the "x" hasn't been inserted anywhere
  2. Tkinter gets the bind tags for the widget that has the keyboard focus.
  3. By default the first bind tag is the widget itself. Is there a binding on that tag? If so, execute it. For example, you might print the contents of the widget. Because no other bindings have fired yet, the "x" will not be part of the contents.
  4. If the bound function returns "break" then no more event processing is done. The "x" will not get inserted into the widget.
  5. If the widget did not return "break", tkinter proceeds to the next bind tag.
  6. By default, the next bind tag is the widget class. Is there a binding on the class that matches this event (a keypress of the letter "x")?
  7. In this case the class does have a binding for this event, which is to actually insert the "x" into the widget. Until this point, the "x" still hasn't been inserted into the widget. After this binding, however, if you did a print of the contents you would see the "x"
  8. Processing continues in this manner until all bind tags have been processed.

Based on the ongoing discussion in the comment section, it appears this is still unclear. I'll try to make this as simple as possible:

It is the class binding which copies a character from the in-memory event object to the widget and thus causing it to appear on screen. Before the class binding fires, the character will not appear in the widget. After the class binding it will be in the widget.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • 1
    Thanks for the explanation here. One question here why the letter "x" is visible only when bindtag corresponding to widget class is used and not when the widget instance bindtag is used. I mean why is it onls if widget is called, this value is visible. – sarbjit Jul 18 '12 at 13:21
  • As I explained, the "x" is inserted when the class binding fires, it isn't magically inserted by the OS or Tkinter before any bindings. The class binding is the mechanism for inserting the "x". If you have bindings that fire before the class bindings, the "x" is simply not there because the thing that puts it there hasn't been executed yet. – Bryan Oakley Jul 18 '12 at 16:20
  • Got it!! Just one doubt. When bindtags are processed for default case, first widget instance binding will be evaluated and as it doesn't have any break statement, next binding which is CLASS will be processed, So why here the value is not printed? I mean if there is no break statement, all the bindtags should be processed for one event and that includes CLASS binding as well. – sarbjit Jul 19 '12 at 07:04
  • @sarbjit: don't understand the question. If there is no break statement, the value _will_ be printed when the class binding fires (assuming you modified the class bindings to do a print, or do the print after the class binding). Only after the class binding fires will the widget value have the character for the key that was just pressed. – Bryan Oakley Jul 19 '12 at 11:12
  • What i meant was, you mentioned that all the bindtags will be evaluated i.e. " widget instance, class, . , all". Now my question is when i pressed any character "x", widget bind tag will be fired, there is no break statement so next bind tag i.e CLASS will be fired and so on.... So when user pressed second character "y", why now "x" is printed on evaluating CLASS binding and why it was not printed when all the bindtags were being evaluated. – sarbjit Jul 19 '12 at 11:45
  • 1
    @sarbjit: for the same reason the "x" wasn't printed the first time. When you press "y", you won't see it until that event has been processed by the class bindings. When you order ice cream (press "x") in a cup (the widget), the cup is empty until the server (the class binding) puts a scoop in the cup. You ask for another scoop (press "y") and immediately look in the cup, you will only see the first scoop until the server (again, the "class") has a chance to put a second scoop in the cup. – Bryan Oakley Jul 19 '12 at 12:39
  • Here is my understanding:: When class binding is fired on pressing first character "x", it will just keep value "x" in memory, and when user presses second character "y", when CLASS binding is fired in sequence after widget instance binding, "x" character is printed. So in nutshell "CLASS" binding will be evaluated every time on event (Character is inserted), its just it will process pvs value on binding of CLASS of next event. – sarbjit Jul 19 '12 at 13:08
  • 2
    @sarbjit: What you wrote is wrong. You are over-complicating things. When the class binding is fired, that character is inserted into the widget. There's no "in memory" copy or some problem with "pvs" (previous?) value. Before the class binding, the character is not in the widget. After the class binding, it is. It's just that simple. – Bryan Oakley Jul 19 '12 at 14:15
  • I understand, I was just confused. – sarbjit Jul 19 '12 at 14:43