Python Forum
make widgets disappear from tkinter - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: GUI (https://python-forum.io/forum-10.html)
+--- Thread: make widgets disappear from tkinter (/thread-41521.html)

Pages: 1 2


make widgets disappear from tkinter - jacksfrustration - Jan-30-2024

hello
i am writing a workout tracker app. i have the option of adding rows of information to save multiple workout data at the same time. i accomplish that with the following code

globals()[f'day_lbl{self.count}'] = Label(text="On")
        globals()[f"day_value_inside{self.count}"] = StringVar(window, value=self.day_value_inside0.get())
        globals()[f"day_opt{self.count}"] = OptionMenu(window, globals()[f"day_value_inside{self.count}"],
                                                       self.updated_list[0],
                                                       self.updated_list[1], self.updated_list[2], self.updated_list[3],
                                                       self.updated_list[4],self.updated_list[5],self.updated_list[6],self.updated_list[7],self.updated_list[8],self.updated_list[9],self.updated_list[10])
        globals()[f"day_opt{self.count}"].grid(row=self.cur_row, column=1)

        globals()[f'lbl{self.count}'] = Label(text="I did")
        globals()[f"lbl{self.count}"].grid(row=self.cur_row, column=2)
        globals()[f"act_value_inside{self.count}"] = StringVar(window, value="Aerobics")
        globals()[f"act_opt{self.count}"] = OptionMenu(window, globals()[f"act_value_inside{self.count}"], "Aerobics",
                                                       "Cycling",
                                                       "Running", "Swimming", "Walking")
        globals()[f"act_opt{self.count}"].grid(row=self.cur_row, column=3)
        globals()[f"unit_lbl{self.count}"] = Label(text="for: ")
        globals()[f"unit_lbl{self.count}"].grid(row=self.cur_row, column=4)

        globals()[f"unit_ent{self.count}"] = Entry()
        globals()[f"unit_ent{self.count}"].grid(row=self.cur_row, column=5)
        globals()[f"min_lbl{self.count}"] = Label(text="minutes")
        globals()[f"min_lbl{self.count}"].grid(row=self.cur_row, column=6)
        
i want to add the option to get rid of a row of data. as you can see i use the .grid function to place the widgets according to my design. is there a way of removing either the selected row of data or the last one?


RE: make widgets disappear from tkinter - DeaD_EyE - Jan-30-2024

DO NOT USE GLOBAL

Just keep references of the objects in a list or a dict. Then you can iterate over the elements in the list and destroy them. You can also swap the text and the command of Buttons.

This is not the best example. One function with closures.
A class would be a better solution for it.

from collections import defaultdict
from tkinter import Tk, Button, Label


def gui():
    root = Tk()
    removeable = defaultdict(list)

    def remove_col0():
        for row in removeable[0]:
            row.destroy()
        removeable[0].clear()
        col0_button["text"] = "Add column 0"
        col0_button["command"] = add_col0

    def remove_col1():
        for row in removeable[1]:
            row.destroy()
        removeable[1].clear()
        col1_button["text"] = "Add column 1"
        col1_button["command"] = add_col1

    def add_col0():
        add_labels(0)
        col0_button["text"] = "Remove column 0"
        col0_button["command"] = remove_col0

    def add_col1():
        add_labels(1)
        col1_button["text"] = "Remove column 1"
        col1_button["command"] = remove_col1

    def add_labels(column):
        for row in range(5):
            label = Label(root, text=f"R{row} C{column}")
            label.grid(row=row, column=column)
            removeable[column].append(label)

        return row + 1

    add_labels(0)
    row = add_labels(1)

    col0_button = Button(root, text="Remove Row 0", command=remove_col0)
    col0_button.grid(row=row, column=0, columnspan=2)

    row += 1
    col1_button = Button(root, text="Remove Row 1", command=remove_col1)
    col1_button.grid(row=row, column=0, columnspan=2)

    row += 1
    Button(root, text="Close", command=root.destroy).grid(
        row=row, column=0, columnspan=2
    )

    return root


if __name__ == "__main__":
    gui().mainloop()



RE: make widgets disappear from tkinter - deanhystad - Jan-30-2024

This is hideous.
globals()[f"unit_ent{self.count}"] = Entry()
globals()[f"unit_ent{self.count}"].grid(row=self.cur_row, column=5)
globals()[f"min_lbl{self.count}"] = Label(text="minutes")
globals()[f"min_lbl{self.count}"].grid(row=self.cur_row, column=6)
Whenever you have variable names that contain index numbers, even if they aren't dynamically generated, you should think "I need to use a list or a dictionary here."

I would rewrite the code above to look like this:
unit_ent.append(tk.Entry())
unit_ent[-1].grid(row=self.cur_row, column=5)
min_lbl.append(tk.Label(text="minutes"))
min_lbl[-1].grid(row=self.cur_row, column=6)
But to address your question.

If you want to erase a tkinter widget from a window, forget it. There is a pack_forget() for removing widgets that were packed, and a grid_forget() for widgets that are in a grid. You can also destroy the widgets if you don't want to use them anymore.

Since you want to remove entire rows, it might make sense to group your controls in widgets in rows, so all the controls for a given row are in a list.


RE: make widgets disappear from tkinter - jacksfrustration - Jan-31-2024

(Jan-30-2024, 09:40 PM)deanhystad Wrote: This is hideous.
globals()[f"unit_ent{self.count}"] = Entry()
globals()[f"unit_ent{self.count}"].grid(row=self.cur_row, column=5)
globals()[f"min_lbl{self.count}"] = Label(text="minutes")
globals()[f"min_lbl{self.count}"].grid(row=self.cur_row, column=6)
Whenever you have variable names that contain index numbers, even if they aren't dynamically generated, you should think "I need to use a list or a dictionary here."

I would rewrite the code above to look like this:
unit_ent.append(tk.Entry())
unit_ent[-1].grid(row=self.cur_row, column=5)
min_lbl.append(tk.Label(text="minutes"))
min_lbl[-1].grid(row=self.cur_row, column=6)
But to address your question.

If you want to erase a tkinter widget from a window, forget it. There is a pack_forget() for removing widgets that were packed, and a grid_forget() for widgets that are in a grid. You can also destroy the widgets if you don't want to use them anymore.

Since you want to remove entire rows, it might make sense to group your controls in widgets in rows, so all the controls for a given row are in a list.

thanks for your help again, friend


RE: make widgets disappear from tkinter - jacksfrustration - Jan-31-2024

(Jan-30-2024, 09:40 PM)deanhystad Wrote: This is hideous.
globals()[f"unit_ent{self.count}"] = Entry()
globals()[f"unit_ent{self.count}"].grid(row=self.cur_row, column=5)
globals()[f"min_lbl{self.count}"] = Label(text="minutes")
globals()[f"min_lbl{self.count}"].grid(row=self.cur_row, column=6)
Whenever you have variable names that contain index numbers, even if they aren't dynamically generated, you should think "I need to use a list or a dictionary here."

I would rewrite the code above to look like this:
unit_ent.append(tk.Entry())
unit_ent[-1].grid(row=self.cur_row, column=5)
min_lbl.append(tk.Label(text="minutes"))
min_lbl[-1].grid(row=self.cur_row, column=6)
But to address your question.

If you want to erase a tkinter widget from a window, forget it. There is a pack_forget() for removing widgets that were packed, and a grid_forget() for widgets that are in a grid. You can also destroy the widgets if you don't want to use them anymore.

Since you want to remove entire rows, it might make sense to group your controls in widgets in rows, so all the controls for a given row are in a list.

i tried both grid_forget and forget that you recommended, using the last item in the list but the widgets still dont disappear. i tried googling it and i ended up with the following

        self.remove_button.append(Button(text="Remove Entry",command=lambda : self.remove_button(self.count)))


i ended up with a TypeError

Error:
TypeError: 'list' object is not callable
what am i doing wrong?


RE: make widgets disappear from tkinter - deanhystad - Jan-31-2024

Example showing forget and destroy.
import tkinter as tk


def toggle_forget():
    if forget_lbl.winfo_viewable():
        forget_lbl.grid_forget()
    else:
        forget_lbl.grid(row=0, column=0, padx=10, pady=10)


def toggle_destroy():
    global destroy_lbl
    if destroy_lbl is None:
        destroy_lbl = tk.Label(root, text="Destroy")
        destroy_lbl.grid(row=0, column=1, padx=(0, 10), pady=10)
    else:
        destroy_lbl.destroy()
        destroy_lbl = None


root = tk.Tk()

forget_lbl = tk.Label(root, text="Forget")
forget_lbl.grid(row=0, column=0, padx=10, pady=10)
button = tk.Button(root, text="Toggle", command=toggle_forget)
button.grid(row=1, column=0, padx=10, pady=(0, 10))

destroy_lbl = tk.Label(root, text="Destroy")
destroy_lbl.grid(row=0, column=1, padx=(0, 10), pady=10)
button = tk.Button(root, text="Toggle", command=toggle_destroy)
button.grid(row=1, column=1, padx=(0, 10), pady=(0, 10))

root.mainloop()



RE: make widgets disappear from tkinter - jacksfrustration - Feb-03-2024

(Jan-31-2024, 06:39 PM)deanhystad Wrote: Example showing forget and destroy.
import tkinter as tk


def toggle_forget():
    if forget_lbl.winfo_viewable():
        forget_lbl.grid_forget()
    else:
        forget_lbl.grid(row=0, column=0, padx=10, pady=10)


def toggle_destroy():
    global destroy_lbl
    if destroy_lbl is None:
        destroy_lbl = tk.Label(root, text="Destroy")
        destroy_lbl.grid(row=0, column=1, padx=(0, 10), pady=10)
    else:
        destroy_lbl.destroy()
        destroy_lbl = None


root = tk.Tk()

forget_lbl = tk.Label(root, text="Forget")
forget_lbl.grid(row=0, column=0, padx=10, pady=10)
button = tk.Button(root, text="Toggle", command=toggle_forget)
button.grid(row=1, column=0, padx=10, pady=(0, 10))

destroy_lbl = tk.Label(root, text="Destroy")
destroy_lbl.grid(row=0, column=1, padx=(0, 10), pady=10)
button = tk.Button(root, text="Toggle", command=toggle_destroy)
button.grid(row=1, column=1, padx=(0, 10), pady=(0, 10))

root.mainloop()

thanks for your time but its still not working
example code follows. ive tried

self.day_opt[-1].forget()
and


self.day_opt[-1].destroy()
and

self.day_opt[-1].grid_forget()
nothing works


RE: make widgets disappear from tkinter - deanhystad - Feb-03-2024

Please provide some sample code showing grid_forget() or pack_forget() not working. forget() is not a tkinter widget method. You just have gotten a NameError when you tried that. Why didn't you post the error message?


RE: make widgets disappear from tkinter - jacksfrustration - Feb-05-2024

(Feb-03-2024, 06:35 PM)deanhystad Wrote: Please provide some sample code showing grid_forget() or pack_forget() not working. forget() is not a tkinter widget method. You just have gotten a NameError when you tried that. Why didn't you post the error message?

i got the following code:


    def remove_button(self):

        self.day_lbl[-1].forget()
        self.act_lbl[-1].forget()
        self.day_opt[-1].forget()
        self.act_opt[-1].forget()
        self.unit_lbl[-1].forget()
        self.unit_ent[-1].forget()
        self.min_lbl[-1].forget()
        self.count-=1
i tried grid_forget() and destroy() but none work. this code however doesnt give me any error codes it just simply does nothing
im creating the list(s) in the class init function and i append to them when the add_column function is triggered.


RE: make widgets disappear from tkinter - deanhystad - Feb-05-2024

I didn't know there was a forget. It is never mentioned anywhere in the documentation. So I asked Python about it.
Output:
>>> import tkinter as tk >>> root = tk.Tk() >>> label = tk.Label(root, text="Label") >>> label.forget <bound method Pack.pack_forget of <tkinter.Label object .!label>> >>>
forget() is just another name for pack_forget(). forget() only works if you use pack() to position the widgets. forget() does nothing if you use grid() to place widgets.

I assume forget() is the main problem, but this is also a problem.
    self.day_lbl[-1].forget()
    self.act_lbl[-1].grid_forget()
    self.day_opt[-1].grid_forget()
    self.act_opt[-1].grid_forget()
    self.unit_lbl[-1].grid_forget()
    self.unit_ent[-1].grid_forget()
    self.min_lbl[-1].grid_forget()
    self.count-=1
This code might work the first time you call it, but probably not the second. Can you guess why? After you try to erase a row, what is in self.act_lbl? does len(self.act_lbl) == self.count? Is it important that they are the same? I can only guess because you didn't post the relevant code.

Please post short runnable examples. Someone reading your post would have no idea what day_lbl[-1] is or what day_lbl[-1].forget() should do. Posting examples that can be run (having all the imports and setup code) serves as documentation and lets others see what you see when you run your code.

If you don't spend time on your posts making it easy for others to help you, others will be less inclined to help you.