Every time a character is entered into a Text
widget, I want to get the contents of that widget and subtract its length from a certain number (basically a "you have x characters left" deal).
But the StringVar()
is always one event behind. This is, from what I gather, because the event is processed before the character is entered into the Text widget. This means that if I have 3 characters in the field and I enter a 4th, the StringVar
is updated but is still 3 characters long, then it updates to 4 when I enter a 5th character.
Is there a way to keep the two in line?
Here's some code. I removed irrelevant parts.
def __init__(self, master):
self.char_count = StringVar()
self.char_count.set("140 chars left")
self.post_tweet = Text(self.master)
self.post_tweet.bind("<Key>", self.count)
self.post_tweet.grid(...)
self.char_count = Label(self.master, textvariable=self.foo)
self.char_count.grid(...)
def count(self):
self.x = len(self.post_tweet.get(1.0, END))
self.char_count.set(str(140 - self.x))
A simple solution is to add a new bindtag after the class binding. That way the class binding will fire before your binding. See this answer to the question How to bind self events in Tkinter Text widget after it will binded by Text widget? for an example. That answer uses an entry widget rather than a text widget, but the concept of bindtags is identical between those two widgets. Just be sure to use Text
rather than Entry
where appropriate.
Another solution is to bind on KeyRelease, since the default bindings happen on KeyPress.
Here's an example showing how to do it with bindtags:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.post_tweet = tk.Text(self)
bindtags = list(self.post_tweet.bindtags())
bindtags.insert(2, "custom") # index 1 is where most default bindings live
self.post_tweet.bindtags(tuple(bindtags))
self.post_tweet.bind_class("custom", "<Key>", self.count)
self.post_tweet.grid()
self.char_count = tk.Label(self)
self.char_count.grid()
def count(self, event):
current = len(self.post_tweet.get("1.0", "end-1c"))
remaining = 140-current
self.char_count.configure(text="%s characters remaining" % remaining)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()