Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
618 views
in Technique[技术] by (71.8m points)

python - Tkinter: set StringVar after <Key> event, including the key pressed

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))
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

56.9k users

...