Entry Validation with tkinter - python

When I am trying to enter something , its just going to Invalid block, without letting me enter any key. I understand, I can get rid of it by using other options 'focus' , 'focusout' etc. But I want it to work with key :
from tkinter import *
import re
def validate_email(P):
# master.errormsg.config(text='')
x = re.match(r"[^#]+#[^#]+\.[^#]+", P)
print(P)
errormsg.config(text='')
emailentry.focus_set()
return x != None
def invalid_email(P):
errormsg.config(text='Invalid Email Address')
emailentry.focus_set()
if __name__ == '__main__':
master = Tk()
errormsg = Label(master,text='', fg='red')
errormsg.pack()
L1 = Label(master, text='Enter Email Address')
L1.pack(side=LEFT)
vcmd = (master.register(validate_email), '%P')
invcmd = (master.register(invalid_email), '%P')
emailentry = Entry(master, validate="focusout", validatecommand=vcmd,
invalidcommand=invcmd)
emailentry.pack()
Button(master, text="Login").pack()
master.mainloop()

I'm a beginner myself but in running your code and debugging it looks when you use 'key', the entry field is grabbing the value of the key that you are currently pressing on your keyboard. Your two functions are evaluating the value and it's failing every time because it's of coarse not an email address.
If you look in the console you can see that print(P) is printing out whichever key you happen to be pressing at the time.

Related

How to check input in Textbox Tkinter Python?

I am Python coder and got stuck in a question that "How to check input in textbox of tkinter python". The problem is that it is not giving output on writing this code .
def start(event):
a = main.get(1.0,END)
if a == 'ver':
print('.....')
main = Text(root)
main.pack()
root.bind('<Return>',start)
We can do this by get() method:
from tkinter import *
a=Tk()
def check():
print(x.get('1.0',END)[:-1])
x=Text(a)
b=Button(a,text='Check',command=check)
x.pack()
b.pack()
a.mainloop()
The text widget guarantees that there is always a trailing newline. When you do get(1.0,END) you're getting that trailing newline even if the user doesn't enter a newline.
If you want to get exactly what the user entered, use get("1.0", "end-1c"). That will get all of the characters up to the end, minus one character.
Note: text indexes are strings of the form line.character. Your code is using the floating point value 1.0 which is incorrect. It works fine in some cases, but it won't work in all. You should always use a string rather than a float for text indexes.
from tkinter import *
from threading import Thread
root = Tk()
def check():
while True :
a = main.get(1.0,END)[:-1]
if a == 'ver':
print('.....')
main = Text(root)
main.pack()
Thread(target=check).start()
root.mainloop()
You should write something like
def start(event):
t = var.get()
if t == 'something':
pass
var = StringVar()
e = Entry(master, textvariable=var)
e.pack()
e.bind(bind('<Return>',start)

how do i use my validation in a tkinter environment?

from tkinter import *
window = Tk()
window.title("Registration")
window.configure(background="blue")enter code here
Label (window, text= "Firstname: ", bg="blue", fg="white", font="verdana 12 bold") .grid(row=0, sticky=E)
firstname = Entry(window, width=100, bg="white")
firstname.grid(row=0, column=1, sticky=W)
firstname = firstname.get()
firstname = firstname.strip()
firstname = firstname.lower()
Label (window, bg = "blue") .grid(row=2)
Label (window, text= "Surname: ", bg="blue", fg="white", font="verdana 12 bold") .grid(row=3, sticky=E)
surname = Entry(window, width=100, bg="white")
surname.grid(row=3, column=1, sticky=W)
surname = surname.get()
surname = surname.lower()
surname = surname.strip()
Label (window, bg = "blue") .grid(row=4)
Label (window, text = "Pick a number between 0 and 10: ", bg="blue", fg="white", font = "verdana 12 bold") .grid(row=5, sticky=E)
number = Entry(window, width=100, bg="white")
number.grid(row=5, column=1)
while True:
try:
number = number.get()
if (number > 10) or (number < 0): 1/0
except:
print("Sorry, your response must be a number between 0 and 10")
continue
break
window.mainloop()
This is my code so far. I am trying to create a registration system for a quiz I'm making, however, now that I am dealing with GUI, I don't know how to use my validation code in the GUI way/environment. for example, just having "print("sorry, your response must be a number between 0 and 10")" won't and isn't working with my program.
My question: How do I output a message into a textbox like an error message and how do I implement my validation code?
Also, I made my validation code onths ago when I was new to python and used a stack overflow piece of code to help apply it to my program. Anyway, could someone help explain how this code actually works. I don't seem to understand it now and my teacher has struggled to explain it in an understandable way. Specifically the : 1/0 bit. I'm not used to using try and except, I only know how to use for and while loops usually.
Many thanks
Showing messages in tkinter (1st question)
To show the user basic massages and get basic options (show an error, ask OK/Cancel or yes/no...) you can use tkinter.messagebox. It provides show*() and ask*() functions. In your case, showerror() or showwarning() is probably best.
To get basic input, tkinter.simpledialog can be used. It provides the functions askinteger, askfloat and askstring that will ask the user for the respective data type.
To get file (path) input, use tkinter.filadialog.
For more complex situations, it is best you use the tkinter.Toplevel widget.
2nd question
Your code
I am going to play interpreter and go through your code. If you just want the solutions (not recommended), jump below.
firstname = Entry(...) => create an Entry widget and assign it to firstname
firstname.grid(...) => put the widget in/on the window
firstname = firstname.get() => get the text currently in the widget and assign it to firstname.
OK, you want to get the text. Just, the window isn't even visible yet. These instructions will work in the IDLE shell, because of special reasons and you wait to call .get() until you typed your name. In "real" execution, the interpreter won't wait and your user can't type (because there isn't a window) before you call .mainloop(). One solution, if you read above, is to use simpledialog. But also this should run after the GUI started, i.e. after .mainloop() is called. I'll get to that part later.
-- same for surname --
Your validation
Interpreter:
number = Entry(...) => create a new Entry widget and assign it to number
number.grid(...) => show it
# while True here
# try here
number = number.get() => call number.get() and assign the value (a str) to number -- keep that in mind
# if condidtion below
number > 10 => check if a str is larger/greater than an int; can't do that, raise a TypeError
# error -> go to the except
print("I asked how to do this on SO") => show this (in the console); in future hopefully via messagebox
continue => go to the beginning of the loop
# in try
number = number.get() => call the .get() method of the str number; can't find it, raise an AttributeError
# error -> go to the except
print(...) => as before
continue => as before
You get caught in an infinite loop of exception that won't stop even if the user enters a correct number (which can't happen anyway, we don't have a window yet). This is a very good reason for avoiding a bare except - you will also catch lots of stuff you don't want.
Why the method you are trying to use wooooould work (you said you found it here -- do you still have a link or remember the title?):
Code (this example in the console for simplicity):
while True:
try:
value = int(input())
if not 0<value<10:
1/0
except ZeroDivisionError: # let ValueError through (you willl want to catch it separately)
print('by executing 1/0, I raised a ZeroDivisionError. This code therefore will execute.')
else:
print('Everything did fine. I will exit the loop to continue.')
break
Interpreter:
# loop
# in try
value = int(input()) => read input and try to convert to an int; we assume this doesn't fail.
# if condition
not 0<value<10 => is value not between 0 and 10? (we assume it isn't)
# if block
1/0 => what?!? I can't do that, I'll raise a ZeroDivisionError
# except block
print(...) => show the text
# loop again
# in try
value = int(input()) => as above
# if condition
not 0<value<10 => we assume value is between 0 and 10 (evaluetes to False)
# else part of the try...except...else
print(...) => show text
break => exit the loop
You intentionally perform 1/0, which will raise a ZeroDivisionError and act on that in the except. Since you said you don't usually do it, I recommend you try to understand what it does.
How to do it better
Make the window appear before expecting user input: Put all code that should execute at application start in a function and either delay it with tkinter.Tk.after (window.after) or add a nice "Start!"
Button.
Don't (ab)use exceptions when a simple if will do (if you really want to (show off), define your own class MyFancyException(Exception): pass)
Look up on concepts you don't understand before using them and insert comments to remind you if something is so complicated you're afraid you won't remember later.
.
import tkinter as tk
from tkinter.simpledialog import askstring, askinteger
from tkinter.messagebox import showwarning
def do_stuff(first_name, surname, number):
...
def start():
# if you want to use Entry, add a "Submit" Button
first_name = askstring('Title', 'first name:')
surname = askstring('Title', 'last name:')
while True: # ask* return None when cancel is pressed
number = askinteger('Title', 'insert a number between 0 and 10:')
if number is not None and 0<number<10: # what we want
break
do_stuff(first_name, surname, number)
# GUI preparation code
window = tk.Tk()
button_start = tk.Button(window, text='Start!', command=start)
button_start.pack() # for use with other widgets (that use grid), you must .grid() here
window.mainloop() # the GUI appears at this moment

Python Tkinter Use Entry Field to Create MessageBox

I'm somewhat new to Tkinter with minor Python experience so I hope the answer's not too obvious, I've attempted to search for an answer but couldn’t find anything helpful. Essentially I'm trying to build a program where (as a placeholder test for now), if a user enters 1 in the entry field and hits submit, a window appears telling them they typed 1, otherwise they're told to type 1. If my understandings correct, this should work:
from Tkinter import *
#-----------------------------------------------------------
import tkMessageBox
root = Tk()
#-----------------------------------------------------------
root.title('Payroll System')
#-----------------------------------------------------------
def on_printtext(root):
global entryform
string = entryform.get()
if string == 1:
tkMessageBox.showinfo('You typed 1')
elif string != 1:
tkMessageBox.showinfo('Please type 1')
#-----------------------------------------------------------
entryform = Entry(root)
entryform.pack()
submit = Button(root, text="Submit", command=on_printtext)
submit.pack()
root.mainloop()
However when I try to run it and enter 1 in the entry form after hitting submit I get this:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1489, in call
return self.func(*args)
TypeError: on_printtext() takes exactly 1 argument (0 given)
The issue is that tkinter is trying to call the function registered as the command for the button without any arguments, but your function has 1 argument - root without any default variable, and hence that is causing the issue you are having.
Also some other issues in your code -
Entry.get() returns a string, but you are trying to compare that against an integer , it would never be equal, so even if you enter 1 it would still show Please type 1 .
When you do - tkMessageBox.showinfo('You typed 1') - you are actually setting the title to You typed 1 , not the actual message. For the functions of tkMessageBox, the first argument is the title, and the second argument is the message. If you want that as the message, set it as themessage` using keyword argument. Example -
tkMessageBox.showinfo(message='You typed 1')
Example code that works -
from Tkinter import *
import tkMessageBox
root = Tk()
root.title('Payroll System')
def on_printtext():
global entryform
strng = entryform.get()
if strng == '1':
tkMessageBox.showinfo(message='You typed 1')
else:
tkMessageBox.showinfo(message='Please type 1')
entryform = Entry(root)
entryform.pack()
submit = Button(root, text="Submit", command=on_printtext)
submit.pack()
root.mainloop()
If you are using Python 3.x, the code above does not work since tkMessageBox has been changed to messagebox.
Here's the modified code:
from tkinter import * # modif 1 Tkinter with minus t !
import tkinter.messagebox # modif 2:tkMessageBox no longer valid
root = Tk()
root.title('Payroll System')
def on_printtext():
global entryform
strng = entryform.get()
if strng == '1':
tkinter.messagebox.showinfo(message='You typed 1') # modif 3
else:
tkinter.messagebox.showinfo(message='Please type 1') # modif 4
entryform = Entry(root)
entryform.pack()
submit = Button(root, text="Submit", command=on_printtext)
submit.pack()
root.mainloop()

Python Tkinter label widget doesn't update

Here's a piece of simplified code which doesn't work as i want it to:
def get_tl(self,x):
self.var_tl = IntVar()
if x == "Random (max = 6)":
self.var_tl.set(randint(1,6))
else:
ask_tl = Toplevel()
def destroy_t_set_tl():
self.var_tl.set(entry_tl_t.get())
ask_tl.destroy()
label_tl_t = Label(ask_tl, text="length:").pack(side=LEFT)
entry_tl_t = Entry(ask_tl, width=25)
entry_tl_t.pack(side=LEFT)
button_enter_tl_t = Button(ask_tl, text="Enter", command=destroy_t_set_tl).pack(side=LEFT)
self.label_tl = Label(self, text="length:").grid(row=1,column=0)
# This only shows the right number when "Random (max = 6)". When "Manual" it shows 0
self.show_tl = Label(self, text=self.var_tl.get()).grid(row=1,column=1)
def get_values(self):
# This always shows the right number.
self.total_label = Label(self, text=self.var_tl.get()).grid(row=4,column=0)
The function get_tl is called by an OptionMenu widget which gives x the values: "Manual" or "Random (max = 6)".
When this function is called I want it to choose a random number or open a Toplevel window which ask the user a number through an Entry. After the random number is chosen or the user has given a number. The number needs to be displayed as a label so the user can see if the number is correct.
The label only show the right number when "Random (max = 6)". When "Manual" it shows 0
After a button is pressed the function get_values is called. This however does give the right number regardless if it is manual or random.
I'm probably making a simple mistake here. But I fail to see it.
In this part:
def get_tl(self,x):
self.var_tl = IntVar()
You're recreating the variable over and over again, so it holds the default value of 0, as explained in the documentation:
VALUE is an optional value (defaults to 0)
Then you set the variable only if x == "Random (max = 6)", so in all other cases it will remain at its default.
Possibly you want to remove this line:
self.var_tl = IntVar()
You should have it only in the constructor of your class. Then all your methods will share the same instance pointed by self.var_tl.
This seems to be the answer to my own question:
def get_tl(self,x):
def tl():
self.label_tl = Label(self, text="length:").grid(row=1,column=0)
self.show_tl = Label(self, text=self.var_tl.get()).grid(row=1,column=1)
if x == "Random (max = 6)":
self.var_tl.set(randint(1,6))
tl()
else:
ask_tl = Toplevel()
def destroy_t_set_tl():
self.var_tl.set(entry_tl_t.get())
ask_tl.destroy()
tl()
label_tl_t = Label(ask_tl, text="length:").pack(side=LEFT)
entry_tl_t = Entry(ask_tl, width=25)
entry_tl_t.pack(side=LEFT)
button_enter_tl_t = Button(ask_tl, text="Enter", command=destroy_t_set_tl).pack(side=LEFT)
def get_values(self):
self.total_label = Label(self, text=self.var_tl.get()).grid(row=4,column=0)
Now both the option "Manual" and "Random" will call the function tl() which will show the number so the user can check it.
I also moved the self.var_tl = IntVar() to the constructor of the class. It might not be the optimal solution but for me it works.

First letters of a Tkinter input

My program should check if the first three letters of the input word are similar to a predefined word.
I've made a GUI with Tkinter and want to get the letters of the input field.
Somehow I can't implement it in like I would do without Tkinter.
That's how I do it just for the shell:
text = raw_input('Enter a word: ')
if (text[0] + text[1] + text[2] == 'sag'):
print "sagen"
else:
print "error"
So, when I input the word "sagst" it checks the first three letters and should put out "sagen". Works fine.
I learned that e.g. inputfield.get() gets the input of the entry "inputfield".
But how can I check the first letters of that "inputfield"?
A small selection:
from Tkinter import*
root = Tk()
def check():
if (text[0] + text[1] + text[2] == 'sag'):
print "True"
else:
print "False"
inputfield = Entry(root)
inputfield.pack()
but = Button(root,text='Check!', command = check)
but.pack()
text = inputfield.get()
root.mainloop()
Does not work...
I hope you can understand my question and will answer soon. (Sorry for my bad english and my bad Python skills) ;-)
Thanks!
Your check function will have to retrieve the textfield after the button has been pressed:
def check():
text = inputfield.get()
print text.startswith('sag')
I've changed your test a little, using .startswith(), and directly printing the result of that test (print will turn boolean True or False into the matching string).
What happens in your code is that you define inputfield, retrieve it's contents (obviously empty), and only then show the TKInter GUI window by running the mainloop. The user never gets a chance to enter any text that way.
You can also check this without the need for a button (Now it will check whenever the user presses "Enter"):
from Tkinter import *
root = Tk()
def check(*event):
text = inputfield.get()
print text.startswith('sag')
inputfield = Entry(root)
inputfield.bind('<Return>',check)
inputfield.pack()
root.mainloop()
You can also do other things to have your widget validate the entry as you type. (The link is old, but it also points to newer features that allow you to do this without subclassing).
You're not actually putting the value in the input field into the text variable.
I renamed the value from text to input_text because it was confusing to me. I also changed from using text[0] + text[1] + text[2] to using startswith(). This will keep you from getting IndexErrors on short strings, and is much more pythonic.
from Tkinter import*
root = Tk()
def check():
input_text = inputfield.get()
if input_text.startswith('sag'):
print "True"
else:
print "False"
inputfield = Entry(root)
inputfield.pack()
input_text = inputfield.get()
print input_text # Note that this never prints a string, because it only prints once when the input is empty.
but = Button(root, text='Check!', command=check)
but.pack()
root.mainloop()
The key change is that the check function needs to actually get the value in the inputfield.
Here is a version which uses an Entry widget which validates its contents as the user types (so the user does not have to click a button or even press Return).
import Tkinter as tk
class MyApp(object):
'''
http://effbot.org/zone/tkinter-entry-validate.htm
http://effbot.org/tkinterbook/entry.htm
http://www.tcl.tk/man/tcl8.5/TkCmd/entry.htm#M-validate
'''
def __init__(self, master):
vcmd = (master.register(self.validate),
'%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
self.entry = tk.Entry(master, validate = 'key',
validatecommand = vcmd)
self.entry.pack()
self.entry.focus()
def validate(self, action, index, value_if_allowed,
prior_value, text, validation_type, trigger_type, widget_name):
dtype = {'0':'delete', '1':'insert', '-1':'other'}[action]
n = min(3, len(value_if_allowed))
valid = False
if dtype == 'insert':
if value_if_allowed[:n] == 'sag'[:n]: valid = True
else: valid = False
else: valid = True
print(valid)
return True
root = tk.Tk()
app = MyApp(root)
root.mainloop()

Categories

Resources