How to Bind Self Events in Tkinter Text Widget After It Will Binded by Text Widget

How to bind self events in Tkinter Text widget after it will binded by Text widget?

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()

Call tkinter.Text binds after the text is inputted

You should insert the closing ) at the insertion position and then move the insertion cursor back 1 character:

def close_parenthese(event):
logbox.insert("insert", ")") # add the closing )
logbox.mark_set("insert", "insert-1c") # move back the insertion cursor

text_box.bind("(", close_parenthese)

Suppressing insert of tkinter Text while still allowing other binds

What inserts the tab after your binding is the class binding of the Text widget. You can override this class binding to prevent this without affecting any direct binding (which is executed before the class binding)

 <any_tk_widget>.bind_class("Text", "<Tab>", tabkey)

However, the new class binding tabkey() will be executed everytime tab is pressed in any Text widgets. So if you also use normal Text widgets in your project you need to also keep the usual behavior for them:

def tabkey(event):
if isinstance(event.widget, BaseText):
return "break" # don't insert tab
# standard Text class binding to <Tab>
event.widget.insert("insert", "\t")
event.widget.focus_set()
return "break"

Below is an example where you can compare the behavior of the BaseText and Text widgets when binding the insertion of "TAB" when the user presses Tab

import tkinter as tk

class BaseText(tk.Text):

def __init__(self, *args, **kwargs):
tk.Text.__init__(self, *args, **kwargs)

def tabkey(event):
if isinstance(event.widget, BaseText):
return "break" # don't insert tab
# standard Text class binding to <Tab>
event.widget.insert("insert", "\t")
event.widget.focus_set()
return "break"

root = tk.Tk()
# override standard Tab class binding
root.bind_class("Text", "<Tab>", tabkey)

bt = BaseText(root)
bt.bind("<Tab>", lambda ev: bt.insert("insert", "TAB"))
bt.pack()
tt = tk.Text(root)
tt.bind("<Tab>", lambda ev: tt.insert("insert", "TAB"))
tt.pack()

EDIT: Here is a self-contained version where the binding is inside the BaseText class with an extra class to show that the functionality is compatible with inheritance.

import tkinter as tk

class BaseText(tk.Text):

_initialized = False

def __init__(self, *args, **kwargs):
tk.Text.__init__(self, *args, **kwargs)

if not self._initialized: # do it only once, not every time a BaseText widget is created
self.bind_class("Text", "<Tab>", self.tabkey) # override standard Tab class binding
BaseText._initialized = True

@staticmethod
def tabkey(event):
if isinstance(event.widget, BaseText):
return "break" # don't insert tab
# standard Text class binding to <Tab>
event.widget.insert("insert", "\t")
event.widget.focus_set()
return "break"

class AdvancedText(BaseText):
def __init__(self, *args, **kwargs):
BaseText.__init__(self, *args, **kwargs)
self.bind("<Tab>", self.custom_tab)

def custom_tab(self, event):
self.insert("insert", "....")

root = tk.Tk()

tk.Label(root, text="tk.Text").pack()

tt = tk.Text(root)
tt.bind("<Tab>", lambda ev: tt.insert("insert", "TAB"))
tt.pack()

tk.Label(root, text="BaseText").pack()
bt = BaseText(root)
bt.bind("<Tab>", lambda ev: bt.insert("insert", "TAB"))
bt.pack()

tk.Label(root, text="AdvancedText").pack()
at = AdvancedText(root)
at.pack()

Tkinter Event Binding

You can change the order in which callbacks for a specific event are processed, but you can't change the order of the events themselves.

There's already a question on Stackoverflow that asks something similar, and it has an answer that includes an example that illustrates how to change the order that callbacks are handled. See this answer to the question How to bind self events in Tkinter Text widget after it will binded by Text widget?

That answer mentions tkinter's bind tags. There's another question that goes into a bit of detail about how bindings are processed. See this answer to the question Basic query regarding bindtags in tkinter

How to bind self events in Tkinter Text widget after it will binded by Text widget?

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()

Alt bindings not working in tkinter text widget

Although it worked for me but if its not working for you then change text.bind("<Control-Alt-d>" , func) to text.bind("<Control-Alt-KeyPress-d>" , func) or maybe you should try again in different directory

Tkinter double-prints characters when binding to Key

Considering my actual program has a few buttons and a canvas, is it a bad idea to bind to root?

Yes, it is a bad idea. Binding to root means that every widget will be affected.

If so, how can I bind it to each widget individually and avoid double printing anything without an empty event.char

Well, you could always bind to each widget individually. It looks like you already tried that, but it's not clear why you think it's not working.

keyboard_entries[idx].bind("<Key>", set_key)

As for how to not get double characters, the problem is that the default bindings to the keys are happening after your bound function is called. The simplest solution is to prevent the original binding from firing is by returning "break" from your function.

Also, you don't need to get the focus in your function because part of the event object is a reference to the widget that received the event.

def set_key(event):
event.widget.delete(0, "end")
if (event.char.split() != []):
event.widget.insert(0, event.char)
else:
event.widget.insert(0, event.keysym)
return "break"

For a longer explanation of events and bindings see this answer: http://stackoverflow.com/a/11542200/7432

Python Tkinter on key -Event entry.get() is lagging behind

This is not a bug: The event is triggered before the new character is inserted in the entry field, which is why the entry content is "lagging behind".

To fix this, bind instead to <KeyRelease> which is triggered after the character is inserted in the entry field.



Related Topics



Leave a reply



Submit