简体   繁体   English

每次在文本框中输入文本时,如何使用 tkinter 更新 label

[英]How do you update a label every time text is typed into a textbox, using tkinter

I'm trying to create an app where a part of it involves a label that dynamically changes and displays the number of characters typed within the textbox every time it gets updated.我正在尝试创建一个应用程序,其中一部分涉及 label ,它会在每次更新时动态更改并显示在文本框中键入的字符数。 I couldn't find any information regarding this and unsure whether this is even possible.我找不到有关此的任何信息,并且不确定这是否可能。

The most foolproof way to get notified when the text widget changes is to intercept the low-level insert, delete, and replace commands.在文本小部件更改时获得通知的最简单方法是拦截低级插入、删除和替换命令。 When they occur, an event can be generated, which you can then bind to.当它们发生时,可以生成一个事件,然后您可以绑定到该事件。

This involves a tiny bit of tcl voodoo, but it works, and works whether you type into the widget, paste using the mouse, or directly call the insert or delete methods.这涉及到一点点 tcl voodoo,但无论您在小部件中键入、使用鼠标粘贴还是直接调用 insert 或 delete 方法,它都可以正常工作。

Here is a complete example:这是一个完整的例子:

import tkinter as tk

class CustomText(tk.Text):
    def __init__(self, *args, **kwargs):
        """A text widget that supports a <<TextChanged>> event"""
        tk.Text.__init__(self, *args, **kwargs)

        self.tk.eval('''
            proc widget_proxy {widget widget_command args} {

                # call the real tk widget command with the real args
                set result [uplevel [linsert $args 0 $widget_command]]

                # if the contents changed, generate an event we can bind to
                if {([lindex $args 0] in {insert replace delete})} {
                    event generate $widget <<TextModified>> -when tail
                }
                # return the result from the real widget command
                return $result
            }

        ''')

        # this replaces the underlying widget with the proxy
        self.tk.eval('''
            rename {widget} _{widget}
            interp alias {{}} ::{widget} {{}} widget_proxy {widget} _{widget}
        '''.format(widget=str(self)))

def update_char_count(event):
    count = event.widget.count("1.0", "end-1c")
    # count is a tuple; the character count is the first element
    count = 0 if not count else count[0]
    label.configure(text=f"Characters: {count}")

root = tk.Tk()
text = CustomText(root)
label = tk.Label(root, anchor="w")
label.pack(side="bottom", fill="x")
text.pack(fill="both", expand=True)

text.bind("<<TextModified>>", update_char_count)
root.mainloop()

If you want a simpler solution that requires a bit more work by the caller, look at this:如果您想要一个更简单的解决方案,需要调用者进行更多工作,请查看以下内容:

import tkinter as tk

def callback(event):
    # After 1 ms call `_callback`
    # That is to make sure that tkinter has handled the keyboard press
    root.after(1, _callback)

def _callback():
    # The `-1` is there because when you have `text_widget.get(..., "end")`
    # It adds a "\n" character at then end
    number_of_chars = len(text_widget.get("1.0", "end")) - 1
    print(number_of_chars)

root = tk.Tk()
text_widget = tk.Text(root)
text_widget.pack()

text_widget.bind("<Key>", callback)

root.mainloop()

This basically binds all keyboard presses to the callback function.这基本上将所有键盘按下绑定到callback function。 I can't think of any way to change the text in a text widget without pressing a keyboard button.我想不出任何方法可以在不按键盘按钮的情况下更改文本小部件中的文本。

Please note that it will only run when a key is pressed so it wouldn't work if you called text_widget.insert or text_widget.delete (@acw1668 found that problem).请注意,它只会在按下某个键时运行,因此如果您调用text_widget.inserttext_widget.delete (@acw1668 发现该问题),它将无法工作。 It is the caller's responsibility to call _callback just after they call .insert / .delete .在调用.insert / .delete之后调用_callback是调用者的责任。

An even simpler answer is always to use what someone has already created:D一个更简单的答案是始终使用某人已经创建的内容:D

# `idlelib` is part of the standard library
from idlelib.redirector import WidgetRedirector
import tkinter as tk

def my_insert(*args):
    original_insert(*args)
    update_label()

def my_delete(*args):
    original_delete(*args)
    update_label()

def my_replace(*args):
    original_replace(*args)
    update_label()

def update_label():
    number_of_chars = len(text.get("1.0", "end")) - 1
    print(number_of_chars)

root = tk.Tk()
text = tk.Text(root)
text.pack()

redir = WidgetRedirector(text)
original_insert = redir.register("insert", my_insert)
original_delete = redir.register("delete", my_delete)
original_replace = redir.register("replace", my_replace)

root.mainloop()

This solution works even if you use text.insert(...)即使您使用text.insert(...) ,此解决方案也有效

For more info on how it works just use help(WidgetRedirector) .有关其工作原理的更多信息,请使用help(WidgetRedirector)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM