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.