I want to bind self events after Text
widget class bindings, in order to change the text of the widget when my binding function is called. My binding, for example self.text.bind("<Key>", self.callback)
, is called before the content in Text widget changes.
What is happening in your case is that your binding to print the value happens before the class binding, and it's the class binding that actually takes user input and puts it in the widget. There are several ways to solve this problem. You could bind to <KeyRelease>
instead of <KeyPress>
, or you could use the built-in entry validation features to have your code called on every key press. With that solution you'll be given all the data you need -- the value before the change, the value after the change, the key that was pressed, etc.
Another choice is to change the order in which events are processed. Since your question specifically asked how to change the order, that is what I will address.
Even though a binding appears to be associated with a widget when you do something like entry.bind(...)
, you're actually assigning a binding to a "bind tag" (or "bindtag"). By default each widget has a bindtag that is the same as the name of the widget. Other bindtags include the class of a widget (for example, "Entry"), the path of the root window (eg: ".") and the special tag "all". Widgets are assigned a set of bindtags which are processed in order when an event is received. The default order goes from most- to least-specific: widget, class, toplevel, all.
There are a couple ways to manipulate the bindtags to get the result you desire. One choice is to rearrange the order of the bindtags. By moving the bindtag that represents the widget to be after the bindtag representing the class, the class will handle the event before passing it on to the specific widget.
Another choice is to add an additional bindtag that is after the class binding, and then put your bindings on this tag rather than on the tag that represents the widget.
Why choose one over the other? By rearranging the order you will affect all bindings on that widget. If you have many bindings and some depend on the order (so that the can, for example, disallow certain keystrokes), changing the order may cause those bindings to stop working.
By introducing a new bindtag, you can choose which bindings happen before class bindings and which happen after.
In the following code I create three entry widgets. The first uses the default set of bindtags (explicitly set in the example, though they are identical to the default). The second changes the order, and the third introduces an additional bindtag. Run the code then press a key while the focus is in each window. Notice that in the first entry widget the binding always seems to be one character behind. Again, this is because the widget binding happens before the class binding puts the character into the widget.
In the second and third examples, the binding happens after the class binding so the function sees the change in the widgets.
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")
# Three different bindtags. The first is just the default but I'm
# including it for illustrative purposes. The second reverses the
# order of the first two tags. The third introduces a new tag after
# the class tag.
entry1.bindtags(('.entry1', 'Entry', '.', 'all'))
entry2.bindtags(('Entry', '.entry2', '.', 'all'))
entry3.bindtags(('.entry3','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")
# normally you bind to the widget; in the third case we're binding
# to the new bindtag we've created
entry1.bind("<KeyPress>", OnKeyPress)
entry2.bind("<KeyPress>", OnKeyPress)
entry3.bind_class("post-class-bindings", "<KeyPress>", OnKeyPress)
root.mainloop()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With