Posts: 61
Threads: 29
Joined: Oct 2023
Jan-30-2024, 07:21 PM
(This post was last modified: Jan-30-2024, 07:22 PM by jacksfrustration.)
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?
Posts: 2,128
Threads: 11
Joined: May 2017
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()
Posts: 6,809
Threads: 20
Joined: Feb 2020
Jan-30-2024, 09:40 PM
(This post was last modified: Jan-30-2024, 09:40 PM by deanhystad.)
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.
Posts: 61
Threads: 29
Joined: Oct 2023
(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
Posts: 61
Threads: 29
Joined: Oct 2023
(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?
Posts: 6,809
Threads: 20
Joined: Feb 2020
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()
Posts: 61
Threads: 29
Joined: Oct 2023
(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
Posts: 6,809
Threads: 20
Joined: Feb 2020
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?
Posts: 61
Threads: 29
Joined: Oct 2023
(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.
Posts: 6,809
Threads: 20
Joined: Feb 2020
Feb-05-2024, 08:37 PM
(This post was last modified: Feb-05-2024, 08:37 PM by deanhystad.)
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.
|