A simple solution that works most of the time would be to put a binding on <KeyRelease>
. That will enable a function to be called whenever the user is typing. This won't trigger the callback whenever data is pasted with the mouse, or inserted via other means (such as a toolbar button).
A more robust solution is to set up a proxy for the widget, so that an event is generated whenever anything is inserted or deleted in the widget. This proxy can look at what is being done with the widget (insert, delete, selection changed, etc) and generate an event. You can then bind to this event to do whatever you want.
Here's an example of a custom text class that generates a <<TextModified>>
event whenever data is inserted or deleted:
import tkinter as tk
class CustomText(tk.Text):
def __init__(self, *args, **kwargs):
"""A text widget that report on internal widget commands"""
tk.Text.__init__(self, *args, **kwargs)
# create a proxy for the underlying widget
self._orig = self._w + "_orig"
self.tk.call("rename", self._w, self._orig)
self.tk.createcommand(self._w, self._proxy)
def _proxy(self, command, *args):
cmd = (self._orig, command) + args
result = self.tk.call(cmd)
if command in ("insert", "delete", "replace"):
self.event_generate("<<TextModified>>")
return result
This proxy does four things:
- First, it calls the actual widget command, passing in all of the arguments it received.
- Next it generates an event for every insert and every delete
- Then it then generates a virtual event
- And finally it returns the results of the actual widget command
You can use this widget exactly like any other Text widget, with the added benefit that you can bind to <<TextModified>>
.
For example, if you wanted to display the number of characters in the text widget you could do something like this:
import tkinter as tk
# ... import of definition of CustomText goes here ...
root = tk.Tk()
label = tk.Label(root, anchor="w")
text = CustomText(root, width=40, height=4)
label.pack(side="bottom", fill="x")
text.pack(side="top", fill="both", expand=True)
def onModification(event):
chars = len(event.widget.get("1.0", "end-1c"))
label.configure(text="%s chars" % chars)
text.bind("<<TextModified>>", onModification)
root.mainloop()