1

How to duplicate tkinter text widget so that you can add it in every Notebook tab? I am writing an editor using tkinter, and I have also added undo functions. The problem is, when I add a new tab, the undo function works only for that tab. When I delete that tab, it is not working for other tabs either.

from tkinter.ttk import Notebook
import tkinter.messagebox


class TextClass(Frame):
    def __init__(self, *args, **kwargs):
        Frame.__init__(self, *args, **kwargs)

        self.text = Text(self, bg='white', foreground="black", undo=True,
                         insertbackground='black', height=35, width=135,
                         selectbackground="blue")

        self.scrollbar = Scrollbar(self, orient=VERTICAL, command=self.text.yview)
        self.text.configure(yscrollcommand=self.scrollbar.set)

        self.numberLines = TextLineNumbers(self, width=40, bg='#2A2A2A')
        self.numberLines.attach(self.text)

        self.scrollbar.pack(side=RIGHT, fill=Y)
        self.numberLines.pack(side=LEFT, fill=Y, padx=(5, 0))
        self.text.pack(fill='both', expand=True)

        self.text.bind("<Key>", self.onPressDelay)
        self.text.bind("<Button-1>", self.numberLines.redraw)
        self.scrollbar.bind("<Button-1>", self.onScrollPress)
        self.text.bind("<MouseWheel>", self.onPressDelay)

        def undo():

            try:
                self.text.edit_undo()

            except TclError:
                tkinter.messagebox.showerror(
                    "Nothing to Undo",
                    "You have not typed anything to undo. Please type and try again!"
                )

        undo_button = Button(toolbar, text='Undo', relief='ridge', command=undo, bg='black', fg='white',
                             width=6)
        undo_button.place(x=170, y=5)
        hover(undo_button, on_entrance='white', on_exit='black', entrance_foreground='black', exit_fg='white')

        def redo():

            try:
                self.text.edit_redo()

            except TclError:
                tkinter.messagebox.showerror(
                    "Nothing to Redo",
                    "You have not done an undo to redo. Please type and try again!"
                )

        redo_button = Button(toolbar, text='Redo', relief='ridge', command=redo, bg='black', fg='white',
                             width=6)
        redo_button.place(x=230, y=5)
        hover(redo_button, on_entrance='white', on_exit='black', entrance_foreground='black', exit_fg='white')

    def onScrollPress(self, *args):
        self.scrollbar.bind("<B1-Motion>", self.numberLines.redraw)

    def onScrollRelease(self, *args):
        self.scrollbar.unbind("<B1-Motion>", self.numberLines.redraw)

    def onPressDelay(self, *args):
        self.after(2, self.numberLines.redraw)

    def get(self, *args, **kwargs):
        return self.text.get(*args, **kwargs)

    def insert(self, *args, **kwargs):
        return self.text.insert(*args, **kwargs)

    def delete(self, *args, **kwargs):
        return self.text.delete(*args, **kwargs)

    def index(self, *args, **kwargs):
        return self.text.index(*args, **kwargs)

    def redraw(self):
        self.numberLines.redraw()


class TextLineNumbers(Canvas):
    Canvas.text_widget = None

    def attach(self, text_widget):
        self.text_widget = text_widget

    def redraw(self, *args):
        self.delete("all")

        i = self.text_widget.index("@0,0")
        while True:
            d_line = self.text_widget.dlineinfo(i)
            if d_line is None:
                break
            y = d_line[1]
            line_numbers = str(i).split(".")[0]
            self.create_text(2, y, anchor="nw", text=line_numbers, fill="white")
            i = self.text_widget.index("%s+1line" % i)

def add_tab():
    global tab
    tab = Frame(notebook)
    notebook.add(tab, text=f'{"Untitled1.txt": ^20}')
    global text
    text = TextClass(tab, bg='white')
    text.pack()
    text.text.focus()


def close_tab():
    notebook.forget('current')

def hover(widget, entrance_foreground, exit_fg, on_entrance, on_exit):
    widget.bind("<Enter>", func=lambda e: widget.config(
        bg=on_entrance,
        fg=entrance_foreground
    ))

    widget.bind("<Leave>", func=lambda e: widget.config(
        bg=on_exit,
        fg=exit_fg
    ))


root = Tk()
root.config(bg='white')
root.geometry("1260x680")

toolbar = Frame(root, bg='white', height=45)
toolbar.pack(expand=False, fill='x')

notebook = Notebook(root)
tab = Frame(notebook)
notebook.add(tab, text=f'{"Untitled.txt": ^20}')
notebook.place(x=50, y=60)


text = TextClass(tab, bg='white')
text.pack()

add_tab_btn = Button(toolbar, text='Add new tab', command=add_tab, relief='ridge', bg='black', fg='white')
hover(add_tab_btn, on_entrance='white', on_exit='black', entrance_foreground='black', exit_fg='white')
add_tab_btn.place(x=15, y=5)

delete_tab_btn = Button(toolbar, text='Delete tab', command=close_tab, relief='ridge', bg='black', fg='white')
hover(delete_tab_btn, on_entrance='white', on_exit='black', entrance_foreground='black', exit_fg='white')
delete_tab_btn.place(x=100, y=5)

root.after(200, text.redraw())
scr_menu()
root.mainloop()
  • Please what is the question? You want to have same widget in multiple tabs? – Jakub Szlaur Feb 16 '21 at 10:25
  • if you want such kind, use loops to create the tabs, bind an event which when a tab is closed, that event closes all other tabs. I don't think we can create multiple widgets with a single object (widget) – Rahul A Ranger Feb 16 '21 at 10:39
  • I think that the problem is that you keep making new undo buttons over the old ones. Try putting the button inside the notebook. – TheLizzard Feb 16 '21 at 11:26
  • 1
    Are you the same person that posted this question: https://stackoverflow.com/questions/66220638/how-to-add-a-function-to-the-text-widget-of-tkinter-in-such-a-way-that-the-funct ? – Bryan Oakley Feb 16 '21 at 15:38
  • This code won't run for more than one reason. Please fix it. And please try to reduce it down to a [mcve] - there's a lot of code that isn't necessary for the purposes of this question. – Bryan Oakley Feb 16 '21 at 15:39

1 Answers1

1

As I suspected you keep making new undo/redo buttons over the old ones each time you create a new tab. This means that the button only works for the last tab. To fix that problem you have to move the undo/redo buttons inside the notebook. Just as a proof of concept change this line:

undo_button = Button(toolbar, text='Undo', relief='ridge', command=undo, bg='black', fg='white',
                     width=6)

to

undo_button = Button(self, text='Undo', relief='ridge', command=undo, bg='black', fg='white',
                     width=6)
TheLizzard
  • 7,248
  • 2
  • 11
  • 31