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()
Related
just looking for an example, I know its possible with buttons but I wanted to use the different states of a Checkbutton (onvalue and offvalue) to show and hide a label.
You can achieve this using the check button property command to call a function every time user changes the state of the check button.
def show_hide_func():
if cb_val == 1:
your_label.pack_forget()
# if you are using grid() to create, then use grid_forget()
elif cb_val == 0:
your_label.pack()
cb_val = tk.IntVar()
tk.Checkbutton(base_window, text="Click me to toggle label", variable=cb_val , onvalue=1, offvalue=0, command=show_hide_func).pack()
For detailed documentation about the check button and other Tkinter widgets read here
Simply use comman=function to run code which hide (pack_forget()/grid_forget()/place_forget()) and show it again (pack()/grid(...)/place(...)).
With pack() can be the problem because it will show it again but at the end of other widgets - so you could keep Label inside Frame which will not have other widgets. Or you can use pack(before=checkbox) (or simiar options) to put again in the same place (before checkbox)
Label inside Frame
import tkinter as tk
# --- functions ---
def on_click():
print(checkbox_var.get())
if checkbox_var.get():
label.pack_forget()
else:
label.pack()
# --- main ---
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
label = tk.Label(frame, text='Hello World')
label.pack()
checkbox_var = tk.BooleanVar()
checkbox = tk.Checkbutton(root, text="select", variable=checkbox_var, command=on_click)
checkbox.pack()
root.mainloop()
use pack(before=checkbox)
import tkinter as tk
# --- functions ---
def on_click():
print(checkbox_var.get())
if checkbox_var.get():
label.pack_forget()
else:
label.pack(before=checkbox)
# --- main ---
root = tk.Tk()
label = tk.Label(root, text='Hello World')
label.pack()
checkbox_var = tk.BooleanVar()
checkbox = tk.Checkbutton(root, text="select", variable=checkbox_var, command=on_click)
checkbox.pack()
root.mainloop()
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()
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]
I am new to codding in general. I have found a code for a function which restricts users of entering anything but digits in entry field but I can't assign the function to entry object.
I have tree problems.
I can't assign function to entry.
I want error print message to show in label "A".
Pressing "Enter" multiple times execute "def doit(FFF)" multiple times on top of each other, so I want to restrict pressing it more than one time.
I have been trying for the past 3 days but I keep failing.
from tkinter import *
def doit(FFF):
...
def val():
API = IN.get()
while True:
try:
API = int(input())
except ValueError:
print('Not an integer')
continue
else:
return API
break
root = Tk()
root.geometry("300x200")
IN = Entry(root)
IN.bind("<Return>", val, lambda _:doit(FFF))
IN.pack(side=LEFT, fill="both", expand=True)
A = Label(root, fg="red")
A.pack(side=LEFT)
B = Button(root, text="START", fg="black", command=lambda:doit(FFF))
B.pack(side=RIGHT)
root.mainloop()
Example
is_integer() checks only if text is integer number and returns True/False so it can be used in many places.
<KeyRelease> executes validate() after every keypress (or rather key release) and validate() uses is_integer() to check text in Entry (event.widget gives access to widget to which was bind() event) and changes text in Label.
Button executes function run_it() which uses is_integer() to check if Entry has correct integer (to make sure) or you have to check if Entry is not empty. If Entry has no correct integer then it changes text in Label and ends function.
import tkinter as tk
# --- functions ---
def run_it():
# you can check it again to make sure
text = e.get()
if not is_integer(text):
l['text'] = "You can't run it"
return
# do what you want
l['text'] = 'Running ...'
def validate(event):
#text = e.get()
text = event.widget.get()
if is_integer(text):
l['text'] = ''
else:
l['text'] = 'Not an integer'
def is_integer(text):
try:
value = int(text)
return True
except ValueError:
return False
# --- main ---
root = tk.Tk()
e = tk.Entry(root)
e.pack()
#e.bind("<Return>", validate)
e.bind("<KeyRelease>", validate)
l = tk.Label(root)
l.pack()
b = tk.Button(root, text="START", command=run_it)
b.pack()
root.mainloop()
On the Forum there has recently been posted a Question by #Clueless_captain; it was answered by #furas. I am new to stackoverflow so I can not comment in that Conversation. This is the URL to that Conversation: (Tkinter Entry widget stays empty in larger programs (Python 2)). The Code posted by furas is not exactly what I try to do, but the only Thing I can not do myself is finding a way to re-use the Input that has been given in the EntryWidget. I modified the Code written by furas; my Goal was that the Input would be printed before the GUI terminated. For that I bound the Return key to a new Function, this Function was supposed to get the Textstring in this new Function where it would further be processed. It does only do that when I click the Button to get a name for a second time. Is the order of this Code off? I believe the Issue is closely related to the String 'e.bind' on line ten, but I can not find the Issue.
Best Regards, G_Lehmann
---------- The modified code:
from Tkinter import *
def get_input(text, variable):
win = Toplevel()
win.title("Get value")
f = LabelFrame(win, text=text)
f.pack()
e = Entry(win, textvariable=variable)
e.bind("<Return>", do_more(text, variable, e))
e.pack(side="right")
#I tried e.bind here, but This had no Effect.
b = Button(win, text = "Cancel", command=win.destroy)
b.pack()
#do_more is the new Function I want to use to process the Variable 'data' further.
def do_more(text, variable, e):
data = e.get()
print data
print len(data)
print type(data)
def get_value(text, variable):
get_input(text, variable)
# --- main --
root = Tk()
root.title("Ask-name-SUB")
# global variables
var_name = StringVar()
var_address = StringVar()
b = Button(root, text="Enter your name", command=lambda: get_value("Your name:", var_name))
b.pack()
b = Button(root, text="Enter your address", command=lambda: get_value("Your address:", var_address))
b.pack()
b = Button(root, text="Cancel", command=root.destroy)
b.pack()
root.mainloop()
# --- after -- (My Edit: I disabled this as I want to bind the Variables before my GUI gets terminated)
"""
name = var_name.get()
print "Print name, its length, its type"
print name, len(name), type(name)
address = var_address.get()
print "Print address, its length, its type"
print address, len(address), type(address)
"""
bind expects function name (like in command=) so you have to use lambda to assign function with arguments. Besides bind execute function with argument event so you have to receive it.
e.bind("<Return>", lambda event:do_more(variable))
You can assign do_more to button too
b = Button(win, text="OK", command=lambda:do_more(variable))
and do the same with Return and with Button - and close window after do something with variable.
You can also do the same in get_value after you close window but you have to use win.wait_window() because normally tkinter create window and doesn't wait till you close it.
So now you have two possibility to do something with value - in do_more or in get_value - choose one. Both method can be modify - ie. you can use class and create own dialog window.
from Tkinter import *
# --- ---
# global variable to use in different functions
win = None
def get_input(text, variable):
global win # inform function to use global variable `win` to assign value
win = Toplevel()
win.title("Get value")
f = LabelFrame(win, text=text)
f.pack()
# use `f` instead of `win` to put inside LabelFrame
e = Entry(f, textvariable=variable)
e.pack()#side="right")
e.bind("<Return>", lambda event:do_more(variable))
b = Button(win, text="OK", command=lambda:do_more(variable))
b.pack()
def do_more(variable):
data = variable.get()
print 'do_more:', data, len(data), type(data)
win.destroy()
# --- ---
def get_value(text, variable):
# create window
get_input(text, variable)
# this code is executed directly after creating window, not after closing window
# so code has to wait till window is closed
win.wait_window()
data = variable.get()
print 'get_value:', data, len(data), type(data)
# --- main --
root = Tk()
root.title("Ask-name-SUB")
# global variables
var_name = StringVar()
var_address = StringVar()
b = Button(root, text="Enter your name", command=lambda:get_value("Your name:", var_name))
b.pack()
b = Button(root, text="Enter your address", command=lambda:get_value("Your address:", var_address))
b.pack()
b = Button(root, text="Cancel", command=root.destroy)
b.pack()
root.mainloop()