Clearing objects from a frame, without deleting the Frame - python

I'm attempting to create a simple point of sale system where when a button is pressed, its quantity and price are added up and eventually a total is shown (haven't gotten to this bit yet)
I've decided to also incorporate a clear button which will clear the frame in which the clicked items and their prices + quantities are shown however I'm having some problems with clearing the frame and still being able to click buttons afterwards.
This is the code I have for the Item button:
def AddButton():
global item_num #calls global variable
item_num += 1
item_text = "Chips 2.00"+" "+str(item_num) #concatonates text & variable
item1.config(text=item_text) #updates label text - doesn't add multiple
item1.pack()
addButton = Button(itemFrame, text="Chips", width=10, height=10, command=AddButton)
addButton.grid(row=1, column=1)
item1 = Label(receiptFrame)
and I began by trying to use .destroy like this:
def clearClick(): #blank function for clear button
receiptFrame.destroy()
however, since this completely deletes the frame, I'm then not able to re-input more items after it's been cleared
I also tried re-creating the frame:
def clearClick(): #blank function for clear button
receiptFrame.destroy()
receiptFrame = Frame(root, width=600, height=500, bd=5, relief="ridge")
receiptFrame.grid(row=1, column=3, columnspan=2)
but this still doesn't work
Is there a way to clear the contents of a frame without deleting the frame itself or does .destroy have to be used?

fr.winfo_children() returns the list of widgets inside the frame:
root = tk.Tk()
fr = tk.Frame()
lb = tk.Label(fr)
lb.grid()
print(fr.winfo_children())
for child in fr.winfo_children():
child.destroy()
print(fr.winfo_children()) # Now empty

Related

Messed Up Spacing After Button Click in Tkinter

I'm trying to create a GUI using Tkinter for a Pip-boy from Fallout 3. I'm running into a problem where after I click a button in one of the frames, the spacing between the buttons gets messed up. This spacing change happens for all but one of the buttons in the frame (the Lockpick one).
This is what I want the button spacing to look like (what it looks like before I click a button):
This is what happens after I click a button (in this case the Barter one)
Here is the code I am using:
from tkinter import *
# to read descriptions of each skill from a text file
with open("skills.txt") as f:
lines = f.readlines()
# function that updates the label with a different description when the corresponding button is clicked
def show_skill_desc(index):
desc['text'] = lines[index-1]
# makes the window and frame
root = Tk()
root.geometry("1024x600")
root.title("Skills")
frame = Frame(root)
frame.grid()
# creates the label
Label(root, text="Stats").grid(row=0, column=0)
# list of skills which will each have their separate labels
skills_list = ["Barter", "Big Guns", "Energy Weapons", "Explosives", "Lockpick", "Medicine",
"Melee Weapons", "Repair", "Science", "Small Guns", "Sneak", "Speech", "Unarmed"]
# placing the label in the frame
desc = Label(root, text="", padx=30, wraplength=600, justify=LEFT)
desc.grid(row=2, column=1)
# creates a button for each skill
button_list = []
for i in range(12):
button_list.append(Button(root, text=skills_list[i], width=40,
height=2, command=lambda c=i: show_skill_desc(button_list[c].grid_info()['row']), padx=0, pady=0))
button_list[i].grid(row=i+1, column=0)
root.mainloop()
The purpose of the GUI is to display the description of each skill, when the button for a skill is clicked.
Does anyone know why the spacing change happens? Thank you!

How do I make a function for cancel button which when button is clicked unchecks all buttons that I have checked?

I have made a menu which consists of checkbuttons which are created by adding values to a list, so to be precise if I want to add a new item on the menu all I need to do is to put something in the list and checkbutton will be created through while loop. I landed on a problem here, I want to make a cancel button which unchecks all the selected buttons but due to variable option in checkbutton being same for all the buttons because there is only one while loop for creating them all my buttons get selected when I press on one.
Note I do not want to use deselect() option, I am trying to make it this way. Typical error I get is:
Ponisti[p] = tkinter.IntVar()
IndexError: list assignment index out of range,
Or it does that thing where it selects all when I press one button.
So all in all, I am trying to make the variable in checkbutton change for each new button I add so I can select them independently.
My piece of code is given as an example, I hope you can get a grasp of it and a grasp of my idea.
Piece of code:
import tkinter
window = tkinter.Tk()
Array =[]
p=0
Ponisti =[]
while p != len(Meni):
Ponisti[p] = tkinter.IntVar()
p=p+1
def cancel():
f=0
for i in Array:
Ponisti[f].set('0')
f=f+1
while j != len(Meni):
items = tkinter.Checkbutton(window, text=Meni[j], onvalue=1, offvalue=0)
canceldugme = tkinter.Button(frame1,text="Cancel",command=cancel)
This does what you want.
import tkinter as tk
def reset():
for var in variables:
var.set(0)
menu = [str(i) for i in range(8)]
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
variables = [tk.IntVar(0) for _ in range(len(menu))]
length = len(menu) // 2
for i, var in enumerate(variables):
row, col = divmod(i, length)
checkbutton = tk.Checkbutton(frame, variable=var, text=menu[i])
checkbutton.grid(row=row, column=col, sticky="w")
reset_button = tk.Button(root, text="Reset", command=reset)
reset_button.pack(side="right")
root.mainloop()
Basically you want to create a list of variables and for each variable create a new button that is bound to it. When you call the reset function, all you have to do is iterate over your variables and reset their value.
I put the checkbuttons in a frame so you can use the grid method, since it seeems you want to lay them out in a grid fashion. The reason for using a frame is that you can't mix grid and pack for the same window. Both frame and reset_button are placed using pack(), and the checkuttons inside the frame can then be placed using grid().

Python - Tkinter Manage Frames

I have a small script which is organized in 3 frames:
1 in the first row
1 in the second row Left
1 in the second row right
I press the button in the first row frame and hand over the input value to the Label in the second row in the left.
Here my code:
import tkinter as tk
# Create Window
root = tk.Tk()
# Define String Variable
Name = tk.StringVar()
# Organize root window in 3 frames
EntryFrame = tk.Frame(root)
MainLeftFrame = tk.Frame(root)
MainRightFrame = tk.Frame(root)
# Create Buttons, Entry and Labels
NameLabel = tk.Label(MainLeftFrame, textvariable=Name)
InputName = tk.Entry(EntryFrame, width=20,bg='yellow')
SubmitButton = tk.Button(EntryFrame, text='Submit', command=lambda:action())
# Define what happens when press button reset
def reset():
MainLeftFrame.forget()
MainRightFrame.forget()
EntryFrame.pack()
# Define what happens when button is pressed
def action():
Name.set(InputName.get())
ResetButton = tk.Button(MainRightFrame, text='Reset', command=lambda: reset())
ResetButton.pack()
Placeholder = tk.Label(MainRightFrame, text="place holder")
Placeholder.pack(side="top")
EntryFrame.forget()
# Pack Widgets
EntryFrame.pack(side='top')
MainLeftFrame.pack(side='left')
MainRightFrame.pack(side='right')
InputName.pack()
SubmitButton.pack()
NameLabel.pack()
#mainloop
root.mainloop()
Now to my question:
When I press the "Submit" Button for the Second time (after pressing Reset Button) nothing is happening :(
Thanks in advance!
The reason of your program not working is that, after using forget on the MainLeftFrame and MainRightFrame you aren't packing them again when the action function is called. Adding these 2 lines of code in action function should make it work. BUT
MainLeftFrame.pack()
MainRightFrame.pack()
That's not the only issue, defining new widgets every time the function is called and packing them will additively increase the same widget set over and over again. To avoid this, you would have to predefine them and them perform forget and repacking. But a better thing to do would be to have a dedicated frame for them, so that it becomes easy for you to toggle. I have tried rewriting your script, let me know if this is what you wanted.
from tkinter import *
def reset():
entry_frame.pack()
main_frame.pack_forget()
def submit():
entry_frame.pack_forget()
main_frame.pack()
name.set(name_entry.get())
root=Tk()
entry_frame=Frame(root)
entry_frame.pack()
name_entry=Entry(entry_frame)
name_entry.pack(side='top')
submit_button=Button(entry_frame,text='Submit',command=submit)
submit_button.pack(side='top')
main_frame=Frame(root)
reset_button=Button(main_frame,text='Reset',command=reset)
reset_button.pack(side='top')
name=StringVar()
name_label=Label(main_frame,textvariable=name)
name_label.pack(side='left')
placeholer_label=Label(main_frame,text='placeholer')
placeholer_label.pack(side='right')
root.mainloop()

Tkinter: Adding and self-deleting buttons in list | Adding works, Deleting not

I am just in the middle of creating an entry form for a program, and it seems that I have got stuck with the logic on this one.
Basically I wanted to design a dropdwon-list, which adds words to an array and displays these words as little buttons beneath it. When you click the buttons they disappear again and remove themselfs from the array.
Simple enough, I thought. The adding worked fine so far. But removing not so much... There is a logic error with the button array and I can't seem to figure it out!
I extracted the code for reviewing,
any help is greatly appreciated!
Word adding Window
import tkinter as tk
from tkinter import ttk
def rel_add(*args):
rel_array.append(tkvar.get())
print(rel_array)
rel_buttons_update()
def del_button(i):
print(i)
del rel_array[i]
print(rel_array)
rel_buttons[i].grid_remove()
# del rel_buttons[i]
rel_buttons_update()
def rel_buttons_update():
for i, rel in enumerate(rel_array):
rel_buttons.append(tk.Button(rel_grid, text=rel, font="Helvetica 7", command=lambda c=i: del_button(c)))
rel_buttons[i].grid(column=i, row=0, sticky="nw")
rel_array = []
rel_buttons = []
win = tk.Tk()
tkvar = tk.StringVar(win) # Create a Tkinter variable
choices_words = ["oolbath", "pflanze", "haus", "wasser", "brimbambum"] # Create Variable List
tkvar.set('Related Words...') # set the default option
choices_words.sort() # Sort List
tk.Label(win, text="Related Words: ").grid(row=0,column=0, sticky="w")
rel = tk.OptionMenu(win, tkvar, *choices_words)
rel.grid(row=0,column=1, sticky="w")
# Callbuck Function for Dropdwon Menu
tkvar.trace("w", rel_add)
rel_grid = tk.Frame(win)
# Display the Buttons for the related Words
rel_grid.grid(row=1,column=1, sticky="w")
win.mainloop()
The main problem is that you keep recreating the same buttons over and over, so rel_buttons contains many more elements than you expect.
As a simple experiement, add a print statement to rel_buttons_update like this:
def rel_buttons_update():
for i, rel in enumerate(rel_array):
rel_buttons.append(ttk.Button(rel_grid, text=rel, command=lambda c=i: del_button(c)))
rel_buttons[i].grid(column=i, row=0, sticky="nw")
print('in update, rel_buttons is now', rel_buttons)
You'll notice that there is one button the first time you use the option menu, three buttons the second time, six buttons the third time, and so on.
You need to either only create new buttons, or delete all the old buttons before recreating them.

Tkinter destroy button create by a loop

I want to create a reminder to remind me of the time I have chosen in the combobox by pressing the confirmed button. I put the time in a label and also create a delete button that can delete the label and button itself in the same row by a loop. It works if there's only one label, but if I increased the number of it it can only destroy the last label and button.
below is my code:
class final:
def __init__(self,app):
self.savelist=[]
self.time= StringVar()
self.timecombo = ttk.Combobox(app,textvariable=self.time)
self.timecombo.grid(row=0,column=1)
self.timecombo.config(value =('1:00','2:00','3:00','4:00','5:00','6:00','7:00','8:00','9:00','10:00','11:00','12:00'))
self.button1=Button(app,text='confirmed',command=self.save)
self.button1.grid(row=3,column=2)
***def save(self):
savetext = self.time.get()
self.savelist.append(savetext)
self.deletebutton_list = []
self.savelabel_list = []
for i in range(len(self.savelist)):
savelabel = Label(app, text=self.savelist[i])
savelabel.grid(row=4 + i, column=0)
self.savelabel_list.append((savelabel))
deletebutton = Button(app, text='delete' , command=functools.partial(self.deletelabel,idx=i))
deletebutton.grid(row=4 + i, column=1)
self.deletebutton_list.append(deletebutton)
def deletelabel(self, idx):
self.savelabel_list[idx].destroy()
self.deletebutton_list[idx].destroy()
self.savelist.remove(self.savelist[idx])
self.savelabel_list.remove(self.savelabel_list[idx])
self.deletebutton_list.remove(self.deletebutton_list[idx])***
app = Tk()
a = final(app)
app.title('things to do')
app.geometry("500x300+200+200")
app.mainloop()
I believed that there must be something wrong in the loop or the function deletelabel but I still can't fix it.
self.savelabel_list.remove(self.savelabel_list[idx])
Do not change the list. If you delete label/button #1, then label #2 becomes #1, and so when you press the button to delete label #2, it deletes label #3 because everything has moved up. Also, note that every time you call "save()" it creates a new set of widgets that overlay the old widgets, which will eventually slow down the computer. Create and grid the new time label only. Keep track of the row with a self.next_row variable (or whatever) and increment it by one each time.
This is a question that points out how usable classes are. Create a new class, with label and a close button, for each reminder.
from tkinter import *
from tkinter import ttk
class NewButton:
def __init__(self, master, label_text, this_row):
## put everything in a new frame so destroying
## one frame will destroy everything in it
self.fr=Frame(master)
self.fr.grid(row=this_row, column=1)
Label(self.fr, text=label_text).grid(row=0, column=1)
Button(self.fr, text="Close This",
command=self.fr.destroy).grid(row=0, column=2)
class Final:
def __init__(self,app):
self.app=app
self.this_row=4
self.time_var= StringVar()
self.timecombo = ttk.Combobox(app,textvariable=self.time_var)
self.timecombo.grid(row=0,column=1)
self.button1=Button(app,text='confirmed',command=self.save)
self.button1.grid(row=3,column=2)
def save(self):
save_text = self.time_var.get()
self.this_row += 1
next_button=NewButton(self.app, save_text, self.this_row)
self.time_var.set("")
app = Tk()
a = Final(app)
app.title('things to do')
app.geometry("500x300+200+200")
app.mainloop()

Categories