0

I am making a tkinter program where I want to restrict user input to only floats. I have done this using the register and validate features. All of it is working fine but when I enter a set of numbers the program does not let me remove the first one. I think this has something to do with my final function inside my class, but I don't know how to edit it without breaking the rest of the program.

Here is my simplified problem.

from tkinter import *

class investables(Frame):

 def __init__(self, master):
    Frame.__init__(self, master=None)
    self.pack()
    self.master = master
    
    vcmd = (master.register(self.validate),'%d', '%i', '%P', '%s', 
           '%S', '%v', '%V', '%W')
    self.entry1 = Entry(self, validate="key", validatecommand=(vcmd))
    self.entry1.grid(row=1, column=3)

    # Define validate function, this also passes the many components 
    # of the register tool
 def validate(self, d, i, P, s, S, v, V, W):
    # Begin if else statement that prevents certain user input
    if P:
        try:
            # Checks if %P is a float
            float(P)
            # If this is the case then the input is excepted
            return True
        # If this is not the case, then the code will not accept user 
        # input
        except ValueError:
            return False
    # If any other input is inserted, the program will not accept it 
     # either    
    else:
         return False

root = Tk()
my_gui = investables(master=root)
my_gui.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301
Vorbeker
  • 17
  • 4

2 Answers2

0

I changed validate to focusout because you need to be able to enter a floating point number into the Entry widget before validation.

I added a Button so that focusout has something to focus on.

The use of validate "key" only allows single key entry which obviously prevents entering a float.

For more information try using stack overflow search tool and enter the following.

is:Interactively validating Entry widget content in tkinter

This will give you multiple questions and answers that may be of use to you.

import tkinter as tk

class investables(tk.Frame):

    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.pack(fill="both", expand=True)

        vcmd = (master.register(self.validate),
                '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
        self.entry1 = tk.Entry(self, validate = "focusout", validatecommand = vcmd)
        self.entry1.pack(side = "left", fill = "both", expand = True)
        self.ok = tk.Button(self, text = "Ok", command = self.action)
        self.ok.pack(side = "right", fill = "both", expand = True)
        self.entry1.focus()

    def action( self ):
        self.ok.focus()

    def validate(self, d, i, P, s, S, v, V, W):
        try:
            if repr(float(P)) == P:
                print( "Validation successful" )
                return True
            else:            
                print( "Validation Failed" )
                return False
        except ValueError:
            print( "Validation Failed" )
            return False

root = tk.Tk()
my_gui = investables(master = root)
root.mainloop()
Derek
  • 1,916
  • 2
  • 5
  • 15
  • IMO it would be a teensy bit better to explain *why* changing `validate` to `'focusout` supposedly fixes the problem (or what role it played in doing so) instead of suggesting that they search the whole Internet to get more information. – martineau Aug 13 '21 at 17:39
  • I mistook what you were suggesting about seraching, but even "only" that many matches is quite a bit, so the gist of what I said and the rest of my criticism about not explaining things yourself was valid — and your update is an improvement. BTW, you could have formatted the search as a link like [this](https://stackoverflow.com/search?q=Interactively+validating+Entry+widget+content+in+tkinter). – martineau Aug 14 '21 at 06:28
0

The reason it won't let you delete the last digit is because that makes P equal to the empty string, "", which will cause float("") to raise a ValueError exception — so the minimal fix is to modify your code to accept P when it's that value.

Below shows a way to do that. I've also streamlined what you had a little and reformatted it to follow the PEP 8 - Style Guide for Python Code guidelines to some degree. I strongly suggest you read and start following them yourself — it will make your code more readable and easier to maintain.

from tkinter import Entry, Frame, Tk


class Investables(Frame):
    def __init__(self, master):
        super().__init__(master)  # Initialize base class.
        self.pack()

        vcmd = (master.register(self.validate),
                '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
        self.entry1 = Entry(self, validate="key", validatecommand=vcmd)
        self.entry1.grid(row=1, column=3)

    # Define validate function, this receives the many components specified when
    # the registering the function (even though only `P` is used).
    def validate(self, d, i, P, s, S, v, V, W):
        if P: # Check the value that the text will have if the change is allowed.
            try:
                float(P)  # Checks if %P is a float
            except ValueError:  # Don't accept user input.
                return False
        return True  # Note this will consider nothing entered as valid.


if __name__ == '__main__':
    root = Tk()
    my_gui = Investables(master=root)
    my_gui.mainloop()

martineau
  • 119,623
  • 25
  • 170
  • 301
  • @Vorbeker: FWIW, here's [another way](https://stackoverflow.com/a/68779709/355230) to do something like this which while not quite as generic as registering a validation callback for an `Entry` widget, does have the advantage of being simpler. – martineau Aug 14 '21 at 06:46