2

I am in need of a function that retricts the input of the user to numeric values. I've been looking for an answer but the one I've found does not allow for '-', '+' and ','(comma). Here's the code for the validation method:

   def __init__(self, master1):
    self.panel2 = tk.Frame(master1)
    self.panel2.grid()
    vcmd = (master1.register(self.validate),
            '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
    self.text1 = tk.Entry(self.panel2, validate = 'key', validatecommand = vcmd)
    self.text1.grid()
    self.text1.focus()



def validate(self, action, index, value_if_allowed,
    prior_value, text, validation_type, trigger_type, widget_name):
    # action=1 -> insert
    if(action=='1'):
        if text in '0123456789,-+':
            try:
                float(value_if_allowed)
                return True
            except ValueError:
                return False
        else:
            return False
    else:
        return True

Again, this only seems to work with numbers, but restricts commas and plus and minus signs, which is not intended. How could this be fixed?

AlBud
  • 35
  • 1
  • 7
  • What have you done to debug this? I see you have a try/except where you're ignoring the error. Perhaps you should print out the error so that you understand why it's rejecting your characters. – Bryan Oakley Sep 08 '17 at 12:33
  • If the user types `5,`, is that allowed? – Bryan Oakley Sep 08 '17 at 12:57
  • @BryanOakley The five would be allowed, but the comma woud not appear on the entry widget on the GUI. This script does not print out an error. What it does is, it does not allow non numerical characters as well as commas, + and - to be displayed. – AlBud Sep 08 '17 at 13:46
  • I don't quite understand that response. Your question implies that commas are allowed. – Bryan Oakley Sep 08 '17 at 13:47
  • I want them to be allowed, but they are not. That's the issue. – AlBud Sep 08 '17 at 13:48
  • Ok, so you're saying that `5,` should be allowed. What about `5,,` or `.5,`? – Bryan Oakley Sep 08 '17 at 13:49
  • I see what you mean. Maybe what needs to be done is first, a function that restricts input to numbers and commas and then a function that processes the resulting string so as to evitate things like '5,,' or '.5,'. I'll have to work on this... :) – AlBud Sep 08 '17 at 14:06

1 Answers1

3

The right tool is definitely the re module.

Here is a regular expression that should do the job:

(\+|\-)?\d+(,\d+)?$

Let's break it down:

(\+|\-)?\d+(,\d+)?$

 \+|\-                  Starts with a + or a -...
(     )?                ... but not necessarily
        \d+             Contains a repetition of at least one digits
            ,\d+        Is followed by a comma and at least one digits...
           (    )?      ... but not necessarily
                  $     Stops here: nothing follows the trailing digits

Now, your validate function only has to return True if the input matches that regex, and False if it does not.

def validate(string):
    result = re.match(r"(\+|\-)?\d+(,\d+)?$", string)
    return result is not None

Some tests:

# Valid inputs
>>> validate("123")
True
>>> validate("123,2")
True
>>> validate("+123,2")
True
>>> validate("-123,2")
True

# Invalid inputs
>>> validate("123,")
False
>>> validate("123,2,")
False
>>> validate("+123,2,")
False
>>> validate("hello")
False

Edit

I understand now that you want to check in real-time if the input is valid. So here is an example of what you can do:

import tkinter as tk
import re

def validate(string):
    regex = re.compile(r"(\+|\-)?[0-9,]*$")
    result = regex.match(string)
    return (string == ""
            or (string.count('+') <= 1
                and string.count('-') <= 1
                and string.count(',') <= 1
                and result is not None
                and result.group(0) != ""))
    
def on_validate(P):
    return validate(P)    
    
root = tk.Tk()
entry = tk.Entry(root, validate="key")
vcmd = (entry.register(on_validate), '%P')
entry.config(validatecommand=vcmd)
entry.pack()
root.mainloop()

The validate function now checks more or less the same thing, but more loosely. Then if the regex results in a match, some additional checks are performed:

  • Allow the input to be empty;
  • Prevent the input to have more than one '+'/'-', and more than one ',';
  • Ignore a match against the empty string, because the pattern allows it, so "a" would result in a match but we don't want it.

The command is registered, and works with the '%P' parameter, that corresponds to the string if the input were accepted.

Please not however that forcing the input to be always correct is somewhat harsh, and might be counter-intuitive. A more commonly used approach is to update a string next to the entry, and have the user know when their input is valid or invalid.

Community
  • 1
  • 1
Right leg
  • 16,080
  • 7
  • 48
  • 81
  • The OP mentions wanting to support commas, which your code doesn't do. – Bryan Oakley Sep 08 '17 at 12:34
  • This still doesn't seem to address what the user is needing. They are trying to validate characters as they are being typed (the notion is flawed, but that's what they are asking). If the OP plugs your function into their code, it will prevent the user from typing `123,456` because it will prevent them from typing `123,` which they must type before `123,45` and `123,456`. Your answer needs to address the fact that your code can't be used in the context of the original code. – Bryan Oakley Sep 08 '17 at 13:00
  • 'def validate(string): result = re.match(r"(\+|\-)?\d+(,\d+)?$", string) return result is not None root = Tk() v1_1 = StringVar() userInput = Entry(root, textvariable=v1_1) userInput.bind('key', validate(v1_1)) userInput.pack()' This script returns the following error: TypeError: expected string or bytes-like object How am I to convert the input to string or bytes-like object? Thanks for your help. – AlBud Sep 08 '17 at 13:59
  • @AlBud I edited my answer. This should produce the result you're expecting. – Right leg Sep 08 '17 at 14:26
  • @BryanOakley I think this should look like what OP was looking for. Thanks a lot for your pointing out my misunderstanding; I learnt a lot today. Also, thanks for your post about entry validation - I left you there a comment with a doc I often use about that topic; you might want to append it as a reference, as doc for tkinter is somewhat rare. – Right leg Sep 08 '17 at 14:29
  • Thank you so much. Works perfectly. – AlBud Sep 08 '17 at 14:43