Reputation: 155
I have a spinbox that has a values ranges from 0-366, it means it only allowed a numeric data type and a backspace. Whenever a user type a character, it automatically deleted if it not a number type. I'm from a C# background and this is my first attempt in Python language. Here is my code.
def validate(event):
charPress=event.keysym
val=sbDays.get() #previous values
if not charPress.isdigit():
sbDays.config(textvariable=StringVar(windows).set(val))
sbDays=tk.Spinbox(frame,from_=0,to=366,borderwidth=sbBorderWidth)
sbDays.place(relx=initialX,rely=yDistance,relwidth=sbWidth)
sbDays.config(validate='all',validatecommand=(windows.register(validate),'% P'))
sbDays.update()
sbDays.bind('<Key>',validate)
From the code above, when I run it, it returns nothing. Since I'm from C# background. This is what I actually need. This is the C# keypress event
public static void TextBox_KeyPress_NumberBackspace(object sender, KeyPressEventArgs e)
{
char keyChar = e.KeyChar;
if (char.IsNumber(keyChar) || char.IsControl(keyChar))
e.Handled = false;
else
e.Handled = true;
}
tbDays.KeyPress += TextBox_KeyPress_NumberBackspace;
Upvotes: 1
Views: 639
Reputation: 385970
You're not using the validatecommand
attribute properly. The function should never directly change the value. The job of the validatecommand
function is to return either True
or False
. If it returns True
the input will be allowed, otherwise the input will be rejected.
If it returns anything else besides True
or False
(including None
) or if the function attempts to change the value, the validation function will be disabled.
You can configure tkinter to send the information you need to determine whether the data is valid or not. You should not be relying on the value returned by get()
, since that will only return the previous value rather than the value being input from the user.
Since you're only wanting to allow integers, and you want to limit the value to a max of 366, the best solution would be to have tkinter pass in the value if the edit is allowed (using %P
), which you can then use to determine whether or not it is valid.
You also should not bind to the <Key>
event, it's unnecessary for the validation to function, and in fact would get in the way of it working.
The function would look something like this:
def validate(new_value):
if new_value == "":
# allow blank entry so user doesn't get frustrated
# when trying to delete the first character
return True
try:
value = int(float(new_value))
except ValueError:
return False
return value >= 0 and value <= 366
You should configure the validatecommand
like the following (notice there's no space between %
and P
):
sbDays.configure(validate='all',validatecommand=(root.register(validate), '%P'))
You will probably need to add a little extra code elsewhere to handle the edge case where the user deletes everything in the widget and doesn't enter anything else. For example, you could use something like value=sbDays.get() or 0
.
Upvotes: 2