0

In the code below I bind keys to a text widget. Depending on the way the binding is done I get a different result when an 'x' is typed in the widget.

Why is there a difference in the output between bind and bind_all? How should I use all three variants so they all give the same result?

import tkinter as tk

class Q_bind(tk.Text):
    def __init__(self):
        tk.Text.__init__(self)

        self.bind("<Key>", self._insert_a)    #-> 'a\nx' with break a!
        #self.bind_class("<Key>", self._insert_a) # -> 'x' Replace with:
        #self.bind_class("Text", "<Key>", self._insert_a) # -> 'a'!!
        #self.bind_all("<Key>", self._insert_a)   # -> 'xa\n'
        print(self.bindtags())           #shows the bind-tags

    def _insert_a(self, event=None):
        print(event.char)
        self.insert('end' ,"a\n")
        return 'break'              #added

if __name__ == "__main__":
    app = Q_bind()
    app.pack(fill="both", expand='y')
    app.master.geometry('400x400')
    app.mainloop()
ingo
  • 117
  • 10
  • different bindings are for different purpose and they work in different way (they can be executed in different moment when `mainloop` checks bindings). http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm – furas Feb 12 '16 at 11:17
  • 1
    btw: it should be `self.bind_class("Text", "", self._insert_a)` - with class name "Text" – furas Feb 12 '16 at 11:21
  • 2
    Does this answer your question? http://stackoverflow.com/a/11542200/7432 – Bryan Oakley Feb 12 '16 at 12:17
  • Not completely @Bryan. After reading the explanation I changed the sample (@furas) and now both bind and bind_class work as I expected. I'll try: press x goes to the widget level and normally invokes the 'x', but as it encounters a break it does nothing and goes to the 'next' level, being the class binding. That will print the "a". "x" is lost on the way. Bind_all ?? – ingo Feb 12 '16 at 14:43

1 Answers1

2

You cannot use all three bindings and get the same result1 because the result is dependent on the ordering of the widget bind tags.

Every widget has an ordered set of bind tags. Bindings are actually attached to these tags rather than the actual widget. It just so happens that the first bind tag is the name of the widget.

By default a widget has the following bind tags in the following order:

  1. the widget itself
  2. the widget class (internal tk class, not tkinter class)
  3. the widget toplevel (or root window)
  4. "all"

When an event occurs on a widget, the following four things happen:

  1. if the widget has a binding that matches the event, process that binding
  2. if the widget class has a binding that matches the event, process that binding
  3. if the widget's toplevel (or root) has a binding that matches the event, process that binding
  4. if there is a binding on "all", process that event.

At any time, if a binding returns the literal string "break", no more bindings will be processed. Without returning "break", all four of the event handlers are processed. In the case of your widget binding returning "break", the class, toplevel and "all" bindings won't be processed.

In the specific case of binding <Key> with bind_all, this is what happens:

  1. the widget has no binding that matches the event, so nothing is done
  2. the widget class has a binding, so it is processed. In this case a letter for the key is inserted into the widget
  3. the widget's toplevel/root has no binding, so nothing is done
  4. the "all" tag has a binding so it will insert "a\n".

Because the binding for "all" happens after the binding for the widget class, returning "break" has no effect, and cannot prevent the default behavior of inserting an "a" in addition to your custom behavior.

Bind tags are a stunningly beautiful way of handling events. I've used at least half a dozen different GUI toolkits in several different languages over the course of many years, and nothing comes close to the power of tk's bind tags mechanism.


1 You actually can get the same behavior for all three by adding or removing bind tags, or changing the order of the bind tags. You do this with the bindtag method. There is rarely a need to do so, but it is remarkably handy for some special cases.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thank you @Bryan for a real explanation, with this I can visualise the process although it may take some more time to really digest. Much appreciated. – ingo Feb 12 '16 at 16:10