如何在Python中验证输入组件?
我正在用Tkinter创建一个图形用户界面(GUI),用来收集用户的信息。我需要验证以下内容:
- 名字和姓氏只能包含字母、撇号和连字符
- 地址
- 电话号码的位数要正确
- 生日要有效(比如2月31日是不存在的,出生年份要在1900到2014年之间)
- 电子邮件地址要包含'@'和'.'
这是我的代码:
from tkinter import *
import datetime
class tkwindow:
def __init__(self):
window = Tk() # Create window
window.title("Contact Form") # Give window a title
menubar = Menu(window) # Create menu bar
window.config(menu = menubar) # Display menu bar
'''Using the pulldown menu allows easier access to the menu items instead of using a pop-up menu '''
# Create pulldown menu and add to menu bar
messagesMenu = Menu(menubar, tearoff = 0)
menubar.add_cascade(label = "Messages", menu = messagesMenu)
messagesMenu.add_command(label = "Send a Birthday Greeting", command = self.bdayGreeting)
messagesMenu.add_command(label = "Print Address", command = self.printAddress)
# Create another menu option
endMenu = Menu(menubar, tearoff = 0)
menubar.add_cascade(label = "End", menu = endMenu)
endMenu.add_command(label = "Reset Form", command = self.resetForm)
endMenu.add_command(label = "Exit Program", command = window.quit)
# Using Label widget
labelFirst = Label(window, text = "First Name: ").grid(row = 1, column = 1, sticky = E)
labelLast = Label(window, text = "Last Name: ").grid(row = 2, column = 1, sticky = E)
labelAddress = Label(window, text = "Address: ").grid(row = 3, column = 1, sticky = E)
labelPhone = Label(window, text = "Phone Number (8005551234): ").grid(row = 4, column = 1, sticky = E)
labelBday = Label(window, text = "Birthday (MM/DD/YYYY): ").grid(row = 5, column = 1, sticky = E)
labelEmail = Label(window, text = "Email Address (user@domain.com): ").grid(row = 6, column = 1, sticky = E)
# Using Entry widget
self.firstName = StringVar()
entryFirst = Entry(window, textvariable = self.firstName, justify = LEFT).grid(row = 1, column = 2, sticky = W)
self.lastName = StringVar()
entryLast = Entry(window, textvariable = self.lastName, justify = LEFT).grid(row = 2, column = 2, sticky = W)
self.address = StringVar()
entryAddress = Entry(window, textvariable = self.address, justify = LEFT).grid(row = 3, column = 2, sticky = W)
self.phone = StringVar()
entryPhone = Entry(window, textvariable = self.phone, justify = LEFT).grid(row = 4, column = 2, sticky = W)
self.bday = StringVar()
entryBday = Entry(window, textvariable = self.bday, justify = LEFT).grid(row = 5, column = 2, sticky = W)
self.email = StringVar()
entryEmail = Entry(window, textvariable = self.email, justify = LEFT).grid(row = 6, column = 2, sticky = W)
self.errorLblFirst = Label(window, fg = "red")
self.errorLblFirst.grid(row = 1, column = 3)
self.errorLblLast = Label(window, fg = "red")
self.errorLblLast.grid(row = 2, column = 3)
self.errorLblAddress = Label(window, fg = "red")
self.errorLblAddress.grid(row = 3, column = 3)
self.errorLblPhone = Label(window, fg = "red")
self.errorLblPhone.grid(row = 4, column = 3)
self.errorLblBday = Label(window, fg = "red")
self.errorLblBday.grid(row = 5, column = 3)
self.errorLblEmail = Label(window, fg = "red")
self.errorLblEmail.grid(row = 6, column = 3)
# Using Button widget
buttonSubmit = Button(window, text = "Submit", command = self.submit).grid(row = 7, column = 2, sticky = E)
window.mainloop()
def resetForm(self):
self.firstName.set('')
self.errorLblFirst["text"] = ''
self.lastName.set('')
self.errorLblLast["text"] = ''
self.address.set('')
self.errorLblAddress["text"] = ''
self.phone.set('')
self.errorLblPhone["text"] = ''
self.bday.set('')
self.errorLblBday["text"] = ''
self.email.set('')
self.errorLblEmail["text"] = ''
def validFirst(self):
for letter in self.firstName.get():
if not letter.isalpha() and letter not in "'-":
self.errorLblFirst["text"] = " * Letters, apostrophes('), and hypens(-) only"
return False
return True
def validLast(self):
for letter in self.lastName.get():
if not letter.isalpha() and letter not in "'-":
self.errorLblLast["text"] = " * Letters, apostrophes('), and hypens(-) only"
return False
return True
def validAddress(self):
for letter in self.address.get():
if not letter.isalnum() and letter not in "'- .,":
self.errorLblAddress["text"] = " * No special characters"
return False
return True
def validPhone(self):
D = 0
for number in self.phone.get():
if number.isdigit():
D += 1
if D == 10:
return True
else:
self.errorLblPhone["text"] = " * Must be 10-digit phone number without spaces"
return False
return True
def validBday(self):
'''try:
valid_date = datetime.datetime.strptime(str(self.bday), '%m/%d/%Y')
except ValueError:
print('Invalid date!')'''
return True
def validEmail(self):
for letter in self.email.get():
if not letter.isalnum() and letter not in "@.":
self.errorLblEmail["text"] = " * Must have @ and ."
return False
return True
def bdayGreeting(self):
if self.validBday() and self.validFirst() == True:
print("Happy Birthday" + self.firstName.get() + "\n" + self.bday.get())
def printAddress(self):
if self.validFirst() and self.validLast() and self.validAddress() == True:
print(self.firstName.get() + " " + self.lastName.get() + "\n" + self.address.get())
def submit(self):
self.validFirst()
self.validLast()
self.validAddress()
self.validPhone()
self.validBday()
self.validEmail()
tkwindow()
我有几个问题。
- 从def validFirst(self)开始,我该如何验证不同的输入字段?我在修改代码时总是遇到
NameError: name "asdf" is not defined
、SyntaxError: unexpected EOF while parsing
和TypeError: 'int' object is not subscriptable
这些错误,现在还是卡在validFirst(self)上。 - 我在第三列有一组标签用来显示错误信息:
errorLblFirst = Label(window, text = " ", fg = "red").grid(row = 1, column = 3)
。如果验证失败,能不能把它设置为" * Invalid Entry", fg = "red"
?有没有其他方法可以显示错误信息?
谢谢你的帮助!
编辑:我更新了我的最新代码。现在大部分验证都能正常工作,除了validBday
和validEmail
,如果你能帮我看看就好了。
1 个回答
errorLblFirst = Label(...).grid(...)
这样你就把 grid()
的结果赋值给了 errorLblFirst
,但 grid()
总是返回 None
。
总是这样做。
errorLblFirst = Label(...)
errorLblFirst.grid(...)
现在你可以这样做(比如):
errorLblFirst["text"] = " * Invalid Entry"
errorLblFirst["fg"] = "red"
编辑:
你在 validFirst()
的 for i in range(first):
中忘记加 len()
了。
而且你不需要 eval()
。
(在 validPhone()
中你也遇到了同样的问题)
F = 0
fName = True
first = self.firstName.get() # without eval()
for i in range(len(first)): # len()
if first[i].isdigit():
F += 1
if F > 0:
return False
else:
return fName
return fName
不过你可以更简洁地做到这一点。
for letter in self.firstName.get():
if letter.isdigit():
return False
return True
如果你只需要字母(没有空格、逗号等),你可以这样做:
return self.firstName.get().isalpha()
编辑:
我看到名字中可以有撇号和连字符。
for letter in self.firstName.get():
if not letter.isalpha() and letter not in "'-":
return False
return True
编辑: 关于 self.phone
和 StringVar
的问题。
self.phone
是 StringVar()
- 不是 string
,
但是 StringVar()
有 .get()
方法可以获取 string
。
for number in range(len( self.phone.get() )):
顺便说一下:你可以让 for
循环更符合 Python 风格 - 不用 range(len())
。
for char in self.phone.get():
if char.isdigit():
编辑: 关于 validEmail()
和 validBday()
的问题。
邮箱验证需要更多的 if
条件。
比如你可以添加:
email = self.email.get()
if '@' not in email and '.' not in email::
print( 'email needs @ and . at the same time')
但是 '.@' 也会通过这个测试 :/
真正的邮箱验证要复杂得多。
查看有效邮箱的例子: https://fightingforalostcause.net/content/misc/2006/compare-email-regex.php
self.bday
是 StringVar
- 用 self.bday.get()
替代 str(self.bday)
。
编辑:
def validPhone(self):
D = 0
for number in self.phone.get():
if number.isdigit():
D += 1
# outside
if D == 10:
return True
else:
self.errorLblPhone["text"] = " * Must be 10-digit phone number without spaces"
return False
return True
或者甚至可以这样:
def validPhone(self):
# remove previous error message
self.errorLblPhone["text"] = ""
D = 0
for number in self.phone.get():
if number.isdigit():
D += 1
if D != 10:
self.errorLblPhone["text"] = " * Must be 10-digit phone number without spaces"
return False
return True
如果数字只能有 10 位(没有空格、没有 - 等):
def validPhone(self):
# remove previous error message
self.errorLblPhone["text"] = ""
D = True
for number in self.phone.get():
if not number.isdigit():
D = False
break
if not D or len(number) != 10:
self.errorLblPhone["text"] = " * Must be 10-digit phone number without spaces"
return False
return True