Python Forum
make widgets disappear from tkinter
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
make widgets disappear from tkinter
#1
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?
Reply
#2
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()
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
Reply
#3
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.
Reply
#4
(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
Reply
#5
(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?
Reply
#6
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()
Reply
#7
(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
Reply
#8
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?
Reply
#9
(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.
Reply
#10
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.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Why I am getting ModuleNotFoundError when I make an Exe file for tkinter GUI? pymn 0 1,663 Apr-01-2022, 05:36 PM
Last Post: pymn
  [Tkinter] It it possible to make a help file explorer with tkinter? Clunk_Head 0 1,998 Aug-07-2021, 06:02 PM
Last Post: Clunk_Head
  .get() from generated Entry widgets in tkinter snakes 4 4,253 May-03-2021, 11:26 PM
Last Post: snakes
  [Tkinter] tkinter.Menu – How to make text-variable? Sir 3 5,673 Mar-10-2021, 04:21 PM
Last Post: Sir
  Tkinter Python: How to make sure tkSimpleDialog takes in No value entered rcmanu95 3 2,363 Aug-05-2020, 05:32 AM
Last Post: Yoriz
  How to make button text bold in Tkinter? scratchmyhead 2 12,101 May-16-2020, 02:53 AM
Last Post: scratchmyhead
  Using Tkinter widgets on child window chewy1418 8 7,267 Feb-27-2020, 10:34 PM
Last Post: Marbelous
  Make Label Text background (default color) transparent using tkinter in python barry76 1 23,860 Nov-28-2019, 10:19 AM
Last Post: Larz60+
  Tkinter - Make changes to graph and update it adriancovaci 0 6,616 Apr-08-2019, 09:02 AM
Last Post: adriancovaci
  [Tkinter] Multiple frames with tkinter - How to make them run on fullscreen mode eabs86 3 18,281 Sep-20-2018, 01:27 AM
Last Post: eabs86

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020