Python Tkinter Text Modified Callback
Solution 1:
I suggest a simpler approach. You can set up a proxy for the widget, and within that proxy you can detect whenever anything was inserted or deleted. You can use that information to generate a virtual event, which can be bound to like any other event.
Let's start by creating a custom text widget class, which you will use like any other text widget:
import Tkinter as tk
classCustomText(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
The proxy in this example does three 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:
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()
Solution 2:
I integrated the above
<<TextModified
>> example in my code and it worked quite well, except that it was interfering with someedit_modified()
commands.Fortunately, the tkinter Text window has a poorly documented feature which is as good and is fully compatible with the
edit_modified()
get or set commands: the predefined<<Modified
>> tag. You don't even have to create it, it works out-of-the-box.Here are the relevant parts of my code:
The "self" prefixes were removed, some adjustments may be needed
Put that in your Text gadget code:
title = set_title(fname, numbr)
text.bind("<<Modified>>", lambda dummy: save_indicator(title))
Make sure these functions are visible:
defset_title(fname, numbr):
"Creates a window title showing the save indicator,""the file name and a window number"
fname = strip_path(fname)
ifnot fname:
fname = "(New Document)"return"+ {} - Window no.{}".format(fname, numbr)
defstrip_path(fname):
return os.path.split(fname)[-1]
defsave_indicator(title, event=None):
"Update the window title"
titre = toggle_star(title)
text.winfo_toplevel().title(title)
deftoggle_star(title):
"Change the first character of the title"chr='+'; chr0='x'if text.edit_modified():
title = chr0 + title[1:]
else:
title = chr + title[1:]
return title
Here is a complete working example with the predefined
<<Modified
>> tag:
deftoggle_star(title):
"Change the color of the star in the title bar"chr='+'; chr0='x'if text.edit_modified():
title = chr0 + title[1:]
else:
title = chr + title[1:]
return title
defset_title(fname, winno):
"Put save indicator, file name and window number in the title"ifnot fname:
fname = "(New Document)"return"+ {} - Window no.{}".format(fname, winno)
defmockSave(title, event=None):
title = toggle_star(title)
root.winfo_toplevel().title(title)
text.edit_modified(0)
defread_ed_mod():
print("text.edit_modified()=", text.edit_modified())
defonModification(title, event=None):
title = toggle_star(title)
root.winfo_toplevel().title(title)
from tkinter import *
fname = 'blabla.txt'
winno = 1 ;
root = Tk()
label = Label(root, anchor="w")
text = Text(root, width=40, height=4)
label.pack(side="bottom", fill="x")
text.pack(side="top", fill="both", expand=True)
Button(root, text='Mock Save', command= lambda: mockSave(title)).pack(side=LEFT)
Button(root, text='Read ed_mod', command= lambda: read_ed_mod()).pack(side=RIGHT)
text.bind('<<Modified>>', lambda event: onModification(title))
title = set_title(fname, winno)
root.winfo_toplevel().title(title)
text.edit_modified(0)
root.mainloop()
Post a Comment for "Python Tkinter Text Modified Callback"