我试图利用布莱恩·奥克利的这个出色的答案,但无济于事(https://stackoverflow.com/a/4140988/5060127)......
我想使用相同的方法来验证Spinbox值。我已经定义了from_和to spinbox的值,但是用户仍然可以在其中键入大部分内容......应该验证只有from_-to范围内的值可以由用户输入,并且只有整数。
这里的代码显示了我有多远......
try:
from Tkinter import *
except ImportError:
from tkinter import *
class GUI:
def __init__(self):
# root window of the whole program
self.root = Tk()
self.root.title('ImageSound')
# registering validation command
vldt_ifnum_cmd = (self.root.register(self.ValidateIfNum),'%s', '%S')
# creating a spinbox
harm_count = Spinbox(self.root, from_=1, to=128, width=5, justify='right', validate='all', validatecommand=vldt_ifnum_cmd)
harm_count.delete(0,'end')
harm_count.insert(0,8)
harm_count.pack(padx=10, pady=10)
def ValidateIfNum(self, s, S):
# disallow anything but numbers
valid = S.isdigit()
if not valid:
self.root.bell()
return valid
if __name__ == '__main__':
mainwindow = GUI()
mainloop()
答案 0 :(得分:2)
我想我发现了这个问题。最初使用S=''
调用Validator函数,并且条件S.isdigit()
返回False
,并且不再调用函数。但在我将条件更新为valid = S == '' or S.isdigit()
后,它开始按预期工作。
当然你可能想要一些更复杂的条件(例如检查值是否在范围内),但看起来空字符串必须通过(至少是初始)验证。
答案 1 :(得分:1)
我做到了!将小部件的from_和值考虑在内的整数输入和范围检查都正常工作!它可能看起来有点hacky,但它的工作!这是所有感兴趣的人的代码:
try:
from Tkinter import *
except ImportError:
from tkinter import *
class GUI:
def __init__(self):
# root window of the whole program
self.root = Tk()
self.root.title('ImageSound')
# registering validation command
vldt_ifnum_cmd = (self.root.register(self.ValidateIfNum),'%P', '%S', '%W')
# creating a spinbox
harm_count = Spinbox(self.root, from_=1, to=128, width=5, justify='right', validate='all', validatecommand=vldt_ifnum_cmd)
harm_count.insert(0,8)
harm_count.delete(1,'end')
harm_count.pack(padx=10, pady=10)
def ValidateIfNum(self, user_input, new_value, widget_name):
# disallow anything but numbers in the input
valid = new_value == '' or new_value.isdigit()
# now that we've ensured the input is only integers, range checking!
if valid:
# get minimum and maximum values of the widget to be validated
minval = int(self.root.nametowidget(widget_name).config('from')[4])
maxval = int(self.root.nametowidget(widget_name).config('to')[4])
# check if it's in range
if int(user_input) not in range (minval, maxval):
valid = False
if not valid:
self.root.bell()
return valid
if __name__ == '__main__':
mainwindow = GUI()
mainloop()
我注意到的一件事情并不是很有效,如果您在旋转框中选择整个文本,并粘贴错误的内容,例如文本。这完全破坏了验证。啊。
答案 2 :(得分:0)
我想出了一种适用于任何Entry小部件的解决方案,也适用于SpinBox的解决方案。它使用validate命令确保仅输入正确的值。空白条目会暂时生效,但是在FocusOut上,它会变回最后一个有效值。
intvalidate.py
import tkinter as tk
def int_validate(entry_widget, limits=(None, None)):
"""
Validates an entry_widget so that only integers within a specified range may be entered
:param entry_widget: The tkinter.Entry widget to validate
:param limits: The limits of the integer. It is given as a (min, max) tuple
:return: None
"""
num_str = entry_widget.get()
current = None if (not _is_int(num_str)) else int(num_str)
check = _NumberCheck(entry_widget, limits[0], limits[1], current=current)
entry_widget.config(validate='all')
entry_widget.config(validatecommand=check.vcmd)
entry_widget.bind('<FocusOut>', lambda event: _validate(entry_widget, check))
_validate(entry_widget, check)
def _is_int(num_str):
"""
Returns whether or not a given string is an integer
:param num_str: The string to test
:return: Whether or not the string is an integer
"""
try:
int(num_str)
return True
except ValueError:
return False
def _validate(entry, num_check):
"""
Validates an entry so if there is invalid text in it it will be replaced by the last valid text
:param entry: The entry widget
:param num_check: The _NumberCheck instance that keeps track of the last valid number
:return: None
"""
if not _is_int(entry.get()):
entry.delete(0, tk.END)
entry.insert(0, str(num_check.last_valid))
class _NumberCheck:
"""
Class used for validating entry widgets, self.vcmd is provided as the validatecommand
"""
def __init__(self, parent, min_, max_, current):
self.parent = parent
self.low = min_
self.high = max_
self.vcmd = parent.register(self.in_integer_range), '%d', '%P'
if _NumberCheck.in_range(0, min_, max_):
self.last_valid = 0
else:
self.last_valid = min_
if current is not None:
self.last_valid = current
def in_integer_range(self, type_, after_text):
"""
Validates an entry to make sure the correct text is being inputted
:param type_: 0 for deletion, 1 for insertion, -1 for focus in
:param after_text: The text that the entry will display if validated
:return:
"""
if type_ == '-1':
if _is_int(after_text):
self.last_valid = int(after_text)
# Delete Action, always okay, if valid number save it
elif type_ == '0':
try:
num = int(after_text)
self.last_valid = num
except ValueError:
pass
return True
# Insert Action, okay based on ranges, if valid save num
elif type_ == '1':
try:
num = int(after_text)
except ValueError:
if self.can_be_negative() and after_text == '-':
return True
return False
if self.is_valid_range(num):
self.last_valid = num
return True
return False
return False
def can_be_negative(self):
"""
Tests whether this given entry widget can have a negative number
:return: Whether or not the entry can have a negative number
"""
return (self.low is None) or (self.low < 0)
def is_valid_range(self, num):
"""
Tests whether the given number is valid for this entry widgets range
:param num: The number to range test
:return: Whether or not the number is in range
"""
return _NumberCheck.in_range(num, self.low, self.high)
@staticmethod
def in_range(num, low, high):
"""
Tests whether or not a number is within a specified range inclusive
:param num: The number to test if its in the range
:param low: The minimum of the range
:param high: The maximum of the range
:return: Whether or not the number is in the range
"""
if (low is not None) and (num < low):
return False
if (high is not None) and (num > high):
return False
return True
就这样
import tkinter as tk
from tkinter import ttk
from intvalidate import int_validate
if __name__ == '__main__':
root = tk.Tk()
var = tk.DoubleVar()
widget = ttk.Spinbox(root, textvariable=var, justify=tk.CENTER, from_=0, to_=10)
widget.pack(padx=10, pady=10)
int_validate(widget, limits=(0, 10))
root.mainloop()