I am attempting to validate the text box field so that the user can only insert integers, although i have used a while loop to attempt and cannot figure it out I keep getting errors. Please help.
from tkinter import *
import tkinter as tk
from tkinter.tix import *
# setup the UI
root = Tk()
# Give the UI a title
root.title("Distance converter Miles to Kilometers")
# set window geometry
root.geometry("480x130")
# setup the buttons
valRadio = tk.IntVar()
myText=tk.StringVar()
e1 =tk.IntVar()
def calculate(*arg):
while True:
try:
if valRadio.get() == 1:
# get the miles ( Calculation )
res = round(float(e1.get()) / 1.6093,2)
# set the result text
myText.set( "Your input converts to " + str(res) + " Miles")
break
if valRadio.get() == 2:
# get the kilometeres
res = round(float(e1.get()) * 1.6093,2)
# set the result text
myText.set( "Your input converts to " + str(res) + " Kilometers")
break
if ValueError:
myText.set ("Please check selections, only Integers are allowed")
break
else:
# print error message
res = round(float(e1.get()) / 1.6093,2)
myText.set ("Please check selections, a field cannot be empty")
break
except ValueError:
myText.set ("Please check selections, a field cannot be empty")
break
# Set the label for Instructions and how to use the calculator
instructions = Label(root, text="""Hover me:""")
instructions.grid(row=0, column=1)
# set the label to determine the distance field
conversion = tk.Label( text=" Value to be converted :" )
conversion.grid(row=1,column = 0,)
# set the entry box to enable the user to input their distance
tk.Entry(textvariable = e1).grid(row=1, column=1)
#set the label to determine the result of the program and output the users results below it
tk.Label(text = "Result:").grid(row=5,column = 0)
result = tk.Label(text="(result)", textvariable=myText)
result.grid(row=5,column=1)
# the radio button control for Miles
r1 = tk.Radiobutton(text="Miles",
variable=valRadio, value=1).grid(row=3, column=0)
# the radio button control for Kilometers
r2 = tk.Radiobutton(text="Kilometers",
variable=valRadio, value=2).grid(row=3, column=2)
# enable a calculate button and decide what it will do as well as wher on the grid it belongs
calculate_button = tk.Button(text="Calculate \n (Enter)", command=calculate)
calculate_button.grid(row=6, column=2)
# deploy the UI
root.mainloop()
I have attempted to use the While loop inside the code although I can only get it to where if the user inputs text and doesn't select a radio button the error will display but I would like to have it where the text box in general will not allow anything but integers and if it receives string print the error as it does if the radio buttons aren't selected.
define validation type and validatecommand. validate = key makes with every key input it runs validatecommand. It only types if that function returns true which is 'validate' function in this case.
vcmd = (root.register(validate), '%P')
tk.Entry(textvariable = e1,validate="key", validatecommand=vcmd).grid(row=1, column=1)
this is the validation function
def validate(input):
if not input:
return True
elif re.fullmatch(r'[0-9]*',input):
return True
myText.set("Please check selections, only Integers are allowed")
return False
it return true only when its full of numbers([0-9]* is an regular expression which defines all numbers) or empty. If it contains any letter it return False any it denied this way.
Also do not forget to imports
import re
The following code works for requesting input from a user through the Tkinter GUI and turning that input into a usable variable in the main script. However, any value that I put as the last in a list in the if statement (here "4") will hang and crash the program upon enter. This was also the case for "n" in a yes/no scenario. It also happens if I replace the if statement with a while not in [values] - the final value will crash the program. Is this just a quirk of Tkinter or is there something that I am missing?
import tkinter as tk
from tkinter import *
# get choice back from user
global result
badinput = True
while badinput == True:
boxwidth = 1
result = getinput(boxwidth).strip().lower()
if result in ['1', '2', '3', '4']:
badinput = False
# iterate through play options
if result == '1':
# Do Something
elif result =='2':
# Do Something
elif result =='3':
# Do Something
else:
# Do Something
def getinput(boxwidth):
# declaring string variable for storing user input
answer_var = tk.StringVar()
# defining a function that will
# get the answer and set it
def user_response(event):
answer_var.set(answer_entry.get())
return
answer_entry = tk.Entry(root, width = boxwidth, borderwidth = 5)
# making it so that enter calls function
answer_entry.bind('<Return>', user_response)
# placing the entry
answer_entry.pack()
answer_entry.focus()
answer_entry.wait_variable(answer_var)
answer_entry.destroy()
return answer_var.get()
In case anyone is following this question, I did end up solving my problem with a simple if statement within the callback. I can feed a dynamic "choicelist" of acceptable responses into the callback upon user return. If the answer is validated, the gate_var triggers the wait function and sends the program and user response back into the program.
'''
def getinput(boxwidth, choicelist):
# declaring string variable for storing user input
answer_var = tk.StringVar()
gate_var = tk.StringVar()
dumplist = []
# defining a function that will
# get the answer and set it
def user_response(event):
answer_var.set(answer_entry.get())
if choicelist == None:
clearscreen(dumplist)
gate_var.set(answer_entry.get())
return
if answer_var.get() in choicelist:
# passes a validated entry on to gate variable
clearscreen(dumplist)
gate_var.set(answer_entry.get())
else:
# return to entry function and waits if invalid entry
clearscreen(dumplist)
ErrorLabel = tk.Label(root, text = "That is not a valid response.")
ErrorLabel.pack()
ErrorLabel.config(font = ('verdana', 18), bg ='#BE9CCA')
dumplist.append(ErrorLabel)
return
global topentry
if topentry == True:
answer_entry = tk.Entry(top, width = boxwidth, borderwidth = 5)
else:
answer_entry = tk.Entry(root, width = boxwidth, borderwidth = 5)
# making it so that enter calls function
answer_entry.bind('<Return>', user_response)
# placing the entry
answer_entry.pack()
answer_entry.focus()
answer_entry.wait_variable(gate_var)
answer_entry.destroy()
return answer_var.get()
'''
I can't get Label.winfo_exists() to see a Label printed earlier.
I have a window with multiple buttons that all pass data to this routine. On the first pass, this prints Labels on "line A." if another button is pressed (including the first one) this routine runs again, printing the same set of labels on line "A" again, while printing the data from the second button press on line "B." I am trying to delete all previously laid labels before putting another layer down. I would actually have up to line "L" as a maximum.
So I run the routine. On the first pass, I get a NameError at the start (because that label is not there, yet). After I create the label, I do the test again and the winfo_exists() shows properly.
On the second pass, (I have tried the same and different buttons), the first check shows 0 from a NameError again, when it should show 1. After I print the label the second time, it shows 1 again and if I try to delete from another routine, it deletes the 2nd one on top, but not the original label.
This window has lots of other data that I can't just delete the whole window.
rosterloops = len(final_roster)
print(f'Roster Loops {rosterloops}') # Prints 1 on 1st run, 2 on 2nd
ex_test = IntVar()
if rosterloops >=1:
try:
ex_test = roster_unit.winfo_exists()
except NameError:
print('NameError') # Prints on both runs
ex_test = 0
except KeyError:
print('Key Error')
ex_test = 0
print(f'BV Test 1 is {ex_test}') # Prints "0" on both runs
if ex_test == 1:
print('Got here!') # Never gets here
roster_del.destroy()
data = final_roster.get(1)
data_split = data.split("#")
org = data_split[0]
name = data_split[1]
model = data_split[2]
gunnery = int(data_split[7])
pilot = int(data_split[8])
num1 = int(bva)
num2 = float(gpmatrix[gunnery][pilot])
roster_adj_bv = round((num1 * num2))
bv_array[1] = roster_adj_bv
# Name/Model
roster_unit = Label(root, text=f' {name} {model}')
roster_unit.place(x=rosterx + 40, y=rostery, width=230)
# Gunnery/Pilot
roster_gun = Label(root, text=f'{gunnery} /')
roster_gun.place(x=rosterx + 270, y=rostery)
roster_gplus = Button(root, image=pimg, width=20, height=15,
command=lambda rl=1, x=rosterx, y=rostery: upg(root, rl, final_roster,
bv_array, x, y))
try:
ex_test = roster_unit.winfo_exists()
except NameError:
print('NameError')
ex_test = 0
except KeyError:
print('Key Error')
ex_test = 0
print(f'BV Test 1a is {ex_test}') # Prints "1" on both runs
I should get the first winfo_exists() run to show a 0 on the 1st pass, then 1 on the second so the program can get to the .destroy() functions under the "Got here!" print.
I am trying to create a program which only allows you to write a certain amount of characters as a prompt while using a text box. When I try to run the program it returns none in the python shell and doesn't complete the function I would like it to. I would like it to write "your prompt has been posted" if there are under ten characters and write "The prompt is too long" if there are over 10 character. Thanks in advance for the help. It is greatly appreciated
label = Label(tk, text="Prompt:")
label.place(relx=0.1, rely=0.2, anchor=CENTER)
text = Text(tk, width=50, height=6, bg="gray")
text.place(relx=0.62, rely=0.2, anchor=CENTER)
def diary():
print("Why does this not work")
def begin():
while True:
answer = input(text.insert(INSERT, diary))
if len(answer) <= 10:
print("Your prompt has been posted")
else:
print("The prompt is too long")
button = Button(tk, text="Submit", command=begin)
button.place(relx=0.5, rely=0.5, anchor=CENTER)
The code never ends because you told it to run a loop forever without changing anything that would cause it to stop.
Also, whatever you think this code is doing, it's probably not doing it. I count at least three things wrong with this one line of code:
answer = input(text.insert(INSERT, diary))
The input command will read in put from the command line (technically, stdin), which is not something you typically do in a GUI. You are passing it the result of a call to text.insert, but text.insert isn't documented to return anything. Plus, you're giving text.insert a function where it expects a string.
If you want to insert what the function diary returns, you must a) define diary to do something, and b) call it as a function. For example:
def diary():
return "something"
...
text.insert(INSERT, diary())
If your real goal is to have begin get what the user has entered in the GUI and check the length, then you need to remove the while loop and replace the call to insert with get:
def begin():
answer = text.get("1.0", "end-1c")
if len(answer) <= 10:
print("Your prompt has been posted")
else:
print("The prompt is too long")
Initially running the code, blinking will start row wise.
What my software should do is that if the user gives the input "1" in the last row textarea,the blinking should start column wise.
Again if the user give the input "1" then the letter should be selected and should be displayed on the top textarea and entire process should start again
I am not able to control the while loop when the user gives the input in the last row textarea.
I am beginner in python tkinter and I am not able to do what I want exactly.
Thanking You in advance
# your code goes here
import Tkinter
from Tkinter import *
import tkMessageBox
top = Tkinter.Tk()
content=0
def helloCallBack1():
tkMessageBox.showinfo( "Hello Python", "Hello World")
L1 = Label(top, text="Your Text Appears Here")
L1.grid(columnspan=10)
E1 = Entry(top, bd =5,width=40)
E1.grid(columnspan=10)
a1 = Tkinter.Button(top, text ="WATER",width="10", command = helloCallBack1)
a1.grid(row=4,column=0)
B = Tkinter.Button(top, text ="B", command = helloCallBack1)
B.grid(row=4,column=1)
C = Tkinter.Button(top, text ="C",command = helloCallBack1)
C.grid(row=4,column=2)
D = Tkinter.Button(top, text ="D", command = helloCallBack1)
D.grid(row=4,column=3)
E = Tkinter.Button(top, text ="E", command = helloCallBack1)
E.grid(row=4,column=4)
F = Tkinter.Button(top, text ="F", command = helloCallBack1)
F.grid(row=4,column=5)
row1 = Tkinter.Button(top, text =" ", command = helloCallBack1)
row1.grid(row=4,column=6)
a1 = Tkinter.Button(top, text ="ALARM",width="10",bg="red", command = helloCallBack1)
a1.grid(row=5,column=0)
H = Tkinter.Button(top, text ="H", command = helloCallBack1)
H.grid(row=5,column=1)
I = Tkinter.Button(top, text ="I", command = helloCallBack1)
I.grid(row=5,column=2)
J = Tkinter.Button(top, text ="J", command = helloCallBack1)
J.grid(row=5,column=3)
K = Tkinter.Button(top, text ="K", command = helloCallBack1)
K.grid(row=5,column=4)
L = Tkinter.Button(top, text ="L", command = helloCallBack1)
L.grid(row=5,column=5)
row2 = Tkinter.Button(top, text =" ", command = helloCallBack1)
row2.grid(row=5,column=6)
a1 = Tkinter.Button(top, text ="FOOD",width="10", command = helloCallBack1)
a1.grid(row=6,column=0)
N = Tkinter.Button(top, text ="N", command = helloCallBack1)
N.grid(row=6,column=1)
O = Tkinter.Button(top, text ="O",command = helloCallBack1)
O.grid(row=6,column=2)
P = Tkinter.Button(top, text ="P", command = helloCallBack1)
P.grid(row=6,column=3)
Q = Tkinter.Button(top, text ="Q",command = helloCallBack1)
Q.grid(row=6,column=4)
R = Tkinter.Button(top, text ="R", command = helloCallBack1)
R.grid(row=6,column=5)
row3 = Tkinter.Button(top, text =" ", command = helloCallBack1)
row3.grid(row=6,column=6)
a4 = Tkinter.Button(top, text ="BACKSPACE",width="10", command = helloCallBack1)
a4.grid(row=7,column=0)
S = Tkinter.Button(top, text ="S", command = helloCallBack1)
S.grid(row=7,column=1)
T = Tkinter.Button(top, text ="T", command = helloCallBack1)
T.grid(row=7,column=2)
U = Tkinter.Button(top, text ="U", command = helloCallBack1)
U.grid(row=7,column=3)
V = Tkinter.Button(top, text ="V", command = helloCallBack1)
V.grid(row=7,column=4)
W = Tkinter.Button(top, text ="W", command = helloCallBack1)
W.grid(row=7,column=5)
row4 = Tkinter.Button(top, text =" ", command = helloCallBack1)
row4.grid(row=7,column=6)
L2 = Label(top, text="Press 1 when you want to select")
L2.grid(columnspan=10)
E2 = Entry(top, bd =5,width=40)
E2.grid(columnspan=10)
content = E2.get()
content=0;
i=0;j=0;
while(i<30):
row1.after(4000*j+1000*i, lambda: row1.config(fg="red",bg="black"))
row1.after(4000*j+1000*(i+1), lambda: row1.config(fg="grey",bg=top["bg"]))
row2.after(4000*j+1000*(i+1), lambda: row2.config(fg="red",bg="black"))
row2.after(4000*j+1000*(i+2), lambda: row2.config(fg="grey",bg=top["bg"]))
row3.after(4000*j+1000*(i+2), lambda: row3.config(fg="red",bg="black"))
row3.after(4000*j+1000*(i+3), lambda: row3.config(fg="grey",bg=top["bg"]))
row4.after(4000*j+1000*(i+3), lambda: row4.config(fg="red",bg="black"))
row4.after(4000*j+1000*(i+4), lambda: row4.config(fg="grey",bg=top["bg"]))
content=E2.get()
if content==1:#this is not working
break
i=i+1
j=j+1
top.mainloop()
The problem is that your while loop runs in like a blink of an eye, and you cant input anything meanwhile. Because of the after calls the blinking persist, but that does not mean you are still in your wile loop. The program exited that loop long when you input something into the box.
What i would do is to bind the entry box to a key (like Return) and when the key is pressed check the content of the entry box, and if it is 1 then stop the blinking.
Also you can just bind this whole stuff to the 1 key, and avoid the whole Entry widget stuff
Let's think about what you are trying to accomplish: you are trying to cycle through a list of objects, "blinking" one at a time. You want to first do this for objects one row at a time, and later for objects one column at a time.
I am assuming the only thing different between these two behaviors is which items it is iterating over. So, if you create a general purpose function for "blinking" sequentially through a list of objects, you can simply switch which objects are being "blinked".
How would this work? Let's start by creating a list of things to "blink":
blink_objects = [row1, row2, row3, row4]
The idea is that we want to "blink" these, one at a time. To change what is blinking (to switch from rows to columns, as in your question), you simply need to redefine blink_objects.
How would we go about blinking them? The normal way to do this sort of animation is to create a function that draws one frame of the animation, and then schedules itself to run again after a short period of time. In your case that period of time is one second.
Let's call this function blink. We want it to take a couple of optional args, which will be of use later. The first is a variable that contains the object that is currently in a "blink" state. We need this so that we can change it back. The second is a flag that we can use to stop the animation in the future.
Here's what the function looks like. The current_object parameter is passed internally by blink and shouldn't be set when calling blink from somewhere else.
def blink(current_object = None, stop=False):
global blink_objects
# "unblink" the current object
if current_object:
current_object.configure(fg="black", bg=top["bg"])
current_object = None
if not stop:
# blink the first item in the list of objects,
# then move the object to the end of the list
current_object = blink_objects.pop(0)
blink_objects.append(current_object)
current_object.configure(bg="black", fg="red")
# schedule the blink again after a second
current_object.after(1000, blink, current_object)
You only need to call this function once. It will take care of calling itself again every second until the end of time. To get this to blink like your original program, we simply need to replace your whole while loop with these two lines of code:
blink_objects = [row1, row2, row3, row4]
blink()
If at any time you want to stop the animation, you can call blink(stop=True). You might want to do that when the user quits your program, for example.
Next, we need to set it up so that typing "i" changes what is blinking. We can do that by setting a specific binding that will fire immediately when the user presses the key:
E2.bind("<i>", change_blink_objects)
What that says is "if the user presses 'i', call change_blink_objects". Now we just need to define change_blink_objects.
Since this function is called from a binding, it will be passed an object that represents the event (what key was pressed, what object got the keypress, etc). At the moment we don't need that information, but we have to accept it.
Within the function, I'm guessing you'll it to intelligently pick the objects to blink, but I don't know what that logic is, so I'll just have it blink the "alarm" row.
def change_blink_objects(event):
global blink_objects
blink_objects = [H, I, J, K,. L)
That's all you have to do: create a generic function for blinking a list of objects, and a binding that calls a function that changes the list of objects.
Make sure when you're testing this that you first click in the entry widget or it won't see when you type "i".
While loops and GUIs do not mix very well. Youe while loop creates 240 delayed callbacks that you cannot cancel. Instead, you should have one delayed callback that conditionally creates another. Here is an untested replacement for your loop. It incorporates Gabor's answer and should get you started,
go = True
def stop():
go = False
root.bind('<key-1>', stop)
def blink(i, j):
if i = 0:
row1.config(fg="red",bg="black"))
if j > 0:
row4.config(fg="grey",bg=top["bg"]))
elif i = 1:
row1.config(fg="grey",bg=top["bg"]))
row2.config(fg="red",bg="black"))
elif i = 2:
row2.config(fg="grey",bg=top["bg"]))
row3.config(fg="red",bg="black"))
elif i = 3:
row3.config(fg="grey",bg=top["bg"]))
row4.config(fg="red",bg="black"))
if go and j < 30:
top.after(1000, blink, (i+1) % 4, j+1)
top.after(1000, blink, 0, 0)