Reputation: 13
I want to know where and what was changed by user in tkinter's Text
widget.
I've found how to get that text was somehow modified by using <<Modified>>
event but I can't get actual changes:
from tkinter import *
def reset_modified():
global resetting_modified
resetting_modified = True
text.tk.call(text._w, 'edit', 'modified', 0)
resetting_modified = False
def on_change(ev=None):
if resetting_modified: return
print ("Text now:\n%s" % text.get("1.0", END))
if False: # ????
print ("Deleted [deleted substring] from row %d col %d")
if False: # ????
print ("Inserted [inserted substring] at row %d col %d")
reset_modified()
resetting_modified = False
root = Tk()
text = Text(root)
text.insert(END, "Hello\nworld")
text.pack()
text.bind("<<Modified>>", on_change)
reset_modified()
root.mainloop()
For example, if I select 'ello' part from "hello\nworld" in Text
widget then I press 'E', then I want to see
"Deleted [ello] from row 0 col 1" followed by "Inserted [E] at row 0 col 1"
is it possible to get such changes (or at least their coordinates) or I have basically to diff
text on each keystroke if I want to detect changes run time?
Upvotes: 0
Views: 893
Reputation: 385980
Catching the low level inserts and deletes performed by the underlying tcl/tk code is the only good way to do what you want. You can use something like WidgetRedirector
or you can do your own solution if you want more control.
Writing your own proxy command to catch all internal commands is quite simple, and takes just a few lines of code. Here's an example of a custom Text widget that prints out every internal command as it happens:
from __future__ import print_function
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)
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):
# this lets' tkinter handle the command as usual
cmd = (self._orig, command) + args
result = self.tk.call(cmd)
# here we just echo the command and result
print(command, args, "=>", result)
# Note: returning the result of the original command
# is critically important!
return result
if __name__ == "__main__":
root = tk.Tk()
CustomText(root).pack(fill="both", expand=True)
root.mainloop()
Upvotes: 1
Reputation: 13
After digging around, I've found that idlelib has WidgetRedirector which can redirect on inserted/deleted events:
from tkinter import *
from idlelib.WidgetRedirector import WidgetRedirector
def on_insert(*args):
print ("INS:", text.index(args[0]))
old_insert(*args)
def on_delete(*args):
print ("DEL:", list(map(text.index, args)))
old_delete(*args)
root = Tk()
text = Text(root)
text.insert(END, "Hello\nworld")
text.pack()
redir = WidgetRedirector(text)
old_insert=redir.register("insert", on_insert)
old_delete=redir.register("delete", on_delete)
root.mainloop()
Though it seems hacky. Is there a more natural way?
Upvotes: 1