Python Forum
Can I get some help doing this Data Validation?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Can I get some help doing this Data Validation?
#1
I'm trying to get this data validation to work. It seems to work ok when checking, but it just does some funny things, that I don't understand. First, here is the code:

import tkinter as tk
from tkinter import messagebox

class Example(tk.Frame):

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        l_focus = self.register(self.leave_field)
        self.entry1 = tk.Entry(self)
        self.entry2 = tk.Entry(self)
        self.entry3 = tk.Entry(self)
        self.entry1.configure(validate="focusout", validatecommand=(l_focus, '%P'))        
        self.entry2.configure(validate="focusout", validatecommand=(l_focus, '%P'))
        self.entry3.configure(validate="focusout", validatecommand=(l_focus, '%P'))
        self.entry1.pack(side="top", fill="x")
        self.entry2.pack(side="top", fill="x")
        self.entry3.pack(side="top", fill="x")
        
    def leave_field(self, inp):
        if inp == "":
            messagebox.showinfo("Blank Entry", "You must enter a number.")
            return False
        else:      
            # Check entry is Number
            try:
                float(inp)
                if int(inp) < 10 or int(inp) > 15:
                    messagebox.showinfo("Out of Range", inp + " is not between 10-15")
                    return False                    
                else:
                    return True
            except ValueError:
                messagebox.showinfo("Not Numeric", inp + " is not a valid number.")
                return False
So first off, if the first textbox is erroneous, my messagebox pops up, I click ok, and then I have to click back on the textbox to fix the error. Well, this triggers the next textbox's validation check, and it shows empty. Is there a way that I can when I click OK on the message box, the erroneous entry retains focus?

Then, how can I reset the validation check? the %P seems to retain the values, and it triggers the checks more than once. Not sure if I'm explaining this well, but for some reason I tend to get my messageboxes popping up more than once.

Thanks.
Reply
#2
Code is not complete. Please supply working snippet
Reply
#3
(Nov-17-2019, 02:21 AM)Larz60+ Wrote: Code is not complete. Please supply working snippet

My apologies. I forgot the bottom portion of the code. Here it is.

import tkinter as tk
from tkinter import messagebox
 
class Example(tk.Frame):
 
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        l_focus = self.register(self.leave_field)
        self.entry1 = tk.Entry(self)
        self.entry2 = tk.Entry(self)
        self.entry3 = tk.Entry(self)
        self.entry1.configure(validate="focusout", validatecommand=(l_focus, '%P'))        
        self.entry2.configure(validate="focusout", validatecommand=(l_focus, '%P'))
        self.entry3.configure(validate="focusout", validatecommand=(l_focus, '%P'))
        self.entry1.pack(side="top", fill="x")
        self.entry2.pack(side="top", fill="x")
        self.entry3.pack(side="top", fill="x")
         
    def leave_field(self, inp):
        if inp == "":
            messagebox.showinfo("Blank Entry", "You must enter a number.")
            return False
        else:      
            # Check entry is Number
            try:
                float(inp)
                if int(inp) < 10 or int(inp) > 15:
                    messagebox.showinfo("Out of Range", inp + " is not between 10-15")
                    return False                    
                else:
                    return True
            except ValueError:
                messagebox.showinfo("Not Numeric", inp + " is not a valid number.")
                return False
            
if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()
Reply
#4
You were returning False, then ignoring it
also, why conversion to float?
logic is strange

import tkinter as tk
from tkinter import messagebox
  

class Example(tk.Frame):
  
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        l_focus = self.register(self.leave_field)
        if l_focus:
            self.entry1 = tk.Entry(self)
            self.entry2 = tk.Entry(self)
            self.entry3 = tk.Entry(self)
            self.entry1.configure(validate="focusout", validatecommand=(l_focus, '%P'))        
            self.entry2.configure(validate="focusout", validatecommand=(l_focus, '%P'))
            self.entry3.configure(validate="focusout", validatecommand=(l_focus, '%P'))
            self.entry1.pack(side="top", fill="x")
            self.entry2.pack(side="top", fill="x")
            self.entry3.pack(side="top", fill="x")

    def leave_field(self, inp):
        if inp == "":
            messagebox.showinfo("Blank Entry", "You must enter a number between 10 and 15.")
            return False
        else:      
            # Check entry is Number
            try:
                float(inp)
                if int(inp) < 10 or int(inp) > 15:
                    messagebox.showinfo("Out of Range", inp + " is not between 10-15")
                    return False                    
                else:
                    return True
            except ValueError:
                messagebox.showinfo("Not Numeric", inp + " is not a valid number.")
                return False
             

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()
Reply
#5
(Nov-17-2019, 04:00 PM)Larz60+ Wrote: You were returning False, then ignoring it
also, why conversion to float?
logic is strange

import tkinter as tk
from tkinter import messagebox
  

class Example(tk.Frame):
  
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        l_focus = self.register(self.leave_field)
        if l_focus:
            self.entry1 = tk.Entry(self)
            self.entry2 = tk.Entry(self)
            self.entry3 = tk.Entry(self)
            self.entry1.configure(validate="focusout", validatecommand=(l_focus, '%P'))        
            self.entry2.configure(validate="focusout", validatecommand=(l_focus, '%P'))
            self.entry3.configure(validate="focusout", validatecommand=(l_focus, '%P'))
            self.entry1.pack(side="top", fill="x")
            self.entry2.pack(side="top", fill="x")
            self.entry3.pack(side="top", fill="x")

    def leave_field(self, inp):
        if inp == "":
            messagebox.showinfo("Blank Entry", "You must enter a number between 10 and 15.")
            return False
        else:      
            # Check entry is Number
            try:
                float(inp)
                if int(inp) < 10 or int(inp) > 15:
                    messagebox.showinfo("Out of Range", inp + " is not between 10-15")
                    return False                    
                else:
                    return True
            except ValueError:
                messagebox.showinfo("Not Numeric", inp + " is not a valid number.")
                return False
             

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()

Well, quite honestly as an explanation, I'm very new to Python, and trying to piece together things I've found on the web that I can apply to my application. So, with that said:
- What do you mean I'm returning false and then ignoring it? Is there a step I'm missing? I'm basically wanting to check the entry, and if it doesn't meet the criteria then show the appropriate message box to alert the user. And actually, thinking about it now (without testing yet), I don't really need it to return true or false, right? Because if the input meets certain criteria set up with "if" statements I just display the messagebox. Correct?
- The conversion to float was my logic telling me how to make sure the input was a numerical value that could have a decimal. But again, thinking about it, could I just change this:
self.entry1.configure(validate="focusout", validatecommand=(l_focus, '%P'))
to:
self.entry1.configure(validate="focusout", validatecommand=(l_focus, float('%P')))
and achieve the same result and be able to eliminate part of the checks in my function?

Would it be better to do a Binding to "FocusOut" for the widget, rather than use validatecommand? I wrote this, and it seems to work fine, but my issue now is how to remember the widget that is being checked. In this code I'm only checking Entry1. How would I pass as a variable the widget that is losing focus and triggering the function?

import tkinter as tk
from tkinter import messagebox
 
class Example(tk.Frame):
 
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.entry1 = tk.Entry(self)
        self.entry2 = tk.Entry(self)
        self.entry3 = tk.Entry(self)
        self.entry1.bind('<FocusOut>', self.focus_out)
        self.entry2.bind('<FocusOut>', self.focus_out)
        self.entry3.bind('<FocusOut>', self.focus_out)
        self.entry1.pack(side="top", fill="x")
        self.entry2.pack(side="top", fill="x")
        self.entry3.pack(side="top", fill="x")
         
    def focus_out(self, event):
        x = self.entry1.get()
        if x == "":
            messagebox.showinfo("Blank Entry", "You must enter a number.")
            self.entry1.focus_set()
        else:
            try:
                float(x)
                if float(x) < 10.0 or float(x) > 15.0:
                    messagebox.showinfo("Out of Range", x + " is not between 10-15")
                    self.entry1.focus_set()
            except ValueError:
                messagebox.showinfo("Not Numeric", x + " is not a valid number.")
                self.entry1.focus_set()

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()
Reply
#6
This is what I came up with. It operates exactly how I want it to. It's probably not the most efficient way of doing what I'm trying to do, but it works. I'd love some more input from people about how I might better achieve this, but as of right now, I'm going to mark the thread as "Solved".

import tkinter as tk
from tkinter import messagebox
 
class Example_1(tk.Frame):
 
    def __init__(self, parent):
        
        tk.Frame.__init__(self, parent)
        self.next_box = True
        self.entry1 = tk.Entry(self)
        self.entry2 = tk.Entry(self)
        self.entry3 = tk.Entry(self)
        self.entry1.bind('<FocusIn>', self.focus_in)
        self.entry2.bind('<FocusIn>', self.focus_in)
        self.entry3.bind('<FocusIn>', self.focus_in)
        self.entry1.bind('<FocusOut>', self.focus_out)
        self.entry2.bind('<FocusOut>', self.focus_out)
        self.entry3.bind('<FocusOut>', self.focus_out)
        self.entry1.pack(side="top", fill="x")
        self.entry2.pack(side="top", fill="x")
        self.entry3.pack(side="top", fill="x")
         
    def focus_out(self, event):
        self.next_box = False
        if self.data_check:
            self.data_check = False
            x = float(self.active_widget.get())
            if x == "":
                messagebox.showinfo(title="Blank Entry", message="You must enter a number.")
                self.active_widget.focus_set()
            else:
                try:
                    x
                    if x < 10.0 or x > 15.0:
                        messagebox.showinfo(title="Out of Range", message=x + " is not between 10-15")
                        self.active_widget.focus_set()
                except ValueError:
                    messagebox.showinfo(title="Not Numeric", message=x + " is not a valid number.")
                    self.active_widget.focus_set()
            if not self.next_box:
                self.next_box = True
                self.data_check = True
        else:
            self.active_widget.focus_set()
            self.data_check = True
            
    def focus_in(self, event):
        if self.next_box:
            self.active_widget = self.focus_get()
            self.data_check= True
        else:
            self.next_box = True

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020