How to validate entry in tkinter? - python

I have found some answers to this question but none of the answers have worked on my program.
I am trying to validate the name variable in my code but isalpha() is not a function used in tkinter.
Here is my code so far:
class newUser:
root.title("Sign Up")
header = Label(root, text = "Sign Up!\n")
header.grid(row = 0, column = 0, sticky = E)
global results
results = False
def getName(): #Getting name of the user
global name
nameX = Label(root, text = "Please enter your name: ")
nameX.grid(row = 1, column = 0, sticky = W)
name = Entry(root)
name.grid(row =1, column = 1)
#name = name.get()
if name.isalpha() and name != "":
name = name
#newUser.getAge()
else:
print("Please ensure you have entered your name. Thank you. ")
newUser.getName()
root.mainloop()
newUser.getName()
I tried to use the name.get() but it created an endless loop.
Any ideas?

isalpha() is a built-in function of str objects. Calling isalpha() on name, which was defined to be a tkinter.Entry object will raise an exception because tkinter.Entry has no such function. While you are correct in attempting to use the get() function, which is a function of tkinter.Entry objects and does return a str which supports isalpha() calls, your implementation is a bit off.
You are entering into a recursion because name.get() immediately returns a string which fails the conditional on the name != "" check causing the Python interpreter to fall into the catch-all else clause which calls newUser.getName(), the function we were already in, and the process repeats until you exceed Python's maximum recursion depth. You don't want to call get() on the tkinter.Entry object immediately because that gives the user no time to enter anything. Instead, get() should be called after some event has occurred, such as a submit button being pressed.
Instead, try something like the following:
import tkinter as tk
root = tk.Tk()
class NewUser:
def __init__(self):
self.name = None
root.title("Sign Up")
self.headerLabel = tk.Label (root, text = "Sign Up!\n")
self.nameLabel = tk.Label (root, text = "Please enter your name: ")
self.nameField = tk.Entry (root)
self.submitButton = tk.Button(root, text = "Submit", command = self.saveName)
self.headerLabel.grid (row = 0, column = 0, columnspan = 2)
self.nameLabel.grid (row = 1, column = 0, sticky = "W")
self.nameField.grid (row = 1, column = 1)
self.submitButton.grid(row = 2, column = 0, columnspan = 2, sticky = "EW")
def saveName(self):
name = self.nameField.get()
if name.isalpha() and name != "":
self.name = name
print("Name saved: {}".format(self.name))
else:
print("Please ensure you have entered your name. Thank you.")
user = NewUser()
root.mainloop()
This code will generate the following window:
At this point, if you hit the Submit button, name will be an empty string and will fail the name != "" comparison. Consequently, the following is printed to the console:
Please ensure you have entered your name. Thank you.
However, if you enter your name, assuming you only include alpha characters like so:
The following is printed to the console upon pressing Submit:
Name saved: Erick
And the saved name is now accessible via the self.name member variable of our NewUser class.

Related

nested while loop in python tkinter

The following code searches a text file for a name and displays the related number in a tkinter entry box in Python.
so original text file includes:
bob 19
dan 20
shayne 17
I would like add another nested loop so that if there are two names the same then two numbers are returned to the entry box. Sorry, I am new to Python, have tried but always come up with an error.
bob 18
bob 19
dan 20
shayne 17
#https://www.youtube.com/watch?v=lR90cp1wQ1I
from tkinter import *
from tkinter import messagebox
race = []
def displayInfo(race, name):
found = False
pos = 0
while pos < len(race) and not found:
if race[pos][0] == name:
found = True
pos+=1
if found:
return race[pos-1][1]
else:
messagebox.showerror(message = "Invalid input, please try again.")
def clickArea():
fin.set(displayInfo(race, name.get()))
def createlist():
raceFile = open ("C:/python/files/number_list.txt", 'r')
for line in raceFile:
racer = line.split()
race.append(racer)
raceFile.close()
return race
root = Tk()
root.title("Read From text File/List GUI")
Label(root, text="Name").grid(row=0, column=0)
name = StringVar()
Entry(root, textvariable=name).grid(row=0, column =1)
Label(root, text="Finish Time").grid(row=2, column=0)
fin=IntVar()
Label(root, textvariable=fin).grid(row=2, column=1)
button = Button(root, text="Finish Time", command=clickArea)
button.grid(row=3, column=0, columnspan=2)
createlist()
print(race)
your question is not related to tkinter, so I made the code without it.
It works like this: you enter the name you're looking for, then it looks for matches using the count method. If there is a match, then the index is written to the 'B' array. Further, since there is a space between the name and number, we take the space index + 1 and start outputting the string from this position to the end.
name = input('who do you want to find: ') + " "
with open("number_list.txt", "r") as file:
A = file.readlines()
#remove program entry '\n'
for i in range(len(A)):
A[i] = A[i].strip()
#getting matching names
B = [] #the court records the names we need
for i in A:
if i.count(name): #truth check
#this notation is equivalent to the notationsi: if i.count(name) == 1:
B.append(i)
print('the following numbers match:')
for i in B:
index_space = i.index(' ') + 1
print(i[index_space:])
If you want to get all the values for a name, you need to go through all the items in race:
def displayInfo(race, name):
# go through the race list and return values for given name
found = [x[1] for x in race if x[0] == name]
if found:
# return the values separated by comma
return ", ".join(found)
# no item found for the given name
return "not found"

Validate Python Tkinter name entry by clicking button?

So here I have a program which first displays a information message and then you click next and it tells you to input your name before opening up the main window.
INFO ->(next) ENTER NAME ->(next)
When I enter my name in the entry box I want it to be checked that it does not contain 1.numbers and 2.is not blank. under the validate="key" option it means that once I start typing it validates. But rather I want it to only check the name once i press the NEXT button... If not it will open errorbox()
class errorbox():
def __init__(self):
windowError = Tk()
windowError.title("Error")
windowError.geometry('300x400')
error_message = Label(windowError, font=("Bold", 10), justify="left", text="Please enter a valid name")
def clicked1():
description.configure(text="Please enter your name")
nameBox = Entry(windowSplash, width=20, textvariable=name)
nameBox.place(rely=0.5, x=130, anchor=W)
reg = windowSplash.register(validate)
nameBox.config(validate="none",validatecommand=clicked2)
button2 = Button(text="Next", bg="white", width=5, command=lambda:[clicked2(),validate()])
button2.place(rely=0.5, x=300, anchor=E)
button1.destroy()
def validate(input):
if input.isdigit():
print("Invalid name was entered" + input)
errorbox()
return False
elif input is "":
print("No name entered")
errorbox()
return False
else:
return True
def clicked2():
print(name.get(), "logged in...")
windowSplash.destroy()
windowTool = Tk()
windowTool.title("Radial Measurements Calculator Tool")
windowTool.geometry('300x400')
name = StringVar()
windowSplash.mainloop()
Welcome to Stack Overflow Community.
I might have interpreted your question, but please make sure that next time you provide a minimal, reproducible example when you ask.
Here are a couple of things that I have observed.
The validate function takes input as a parameter, so make sure you pass that in the lambda function by lambda input = name.get(): [clicked2(),validate(input)].
By checking input.isdigit() does not guarantee that there might not be numbers after/between characters, so I suggest you to iterate through the string and check for isdigit()/type() or use re module. Also, an efficient way to check for empty string could be if not name.get():.
If you aim to open the new window only after the validation, I suggest you to call clicked2 from the validate function under a condition and not form the next button, because in this case your return form validate isn't used for anything.

How to use bind "<Return>" to call a function

I know my question sounds very much like many previous questions but I can honestly not figure it out in the context of my program. I have an algorithm for the Collatz Conjecture that I would like to run through a Tkinter GUI (everything works just fine through the terminal).I have tried to bind the relevant function to the Return key and to a button but I get the same error message for both methods of entering data, which I will show below. I get the output to work perfectly on the GUI if I input through the terminal.
What I have tried is best explained through the code below. (The code above the #### line has mostly to do with making the GUI appear over my Spyder IDE and not hiding behind it.)
Code:
from tkinter import *
root = Tk()
import os
import subprocess
import platform
def raise_app(root: Tk):
root.attributes("-topmost", True)
if platform.system() == 'Darwin':
tmpl = 'tell application "System Events" to set frontmost of every process whose unix id is {} to true'
script = tmpl.format(os.getpid())
output = subprocess.check_call(['/usr/bin/osascript', '-e', script])
root.after(0, lambda: root.attributes("-topmost", False))
########################################################################
lst = []
def collatz(num):
while num != 1:
lst.append(num)
if num % 2 == 0:
num = int(num / 2)
else:
num = int(3 * num + 1)
def main(event):
collatz(num)
#Input Box
input = Entry(root, width = 10, bg = "light grey")
input.grid(row = 0, column = 0, sticky = W)
input.get()
input.bind("<Return>", main)
##Button
#button1 = Button(root, width = 10, text = "Run", command = main)
#button1.grid(row = 1, column = 0, sticky = W)
##Output box
output1 = Text(root, width = 100, height = 10, bg = "light grey")
output1.grid(row = 3, column = 0, sticky = W)
output2 = Text(root, width = 50, height = 1, bg = "white")
output2.grid(row = 2, column = 0, sticky = W)
output1.insert(END, lst)
output2.insert(END, "Number of iterations are: " + str(len(lst)))
########################################################################
raise_app(root)
root.mainloop()
When I run the code as is, the input box appears but when I click return, I get an error message:
Exception in Tkinter callback
Traceback (most recent call last):
File "/anaconda3/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
File "/Users/andrehuman/Desktop/Python/programs/Collatz Conjecture/Collatz_alt3.py", line 43, in main
collatz(num)
NameError: name 'num' is not defined
Exactly the same if I try and link a button to the "main" function.
When I comment the input and buttons out, and enter the number through the terminal, everything works as expected. The list of iteration numbers appear in the text box as it should. (And I can even get a Matplotlib graph to display the data visually in the terminal.) If I can get this problem sorted out I want to try and display (or embed) the Matplotlib graph in the GUI.
Anyway, that's it. Any help will be greatly appreciated.
Andre Human
name 'num' is not defined occurs because you're calling collatz(num), but the program does not understand what value you are referring to when you say num. You should assign a value to that name before using it. I assume you want the value to be the contents of your input box.
def main(event):
num = int(input.get())
collatz(num)
You will also need to copy your output1.insert and output2.insert lines to the inside of main. Right now, those lines execute before the window even appears to the user, so there's no way that they can enter a number fast enough to get collatz to trigger before the text gets written. And changing lst after the fact does nothing to the text, since it's not smart enough to notice that the list has changed.
def main(event):
num = int(input.get())
collatz(num)
#delete previous contents of text boxes
output1.delete(1.0, END)
output2.delete(1.0, END)
output1.insert(END, lst)
output2.insert(END, "Number of iterations are: " + str(len(lst)))
Another problem is that successive calls to collatz will cause lst to grow and grow, because the contents of the list from previous calls is still present. Try entering 4 into the text box, and press Enter a few times. The output will go from 2 to 4 to 6... That's not right.
This is something of a natural hazard when using mutable global state. One possible solution is to reset lst at the beginning of each collatz call.
def collatz(num):
lst.clear()
#rest of function goes here
... But I'm more inclined to make lst local to the function, and return it at the end.
def collatz(num):
lst = []
while num != 1:
lst.append(num)
if num % 2 == 0:
num = int(num / 2)
else:
num = int(3 * num + 1)
return lst
def main(event):
num = int(input.get())
lst = collatz(num)
#delete previous contents of text boxes
output1.delete(1.0, END)
output2.delete(1.0, END)
output1.insert(END, lst)
output2.insert(END, "Number of iterations are: " + str(len(lst)))
#later, just before mainloop is called...
#lst doesn't exist in this scope, so just set the text to a literal value
output1.insert(END, "[]")
output2.insert(END, "Number of iterations are: 0")

Create a variable from 2 Tkinter entries

I am trying to get the first three characters of a Tkinter entry, and then add them to another tkinter entry.
For example:
name = Entry=(root, text="Name: ")
age = Entry=(root, text="Age: ")
username = First three characters of name + age
Then I want the first three letters of their name to add to the age to create a user name.
If the user enters 'Taylor' as a 'name' and '13' as a 'age', I want to make a variable called 'username' which would contain 'Tay13'
Not sure if I made it too clear, but hopefully you understand. Thanks
EDIT: Just tried something else and it says 'StringVar' object is not subscriptable.
Just found out how to do it on Reddit. If any one else need this, then here is the answer:
username = name.get()[:3] + age.get()
This gets the first 3 letters from 'name' and adds 'age' on to the end of it.
Thanks to the people who helped.
You can do this to get the username. The [:3] pulls the first 3 letters of a string.
username = str(name[:3]) + str(age)
print username
Below example adds/updates id to users dictionary each time the button is pressed:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
def update_users():
global users, name, age
user_id = name.get()[:3] + age.get()
users[user_id] = None
print(users)
if __name__ == '__main__':
root = tk.Tk()
users = dict()
name = tk.Entry(root)
age = tk.Entry(root)
update_btn = tk.Button(root, text="Update Users", command=update_users)
name.pack()
age.pack()
update_btn.pack()
root.mainloop()
This how to go about this create two entry widget to receive the content in the entry.Then your funtion to print the content in the entry and slice the first three letters of the name and print plus the age.
from tkinter import *
def Print_variable():
e1 = name.get()
e2 = age.get()
print(e1[:3] + e2)
root = Tk()
root.geometry("400x400")
name = StringVar()
e1 = Entry(root, textvariable=name) # this entry accept the name
e1.pack()
age = StringVar()
e2 = Entry(root, textvariable=age) # this entry accept the age
e2.pack()
b = Button(root, text="print name and age variable", command=Print_variable)
b.pack()
root.mainloop()

My login page won't work

I'm trying to make a log in program, but I can't get it to work.
When I'm running the code and type in the right Username and Password, it says wrong input.
import tkinter
window = tkinter.Tk()
window.title('LoginPage')
gebruikersnaam = 'Donald'
wachtwoord = 'Trump'
lblUsername = tkinter.Label(window,text='Username:')
lblUsername.pack(fill=tkinter.X)
entUsername = tkinter.Entry(window)
entUsername.pack(fill=tkinter.X)
lblPassword = tkinter.Label(window,text='Password:')
lblPassword.pack(fill=tkinter.X)
entPassword = tkinter.Entry(window)
entPassword.pack(fill=tkinter.X)
lblResult = tkinter.Label(window)
lblResult.pack(fil=tkinter.X)
def checkLogin(username,password):
if str(username) == gebruikersnaam and str(password) == wachtwoord:
lblResult.configure(text='Login was succesfull')
else:
lblResult.configure(text='Username or Password is wrong')
btn = tkinter.Button(window,text='Login',command= lambda x=entPassword.get(), y=entUsername.get():checkLogin(y,x))
btn.pack(fill=tkinter.X)
window.mainloop()
Right now, your code calls get() methods as soon as it runs, that's why you are getting empty values and "wrong input".
You need to make get() methods run after button click to give user some time to actually enter some values.
So you should pass Entrys as parameters and call get() methods inside of lambda.
btn = tkinter.Button(..., command=lambda x=entUsername, y=entPassword: checkLogin(x.get(), y.get()))
BUT, I think, instead of lambda, you should use a wrapper function, that calls checkLogin. Which makes your code easier to read and follow.
def wrapperCheck():
checkLogin(entUsername.get(), entPassword.get())
btn = tkinter.Button(window, text='Login', command=wrapperCheck)
btn.pack(fill=tkinter.X)
EDIT: Yet, even better approach is (thanks to comment from Bryan Oakley), remove parameters from your method and fetch those values inside of it.
def checkLogin():
username = entUsername.get()
password = entPassword.get()
#since get returns string objects you dont need str calls
if username == gebruikersnaam and password == wachtwoord:
lblResult.configure(text='Login was succesfull')
else:
lblResult.configure(text='Username or Password is wrong')
#also you can remove these temp values and directly compare like below
# entUsername.get() == gebruikersnaam and entPassword.get() == wachtwoord
btn = tkinter.Button(window,text='Login',command= checkLogin)

Categories

Resources