Tkinter destroy button create by a loop - python

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()

Related

how to change the color of PanedWindow upon hovering over it, for multiple PanedWindow in tkinter?

I am trying to make PanedWindow change color when I hover mouse over it in tkinter.
now this works for a single iteration.
but when i try to do it for multiple panedwindows it only changes color of the last window.
import tkinter as tk
root = tk.Tk()
for i in range(10):
m1 = tk.PanedWindow(root, bd=4, relief="flat", bg="blue")
m1.pack()
def on_enter(e):
m1.config(background='OrangeRed3', relief="flat")
def on_leave(e):
m1.config(background='SystemButtonFace', relief="flat")
# Create a Button
button1 = tk.Button(m1, text=f"{i}")
button1.pack(pady=20)
# Bind the Enter and Leave Events to the Button
m1.bind('<Enter>', on_enter)
m1.bind('<Leave>', on_leave)
m1.add(button1)
tk.mainloop()
Since at each iteration of the loop all variables are overwritten, the functions are bound to the last created element. It is necessary to pass the desired element to the function. It is even better to collect everything created in dictionaries, so that in the future you can easily change them.
import tkinter as tk
from functools import partial
ms = {}
btns = {}
root = tk.Tk()
def on_enter(m, e):
m.config(background='OrangeRed3', relief="flat")
def on_leave(m, e):
m.config(background='SystemButtonFace', relief="flat")
for i in range(10):
ms[i] = tk.PanedWindow(root, bd=4, relief="flat", bg="blue")
ms[i].pack()
# Create a Button
btns[i] = tk.Button(ms[i], text=f"{i}")
btns[i].pack(pady=20)
# Bind the Enter and Leave Events to the Button
ms[i].bind('<Enter>', partial(on_enter, ms[i]))
ms[i].bind('<Leave>', partial(on_leave, ms[i]))
ms[i].add(btns[i])
tk.mainloop()

Clearing objects from a frame, without deleting the Frame

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

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()

How can I display the current time left in a timer in a label?

I made a timer for part of a project I am making. I have the timer made, but I would like for the time left in the timer to be printed on to a label. ALso, if this wasn't expected, I would like for the time to be on the label then after a second it deletes itself and places the new time remaining(I do not want it to keep printing the time on a new line one after another).
One post I found that was pretty much what I wanted to do, but it did not work for me and I had to change some functions and add some new ones. I am not sure why this didn't work, but I would prefer it to be different because it has a preset time of 10 seconds and I would like for it to be the users choice. The link: Making a countdown timer with Python and Tkinter?
class Application(Frame):
def createWidgets(self):
# More code here
self.timeLeftLabel = Label(root, text='Time Left: ')
self.timeLeftLabel.pack()
def timeLeft(t):
time.sleep(1)
print(t)
def countdownInGUI():
countdown = Label(root, text=entryInt)
countdown.pack()
entryInt = IntVar()
t = Entry(root, textvariable=entryInt)
t.bind('<Return>', get)
t.pack(pady=5)
I am hoping that the time left will show in the label called countdown, but instead nothing shows up until the timer ends then it says "PY_VAR0" on a new line for each second (So its on 3 lines for 3 seconds, 4 lines for seconds, etc..)
In your func countdownInGUI, you created your Label widget by Label(root, text=entryInt), so tkinter will try to convert what you passed to a string. What you should do is to set entryInt as a textvariable instead.
On the other hand, you don't really need to set a textvariable for your Entry widget - you can retrieve the content directly by calling Entry.get().
Here is how everything could work base on your code:
import tkinter as tk
class Application(tk.Frame):
def __init__(self,master=None,**kwargs):
super().__init__(master,**kwargs)
self.master = master
self.timeLeftLabel = tk.Label(master, text='Time Left: ')
self.timeLeftLabel.pack()
self.entryInt = tk.StringVar()
self.countdown = tk.Label(master, textvariable=self.entryInt)
self.countdown.pack()
self.t = tk.Entry(master)
self.t.bind('<Return>', self.start_countdown)
self.t.pack(pady=5)
def start_countdown(self,event=None):
if self.t.get().isdigit():
self.time_left(int(self.t.get()))
def time_left(self, t):
self.entryInt.set(t)
t-=1
if t>=0:
self.master.after(1000,self.time_left, t)
else:
self.entryInt.set("Boom!")
root = tk.Tk()
frame = Application(root)
frame.pack()
root.mainloop()

In tkinter how do I generate a new row every time a button is pressed?

I've written this code which makes one entry box appear on a new row when the button is pressed, but I'm not sure how to get it to make a new entry on a new row underneath the last one. My main problems are how to increment a row_number variable within the function without re-setting it every time the function is called, and how to make the function give a new name to the "new_entry" object each time it is called so that it doesn't overwrite itself.
def new_row():
#Create widgets
new_entry = ttk.Entry(root, width=7)
#Put widgets in grid
new_entry.grid(column=0, row=2, sticky=(W, E))
root = Tk()
createRow_button = ttk.Button(root, text='New Row', command=new_row)
createRow_button.grid()
root.mainloop
This is the first time I've asked a question on Stack Overflow so sorry if I've got anything wrong.
Use a class and store the number of rows as class attribute, self.num_rows:
import Tkinter as tk
class App(object):
def new_row(self):
# Create widgets
new_entry = tk.Entry(root, width=7)
# Put widgets in grid
self.num_rows += 1
new_entry.grid(column=0, row=self.num_rows, sticky='WE')
def __init__(self):
self.num_rows = 1
createRow_button = tk.Button(
root, text='New Row', command=self.new_row)
createRow_button.grid()
root = tk.Tk()
app = App()
root.mainloop()

Categories