Validation python, Using GUI - python

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

Related

Python tkinter - Change the Label created by a loop, how can I do?

I read a lot for it and I have the sentiment that there is no solution for me.
Context: user has to enter 6-digit calculation number in the entries. The 9x entries are created by a loop. I wrote the condition to detect if the user did a mistake : number which contains less or more than 6 digit or / and contains a letter. They are working, I tested them. Only 6-digit number are accepted (if 0, it's ok since it means that the user just need to get less than 9x documents).
Goal: if the user did one mistake, a message "Error in the number" at the place of the "0" has to appear right next to the concerned and "faulty" entry.
Everything will be activated through a button.
What I tried: with a list, but it doesn't work.
How can I change the Label dynamically created by the loop ?
user_entries=[]
error_list_length=[0,0,0,0,0,0,0,0,0]
error_list_letters=[0,0,0,0,0,0,0,0,0]
error_calculation_list=[0,0,0,0,0,0,0,0,0]
nbofcalc=9
a=0
b=1
ddd=791250
#------ Function to check if the entered calculation number are composed of 6 characters (mandatory)
def check_calc_number():
global gg_error
gg=0
gg_error=0
while gg<nbofcalc:
if len(user_entries[gg].get()) != 6:
if len(user_entries[gg].get()) ==0:
gg_error+=0
error_list_length[gg]=0
else:
gg_error+=1
error_list_length[gg]=1
else:
gg_error+=0
error_list_length[gg]=0
gg+=1
#------ Function to check if the entered calculation number contains a or many letters (prohibited)
def check_calc_letter():
global hh_error
hh=0
hh_error=0
alphabet_x='a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'
for x in range(nbofcalc):
for n in user_entries[hh].get():
if n in alphabet_x:
hh_error+=1
error_list_letters[hh]=1
hh+=1
#------ Function to check with entries has an error and update the list "error_calculation_list" to try to displays the message "Error in the number" next to the concerned entries
def error_length_letters_display():
ww=0
while ww<nbofcalc:
if error_list_length[ww]==1 :
error_calculation_list[ww]="Error"
var_calculation.set(error_calculation_list[ww])
if error_list_letters[ww]==1:
error_calculation_list[ww]="Error"
var_calculation.set(error_calculation_list[ww])
ww+=1
#----- Loop to crate the entries and Label
for x in range(nbofcalc):
cadre1=Frame(fenetre)
cadre1.pack(side=TOP,anchor=NW)
cadre=Frame(cadre1)
cadre.pack()
c=str(b)
calc=Label(cadre,text="Calculation "+c+" ")
calc.pack(side=LEFT)
var_entry=StringVar()
my_entry=Entry(cadre,textvariable=var_entry, bd=5)
my_entry.insert(0,ddd)
my_entry.pack(side=LEFT)
var_calculation=StringVar()
var_calculation.set(error_calculation_list[a])
calc_error_frame=Label(cadre, textvariable=var_calculation) # The Label to change if error
calc_error_frame.pack(side=RIGHT)
user_entries.append(my_entry)
a+=1
b+=1
ddd+=1
Thank you !
You can store values and objects you need to change, so then you can change them dinamically. See this example
from tkinter import *
r = Tk()
my_entries = []
for i in range(5):
e = Label(r, text=i)
my_entries.append(e)
e.pack(side='top')
r.after(4000, lambda: my_entries[2].configure(text='Example'))
r.mainloop()
EDIT 1:As TheLizzard and Cool Cloud pointed out, it's better to avoid using time.sleep() while using tkinter. Replaced with non-blocking after()
you could setup the trace function for tk.StringVar(), when user enter any value into Entry, it will be checked. for example as shown on below user only can type decimal, and you and setup length as well.
def create_widgets(self):
self.vars = tk.StringVar()
self.vars.trace('w', partial(self.validate2, 1, 1))
# Min Voltage Validating Part
self.vars1 = tk.StringVar()
self.vars1.trace('w', partial(self.validate2, 2, 2))
# Max Voltage Validating Part
self.vars2 = tk.StringVar()
self.vars2.trace('w', partial(self.validate2, 3, 4))
# Current Validating Part
self.vars3 = tk.StringVar()
self.vars3.trace('w', partial(self.validate2, 4, 3))
# Channel Validating Part
# function( key, size)
self.enter_info = tk.Label(self, text="Please enter your information: ", bg="lightgrey")
self.enter_info.grid(tke_Table_EnterInfo)
self.voltage = tk.Label(self)
self.voltage["text"] = "MinVoltage"
self.voltage.grid(tke_Label_MinVoltage)
self.voltageInput = tk.Entry(self, width=10, textvariable=self.vars).grid(tke_StringBox_MinVoltage)
self.vars.set(0)
# Min Voltage Validating Part
self.current = tk.Label(self)
self.current["text"] = "MaxVoltage"
self.current.grid(tke_Label_MaxVoltage)
self.currentInput = tk.Entry(self, width=10, textvariable=self.vars1).grid(tke_StringBox_MaxVoltage)
self.vars1.set(5)
# Max Voltage Validating Part
self.power = tk.Label(self)
self.power["text"] = "Current"
self.power.grid(tke_Label_MaxCurrent)
self.powerInput = tk.Entry(self, width=10, textvariable=self.vars2).grid(tke_StringBox_MaxCurrent)
self.vars2.set(62.5)
# Max Current Validating Part
self.channel = tk.Label(self)
self.channel["text"] = "channel"
self.channel.grid(tke_Label_Channel)
self.channelInput = tk.Entry(self, width=10, textvariable=self.vars3).grid(tke_StringBox_Channel)
self.vars3.set(8)
# Max Channel Validating Part
def validate2(self, key, size, *args):
# TODO:add more information
if key == 1:
value = self.vars.get()
elif key == 2:
value = self.vars1.get()
elif key == 3:
value = self.vars2.get()
else:
value = self.vars3.get()
if not value.isdecimal():
print(len(value))
# if len(value) < 2:
corrected = ''.join(filter(str.isdecimal, value))
if key == 1:
self.vars.set(corrected)
elif key == 2:
self.vars1.set(corrected)
elif key == 3:
self.vars2.set(corrected)
else:
self.vars3.set(corrected)
if key == 1:
corrected = self.vars.get()
corrected = corrected[0:size]
self.vars.set(corrected)
elif key == 2:
corrected = self.vars1.get()
corrected = corrected[0:size]
self.vars1.set(corrected)
elif key == 3:
corrected = self.vars2.get()
corrected = corrected[0:size]
self.vars2.set(corrected)
else:
corrected = self.vars3.get()
corrected = corrected[0:size]
self.vars3.set(corrected)

Tkinter/Python hanging on user response if response is the last option in a while or if loop

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

Tkinter variables

I've made a simple GUI in Tkinter.(Code is below)
When I press the "Fetch" botton than I get the text input from the GUI in a Command line.
Let's say that in this example the "user" is putting 0.5 in the "Service Points Won" field and 0.7 in the " Return Points Won" field as input.
When I press the "Fetch" botton I get the following results displayed in command prompt
0.5
0.7
What I would like to achieve is that next to the displayed result in command prompt the matching "Label" is also displayed.
So to return to my example with 0.5 and 0.7 as input. I want to get a result in command prompt as followed.
"Service Points Won" 0.5
"Return Points Won" 0.7
GUI Script
from Tkinter import *
fields = 'Service Points Won', 'Return Points Won'
def fetch(variables):
for variable in variables:
print "%s" % variable.get()
def makeform(root, fields):
lab1= Label(text="Stats", font="Verdana 10 bold")
form = Frame(root)
left = Frame(form)
rite = Frame(form)
lab1.pack(side=TOP)
form.pack(fill=X)
left.pack(side=LEFT)
rite.pack(side=RIGHT)
variables = []
for field in fields:
lab1= Label()
lab = Label(left, text=field)
ent = Entry(rite)
lab.pack(anchor='w')
ent.pack(fill=X)
var = StringVar()
ent.config(textvariable=var)
var.set('0.5')
variables.append(var)
return variables
if __name__ == '__main__':
root = Tk()
vars = makeform(root, fields)
Button(root, text='Fetch',
command=(lambda v=vars: fetch(v))).pack()
root.bind('<Return>', (lambda event, v=vars: fetch(v)))
root.mainloop()
Any suggestions?
Since you want to map some fields to others, using a dictionary is a better choice than using a list.
def fetch(variables):
for key, val in variables.items():
print key, val.get()
def makeform(root, fields):
...
variables = {}
for field in fields:
...
variables[field] = var #associate a field with its variable
return variables
Which will print out
Service Points Won 0.5
Return Points Won 0.7
Note that you can format your print as you wish in your fetch function.

Changing property of a QLE in python

I'm currently having an issue with a QLE that I created. I would like the qle to take a value, turn it into a float and then increase or decrease a label value depending on if the value is positive or negative. The only problem is is that every time I type something into the qle it will start adding that value to the label before I'm done typing. For example: I type in "4" into the qle that works but once I type in "4." anything it will read it as 4 two times so the label will be changed to 8. Maybe there's a way so that when I press a button it will increase or decrease but only after I press the button?
I added code for a button I created and maybe it would be easier to link that with the qle. Many thanks!
#this creates the increase button for cam1 focus
self.btnCam1IncreaseFocus = QtGui.QPushButton("+",self)
self.btnCam1IncreaseFocus.clicked.connect(self.cam1IncreaseFocus)
self.btnCam1IncreaseFocus.resize(25,25)
self.btnCam1IncreaseFocus.move(75,100)
#This creates a textbox or QLE for a custom tweak value for cam1 focus
self.qleTextBoxCam1Focus = QtGui.QLineEdit(self)
self.qleTextBoxCam1Focus.resize(25,25)
self.qleTextBoxCam1Focus.move(40,100)
self.qleTextBoxCam1Focus.textChanged[str].connect(self.qleCam1Focus)
def cam1IncreaseFocus(self):
text = self.lblCam1Focus.text()
n = float(text)
n = n + 1
self.lblCam1Focus.setText(str(n))
def qleCam1Focus(self):
text = self.qleTextBoxCam1Focus.text()
if text == "":
text = "0.0"
if str(text).isalpha() == False:
n = float(text)
textLabel = self.lblCam1Focus.text()
if textLabel == "":
textLabel = "0.0"
y = float(textLabel)
result = n + y
if result <= 0.0:
result = 0.0
self.lblCam1Focus.setText(str(result))
Instead of textChanged, use the editingFinished signal, which will only fire when return/enter is pressed, or when the line-edit loses focus:
self.qleTextBoxCam1Focus.editingFinished.connect(self.qleCam1Focus)

Python program getting stuck

I am new to Python and I am writing a program just for fun. My program consists of three .py files (let's say a.py, b.py, c.py). a will either call on the functions within either b or c, depending on the user's option. After it completes its first round it asks if the user would like to continue or simply exit the program. If they chose continue it asks again whether it should run b or c.
The problem I ran into is that the first time around, a will call the functions in either perfectly fine, it runs smoothly, and then when I select to continue it again calls either function perfectly fine, it will go into the function, but then the function gets stuck in its first step.
The program is not terminating, not giving an error. It accepts the raw_input variable but it will not continue. I was wondering if there was some way to force it to accept the variable and then continue the process (getting it 'unstuck'). I already tried putting pass on the next line. That didn't work.
Here are the steps it takes starting from the request to continue:
Continue = tkMessageBox.askyesno('Cypher Program', 'I have completed the task'
+ '\nWould you like to do anything else?')
## This is in a.py;
if Continue == True:
cyp()
def cyp():
global root
root = Tk()
root.title("Cypher Program")
root['padx'] = 40
root['pady'] = 20
textFrame = Frame(root)
Label(root, text = 'What would you like to do?').pack(side = TOP)
widget1 = Button(root, text = 'Encrypt a file', command = encrypt)
widget1.pack(side = LEFT)
widget2 = Button(root, text = 'Decrypt a file', command = decrypt)
widget2.pack(side = RIGHT)
widget3 = Button(root, text = 'Quit', command = quitr)
widget3.pack(side = BOTTOM)
root.mainloop()
def encrypt():
root.destroy()
encrypt3.crypt()
##Then from there it goes to b.py;
def crypt():
entry('Enter a file to encrypt:', selectFile)
def entry(msg1, cmd):
global top
top = Toplevel() ##changed it to Toplevel
top.title("File Encrypion")
top['padx'] = 40
top['pady'] = 20
textFrame = Frame(top)
entryLabel = Label(textFrame)
entryLabel['text'] = msg1
entryLabel.pack(side = LEFT)
global entryWidget
entryWidget = Entry(textFrame)
entryWidget['width'] = 50
entryWidget.pack(side = LEFT)
textFrame.pack()
button = Button(top, text = "Submit", command = cmd)
button.pack()
button.bind('<Return>', cmd)
top.mainloop()
def selectFile():
if entryWidget.get().strip() == "":
tkMessageBox.showerror("File Encryption", "Enter a file!!")
else:
global enc
enc = entryWidget.get().strip() + '.txt'
top.destroy() ##gets stuck here
##This is the rest of crypt(). It never returns to the try statement
try:
view = open(enc)
except:
import sys
sys.exit(badfile())
text = ''
You need to restructure your code to only create the root window once, and only call mainloop once. Tkinter is not designed to be able to create and destroy the root multiple times in a single process.
If you need multiple windows, create additional windows with the Toplevel command.

Categories