I have many tkinter entries named idn1 to idn5,instead of repeating the same
delete function for every entry I tried the following which went well with visual basic but not python:
def deleteme():
idn = ""
i = 1
while True:
idn ="idn"+str(i)+".delete(0,END)"
i += 1
idn
if i == 6:
break
return True
root = Tk()
root.geometry('{}x{}'.format(460, 350))
idn1 = Entry(root).grid(row =0,column=1)
idn2 = Entry(root).grid(row =1,column=1)
idn3 = Entry(root).grid(row =2,column=1)
idn4 = Entry(root).grid(row =3,column=1)
idn5 = Entry(root).grid(row =4,column=1)
btn1 = Button(root,command = deleteme)
root.mainloop()
You create strings like "idn0.delete(0,END)" but for Python it is only string, nothing more. It will not run it as command. You would need to use eval("idn0.delete(0,END)") to execute command in this string but eval() is not preferred method.
You should keep widgets on list or in dictionary and then you can use for-loop to delete all items.
But firt other very common mistake
Using
variable = Widget().grid()
you assign None to variable because grid()/pack()/place() return None
You have to do it in two steps
variable = Widget()
variable.grid()
Minimal working code
import tkinter as tk # PEP8: `import *` is not preferred
# --- functions ---
def deleteme():
for item in widgets:
item.delete(0, 'end')
# --- main ---
widgets = []
root = tk.Tk()
for number in range(5):
variable = tk.Entry(root)
variable.grid(row=number, column=1)
widgets.append(variable)
btn1 = tk.Button(root, text='Delete', command=deleteme) # PEP8: inside `()` use space after comma `,` but without spaces around `=`
btn1.grid(row=5, column=1)
root.mainloop()
The same with dictionary
import tkinter as tk # PEP8: `import *` is not preferred
# --- functions ---
def deleteme():
for name, item in widgets.items():
item.delete(0, 'end')
# --- main ---
widgets = {}
root = tk.Tk()
for number in range(1, 6):
variable = tk.Entry(root)
variable.grid(row=number, column=1)
widgets[f"idn{number}"] = variable
btn1 = tk.Button(root, text='Delete', command=deleteme) # PEP8: inside `()` use space after comma `,` but without spaces around `=`
btn1.grid(row=5, column=1)
root.mainloop()
Of course you can add widgets manually without for-loop but this it is long and boring so I skip some widgets. But even if I skip or add widgets I don't have to change code in deleteme()
import tkinter as tk # PEP8: `import *` is not preferred
# --- functions ---
def deleteme():
for name, item in widgets.items():
item.delete(0, 'end')
# --- main ---
widgets = {}
root = tk.Tk()
variable = tk.Entry(root)
variable.grid(row=0, column=1)
widgets["idn1"] = variable
# or
widgets["idn2"] = tk.Entry(root)
widgets["idn2"].grid(row=1, column=1)
widgets["idn3"] = tk.Entry(root)
widgets["idn3"].grid(row=2, column=1)
# etc.
btn1 = tk.Button(root, text='Delete', command=deleteme) # PEP8: inside `()` use space after comma `,` but without spaces around `=`
btn1.grid(row=5, column=1)
root.mainloop()
The simplest solution, assuming your UI design supports it, is to put all of the entry widgets in a frame. You can then delete the frame and all of its children will automatically be deleted.
entry_frame = Frame(root)
idn1 = Entry(entry_frame).grid(row =0,column=1)
idn2 = Entry(entry_frame).grid(row =1,column=1)
idn3 = Entry(entry_frame).grid(row =2,column=1)
idn4 = Entry(entry_frame).grid(row =3,column=1)
idn5 = Entry(entry_frame).grid(row =4,column=1)
...
# this destroys the frame and all of its children
entry_frame.destroy()
If you can't do that or don't want to do that, the next best solution is to store the widgets in a list.
For example, you can create them in a list like so:
entries = []
for row in range(5):
entry = Entry(root)
entry.grid(row=row, column=1)
entries.append(entry)
Of course, you don't have to create them in a loop. That works well if the widgets are largely identical. If each needs to be unique, create then individually and then append them to the list.
idn1 = Entry(root)
idn2 = Entry(root)
idn3 = Entry(root)
idn4 = Entry(root)
idn5 = Entry(root)
entries = [idn1, idn2, idn3, idn4, idn5]
Note: regardless of which method you choose, you must put the call to grid in a separate statement. The way you wrote your original code, all of your entry variables will be set to None since that's what grid returns.
However you create the list, you can delete them by iterating over the list:
for entry in entries:
entry.destroy()
Related
I am new to tkinter. I have tried the below program. It's working fine. Is there any way I can get the number of character without erasing the label text?
import tkinter as tk
my_w = tk.Tk()
from tkinter import *
my_w.geometry("400x150") # Size of the window width x height
my_w.title("plus2net.com") # Adding a title
Generator = tk.Frame(my_w)
Generator.pack(padx=10, pady=10, fill='x', expand=True)
e1_str=tk.StringVar()
e1_str=tk.StringVar() # declaring a StringVar()
e1 = tk.Entry(Generator,textvariable=e1_str,bg='yellow',font=28) # Entry box
e1.pack(padx=0,fill='x', expand=True)
l1 = tk.Label(Generator, text='No of Chars here' ,font=28) # added one Label
l1.pack(padx=0,fill='x', expand=True)
def my_upd(*args):
l1.config(text=str(len(e1_str.get()))) # read & assign text to StringVar()
e1_str.trace('w',my_upd) # triggers on change of StringVar
my_w.mainloop()
Update text property in your label to: f'No of Chars here: {len(e1_str.get())}':
import tkinter as tk
my_w = tk.Tk()
my_w.geometry("400x150")
my_w.title("plus2net.com")
Generator = tk.Frame(my_w)
Generator.pack(padx=10, pady=10, fill='x', expand=True)
e1_str = tk.StringVar()
e1 = tk.Entry(Generator, textvariable=e1_str, bg='yellow', font=28)
e1.pack(padx=0, fill='x', expand=True)
l1 = tk.Label(Generator, text='No of Chars here', font=28)
l1.pack(padx=0, fill='x', expand=True)
def my_upd(*args):
l1.config(text=f'No of Chars here: {len(e1_str.get())}')
e1_str.trace('w', my_upd) # triggers on change of StringVar
my_w.mainloop()
Output:
You get number of characters by e1_str.get()
For example you could modify your function:
def my_upd(*args):
t = e1_str.get()
if t:
l1.config(text='Number of Chars here: ' + str(len(t)))
else:
l1.config(text='No of Chars here')
I made a tool to add multiple order numbers in our system. The first time a row of entry cells is placed the focus is where it should be. But the second time the focus is not in the new left cell. First I thought it has to do with using the tab key. But if I understand the code correct, I first execute the moving of the tab key and then execute the code. So the command to focus on the new left cell is last.
Where am I going wrong?
import tkinter as tk
from tkinter import ttk
# Create variables for later use
order_list = []
date_list = []
row_number = 0
active_order_entry = None
active_date_entry = None
def add_a_row_of_entry_cells():
global row_number
global active_order_entry
global active_date_entry
row_number += 1
order_entry = ttk.Entry()
order_entry.grid(row=row_number, column=0)
order_entry.focus()
date_entry = ttk.Entry()
date_entry.grid(row=row_number, column=1)
# Make these entries the active ones
active_order_entry = order_entry
active_date_entry = date_entry
# Add entries to a list
order_list.append(order_entry)
date_list.append(date_entry)
def tab_pressed(event):
if active_order_entry.get() != "" and active_date_entry.get() != "":
add_a_row_of_entry_cells()
else:
print("Order, date or both are not filled yet")
def button_pressed():
print("Button pressed")
# Create window
window = tk.Tk()
# Add function to the Tab key
window.bind("<Tab>", tab_pressed)
# Labels on top of the columns
label_order_number = tk.Label(window, text="Order", fg="#22368C")
label_order_number.grid(row=row_number, column=0)
label_date = tk.Label(window, text="Date", fg="#22368C")
label_date.grid(row=row_number, column=1)
# Create empty row
empty_row = tk.Label(window)
empty_row.grid(row=87, column=0)
# Create button
button = tk.Button(window, text="Add orders", command=lambda: button_pressed())
button.grid(row=98, column=0, columnspan=3)
# Create empty row
empty_row = tk.Label(window)
empty_row.grid(row=99, column=0)
# Add the first row
add_a_row_of_entry_cells()
window.mainloop()
Hello this is my first question on this platform. I got a problem with tkinter, I have an entry where the user can write a number. If the user writes something else and press save a label appears with "only floats allowed" so far so good. But if the user writes after the label a appears a number an presses yes, the label should be deleted. How can I delete the label after it plots the first time by correcting the entry and pressing save again?
P.s. its my first try to Code a GUI so I'm thankful for other tipps & tricks.
import tkinter as tk
parison_window = tk.Tk()
parison_window.title("Create Parison")
parison_window.geometry("1000x1000")
pwt1_lbl = tk.Label(parison_window, text="PWT1")
pwt1_lbl.pack()
pwt1_lbl.place(x=30, y=130)
label = tk.Label(parison_window, text="1")
label.pack()
label.place(x=10, y=140 + 20 )
entry = tk.Entry(parison_window, width=5, justify="center")
entry.pack()
entry.place(x=30, y=140 + 20)
def check_and_save():
if entry.get():
try:
pwt1 = float(entry.get())
except ValueError:
error_text = tk.Label(parison_window, text="only floats allowed")
error_text.pack()
error_text.place(x=150, y=140 + 20 )
save_button = tk.Button(parison_window, text="save", command=check_and_save)
save_button.pack()
parison_window.mainloop()
If you want to remove text from existing label then you can use error_text.config(text="") or error_text["text"] = "".
If you want to remove all widget then you may do error_text.destroy()
But all this make problem because widget may not exists in some moments and trying to set text or destroy it may generate error.
You should rather create empty label at start and later only replace text in this label.
import tkinter as tk
# --- functions --- # PEP8: all functions before main code
def check_and_save():
if entry.get():
try:
pwt1 = float(entry.get())
error_text['text'] = ""
except ValueError:
error_text['text'] = "only floats allowed"
# --- main ---
parison_window = tk.Tk()
parison_window.title("Create Parison")
parison_window.geometry("1000x1000")
pwt1_lbl = tk.Label(parison_window, text="PWT1")
pwt1_lbl.place(x=30, y=130)
# ---
label = tk.Label(parison_window, text="1")
label.place(x=10, y=140+20)
entry = tk.Entry(parison_window, width=5, justify="center")
entry.place(x=30, y=140+20)
error_text = tk.Label(parison_window) # create label with empty text
error_text.place(x=150, y=140+20)
# ---
save_button = tk.Button(parison_window, text="save", command=check_and_save)
save_button.pack()
parison_window.mainloop()
PEP 8 -- Style Guide for Python Code
BTW:
pack() and place() (and grid()) are different layout managers and when you use place() then you don't need pack() (and grid())
EDIT:
Using destroy() it would need to use global variable error_text with default value None at start. And later it would need to check if error_text is not None and destroy it (and assign again None)
import tkinter as tk
# --- functions --- # PEP8: all functions before main code
def check_and_save():
global error_text # inform function to assign new value to global variable `error_text` instead of creating local variable `error_text`
if entry.get():
try:
pwt1 = float(entry.get())
if error_text is not None:
error_text.destroy()
error_text = None
except ValueError:
if error_text is not None:
error_text.destroy()
error_text = None
error_text = tk.Label(parison_window, text="only floats allowed")
error_text.place(x=150, y=140+20)
# --- main ---
parison_window = tk.Tk()
parison_window.title("Create Parison")
parison_window.geometry("1000x1000")
pwt1_lbl = tk.Label(parison_window, text="PWT1")
pwt1_lbl.place(x=30, y=130)
# ---
label = tk.Label(parison_window, text="1")
label.place(x=10, y=140+20)
entry = tk.Entry(parison_window, width=5, justify="center")
entry.place(x=30, y=140+20)
error_text = None # global variable with default value
# ---
save_button = tk.Button(parison_window, text="save", command=check_and_save)
save_button.pack()
parison_window.mainloop()
Based on this answer, I successfully created my own widget where labels are wrapped automatically in a Text element using window_create.
I'm also able to destroy these labels using nametowidget. However, the window element of the deleted widgets remains and is returned by dump.
Here is my minimal reproducible example:
import tkinter as tk
root = tk.Tk()
btn_frame1 = tk.Frame(root)
btn_frame1.pack(side='top', fill='x')
create_labels_btn = tk.Button(btn_frame1, text='Create Labels')
create_labels_btn.pack(side='left')
delete_labels_btn = tk.Button(btn_frame1, text='Delete Labels')
delete_labels_btn.pack(side='right')
label_text_field = tk.Text(root, height=7, width=40)
label_text_field.pack(side='top')
dump_text_field_btn = tk.Button(root, text='Dump text field')
dump_text_field_btn.pack(side='top')
dumping_text = tk.Text(root, height=7, width=40)
dumping_text.pack(side='top')
def create_labels():
for i in range(1, 7):
lbl = tk.Label(label_text_field, width=12, text=f'label {i}')
label_text_field.window_create("insert", window=lbl, padx=8, pady=8)
create_labels_btn['command'] = create_labels
def delete_labels():
for lbl in label_text_field.dump("1.0", "end"):
if lbl[0] =='window' and lbl[1]:
label_text_field.nametowidget(lbl[1]).destroy()
delete_labels_btn['command'] = delete_labels
def dump_text_field():
dumping_text.delete("1.0", "end")
for obj in label_text_field.dump("1.0", "end"):
dumping_text.insert('end', str(obj) + '\n')
dump_text_field_btn['command'] = dump_text_field
root.mainloop()
After creating and deleting my labels. The deleted elements are still visible in the dump output (the widget was succesfully deleted though and the remaining name is only an empty string):
How can I cleanly remove the associated window so that they don't add up over time?
You need to remove the items from the Text widget using the index returned by .dump(). But you need to remove them in reverse order, otherwise the index will be wrong after removing the first item.
def delete_labels():
for lbl in label_text_field.dump("1.0", "end")[::-1]: # get the items in reverse order
if lbl[0] =='window' and lbl[1]:
label_text_field.nametowidget(lbl[1]).destroy()
label_text_field.delete(lbl[2]) # remove item from text box as well
Actually you can pass window=1 to dump() to return window items only:
def delete_labels():
for lbl in label_text_field.dump("1.0", "end", window=1)[::-1]:
if lbl[1]:
label_text_field.nametowidget(lbl[1]).destroy()
label_text_field.delete(lbl[2])
I have a list of strings sorted in a tuple like this:
values = ('1.Python','2.Ruby','3.PHP','4.Perl','5.JavaScript')
My simple code is:
from tkinter import *
root = Tk()
values = ('1.Python','2.Ruby','3.PHP','4.Perl','5.JavaScript')
ru = Button(root,
text="Next",
)
ru.grid(column=0,row=0)
lab = Label(root,
text=values[0])
lab.grid(column=1,row=0)
ru2 = Button(root,
text="Previous"
)
ru2.grid(column=2,row=0)
root.mainloop()
I have two tkinter buttons "next" and "previous", the text value of the Label is directly taken from the tuple (text=value[0]), however I would want to know how to show the next string from the tuple when the next button is pressed, and how to change it to the previous values when the "previous" button is pressed. I know it can be done using for-loop but I cannot figure out how to implement that. I am new to python.
Use Button(..., command=callback) to assign function which will change text in label lab["text"] = "new text"
callback means function name without ()
You will have to use global inside function to inform function to assign current += 1 to external variable, not search local one.
import tkinter as tk
# --- functions ---
def set_next():
global current
if current < len(values)-1:
current += 1
lab["text"] = values[current]
def set_prev():
global current
if current > 0:
current -= 1
lab["text"] = values[current]
# --- main ---
values = ('1.Python','2.Ruby','3.PHP','4.Perl','5.JavaScript')
current = 0
root = tk.Tk()
ru = tk.Button(root, text="Next", command=set_next)
ru.grid(column=0, row=0)
lab = tk.Label(root, text=values[current])
lab.grid(column=1, row=0)
ru2 = tk.Button(root, text="Previous", command=set_prev)
ru2.grid(column=2, row=0)
root.mainloop()
BTW: if Next has to show first element after last one
def set_next():
global current
current = (current + 1) % len(values)
lab["text"] = values[current]
def set_prev():
global current
current = (current - 1) % len(values)
lab["text"] = values[current]