Zach Reneau
Zach Reneau

Reputation: 1951

Python 3.4 Tkinter - How can I just delete a string anywhere in a text widget?

If you need more details, I'm happy to provide, but instead let me just provide the scenario.

I've done this to insert HTML line breaks into my text:

count = int(text_entry.index('end-1c').split('.')[0])
for i in range(count):
    if i != 0.0:
        text_entry.insert('%d.end' % i, '<br>')

Put simply, I just want to undo this after the rest of my function executes, returning the text to its original state. I tried using text.delete() and text.replace(), but I couldn't seem to simply remove the inserted string without altering the rest of the text. I'm new to Tkinter, so I'm sure I'm not understanding the use of the indices or something basic.

I appreciate your time and attention.

Upvotes: 0

Views: 5543

Answers (1)

Bryan Oakley
Bryan Oakley

Reputation: 386352

The text widget has a delete method which will delete any range of text that you want. It does exactly what it should -- give it two character positions and it will delete the text between them.

For example to delete the entire second line of text you can do:

text_entry.delete("2.0", "3.0")

If you're trying to remember ranges of text so that you can delete them later, make sure you delete from the bottom up. If you delete from the top down, the first delete will cause all of the other saved indexes to be incorrect. If you want to delete lines 2 and 4, when you delete line 2, line 4 becomes line 3, etc. If you delete line 4 first, the line numbers of all previous lines remain unaffected.

If you're simply wanting to undo a series of actions, you can programmatically call the edit_undo method. Since the undo mechanism is a bit under-documented for Tkinter, I'll include an example. The key points are:

  1. set undo=True in the text widget
  2. call edit_separator() before adding your text
  3. call edit_undo(), which will undo everything up to the moment of the last separator

That last point is important -- if you let the user enter other data, separators get automatically added. This trick works best if there's no interaction between the time you insert a bunch of data and then undo those inserts.

Here's a working example:

import Tkinter as tk

class Example(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.toolbar = tk.Frame(self)
        self.toolbar.pack(side="top", fill="x")
        do_button = tk.Button(self.toolbar, text="do it", command=self.do_it)
        undo_button = tk.Button(self.toolbar, text="UNdo it", command=self.undo_it)
        do_button.pack(side="left")
        undo_button.pack(side="left")

        self.text = tk.Text(self, wrap="word", undo=True)
        self.text.pack(fill="both", expand=True)
        with open(__file__, "r") as f:
            self.text.insert("1.0", f.read())

    def do_it(self):
        self.text.edit_separator()
        count = int(self.text.index('end-1c').split('.')[0])
        for i in range(1, count):
            self.text.insert('%d.end' % i, '<br>')
            text = self.text.get("1.0", "end-1c")

    def undo_it(self):
        self.text.edit_undo()

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()

Upvotes: 5

Related Questions