A Tkinter.Text can notice when its contents are changed. This recipe shows how to make use of the virtual event this generates to call your own callback.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | class ModifiedMixin:
'''
Class to allow a Tkinter Text widget to notice when it's modified.
To use this mixin, subclass from Tkinter.Text and the mixin, then write
an __init__() method for the new class that calls _init().
Then override the beenModified() method to implement the behavior that
you want to happen when the Text is modified.
'''
def _init(self):
'''
Prepare the Text for modification notification.
'''
# Clear the modified flag, as a side effect this also gives the
# instance a _resetting_modified_flag attribute.
self.clearModifiedFlag()
# Bind the <<Modified>> virtual event to the internal callback.
self.bind_all('<<Modified>>', self._beenModified)
def _beenModified(self, event=None):
'''
Call the user callback. Clear the Tk 'modified' variable of the Text.
'''
# If this is being called recursively as a result of the call to
# clearModifiedFlag() immediately below, then we do nothing.
if self._resetting_modified_flag: return
# Clear the Tk 'modified' variable.
self.clearModifiedFlag()
# Call the user-defined callback.
self.beenModified(event)
def beenModified(self, event=None):
'''
Override this method in your class to do what you want when the Text
is modified.
'''
pass
def clearModifiedFlag(self):
'''
Clear the Tk 'modified' variable of the Text.
Uses the _resetting_modified_flag attribute as a sentinel against
triggering _beenModified() recursively when setting 'modified' to 0.
'''
# Set the sentinel.
self._resetting_modified_flag = True
try:
# Set 'modified' to 0. This will also trigger the <<Modified>>
# virtual event which is why we need the sentinel.
self.tk.call(self._w, 'edit', 'modified', 0)
finally:
# Clean the sentinel.
self._resetting_modified_flag = False
if __name__ == '__main__':
from Tkinter import Text, BOTH
class T(ModifiedMixin, Text):
'''
Subclass both ModifiedMixin and Tkinter.Text.
'''
def __init__(self, *a, **b):
# Create self as a Text.
Text.__init__(self, *a, **b)
# Initialize the ModifiedMixin.
self._init()
def beenModified(self, event=None):
'''
Override this method do do work when the Text is modified.
'''
print 'Hi there.'
t = T()
t.pack(expand=1, fill=BOTH)
t.mainloop()
|
You can have a Tkinter.Text widget call a callback you define whenever its contents are modified. There's a <<Modified>> Tk virtual event that's generated whenever the text is modified, whether through typing, deleting, pasting, or programmatic insertion or deletion.
In order for it to be generated though, there's a Tk variable 'modified' on the Tk text widget (the one that the Text widget wraps) that must be set to 0 after every change. Just generating the virtual event doesn't reset this variable, so you have to do it yourself. However, resetting this variable to 0 will also trigger the <<Modified>> virtual event.
That's why we have to guard against a recursive call to _beenModified() with the _resetting_modified_flag attribute.
bind() not bind_all(). In function ModifiedMixin.__init() the self.bind_all(...) should be changed to self.bind(...). This becomes obvious when you have two or more Tkinter.Text widgets and this "mixin" only works for the last one created.