0

I'm making a basic BMI calculation program for a class assignment using TKinter for the GUI, and ran into a problem when trying to validate the user's input. I'm trying to only allow numerical input and to deactivate the 'calculate' button and send an error message when the user enters anything that's not a number. However, at the minute it will throw up an error for a single digit number (e.g. 2) but will accept multiple digits (e.g. 23). I'm quite new to this so could you please explain why this is happening, or if there's a better way to write this?

Here are the relevant parts of my code:

#calculate button
cal = ttk.Button(main, text = 'Calculate!')
cal.grid(row = 4, column = 2)

#height entry box
hb = tk.Entry(main, textvariable = height)
hb.grid(row = 2, column = 2)
hb.bind('<Key>', lambda event: val(hb.get()))

#validation error message
vrs = tk.Label(main, text = 'Please enter a number in the box')
vrs.grid(row = 8, column = 2)
#so that its position is saved but won't appear until validation fails
vrs.grid_remove()

#validation function        
def val(value):
    if value.isdigit():
        print('valid')
        vrs.grid_remove()
        cal.state(['!disabled'])
    else:
        print('invalid')
        vrs.grid()
        cal.state(['disabled'])

Thanks in advance for your help.

lemonshark
  • 3
  • 1
  • 2

3 Answers3

0

The first thing you should do to debug this is to print out value inside of val, to see if your assumptions are correct. Validating your assumptions is always the first step in debugging.

What you'll find is that your function is being called before the digit typed by the user is actually inserted into the widget. This is expected behavior.

The simple solution is to put your binding on <KeyRelease>, since the default behavior of inserting the character is on <KeyPress>:

hb.bind('<Any-KeyRelease>', lambda event: val(hb.get()))

Even better would be to use the Entry widget's built-in validation features. For an example, see https://stackoverflow.com/a/4140988/7432

Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
-1

You need to use isdigit on strings.

val = '23'
val.isdigit()  # True
val = '4'
val.isdigit()  # True
val = 'abc'
val.isdigit()  # False

If you're not sure what the type of the input is, cast it first to a string before calling isdigit().

If you want only one-digit numbers, you'll have to check if int(val) < 10

AdrienW
  • 3,092
  • 6
  • 29
  • 59
  • As far as I'm aware the input is a string. Just to check I tried `if str(value).isdigit():`, but that produced the same result. To clarify, I don't want exclusively 1 digit numbers but I don't want them classed as 'invalid' – lemonshark Aug 04 '16 at 15:09
  • The OP _is_ calling isdigit on a string -- just not the string they think. – Bryan Oakley Aug 04 '16 at 15:12
-1

isdigit is a string method. Are you expecting a string, an int, or a float?

You can add some typechecking code like this, so that your program validates regardless of whether the value is a numerical type or a string type.

def val(value):
    if type(value) in (int, float):
       # this is definitely a numerical value
    elif type(value) in (str, unicode, bytes):
       # this is definitely a string
Charles D Pantoga
  • 4,307
  • 1
  • 15
  • 14