Linh Phan
Linh Phan

Reputation: 83

Validation for entry widget not behave as expected

I'm currently having multiple validation for different entry widget, and whilst all commands are set up the same way, only the validation for second entry work. Is there a reason why the other ones don't? Here's the part of my code dealing with entries:

import tkinter
from tkinter import *
import math
from tkinter import messagebox


class MyClass(tkinter.Frame):
    def __init__(self, *args, **kwargs):
        tkinter.Frame.__init__(self, *args, **kwargs)

        #Setting up frame and widgets
        vcmd1 = (self.register(self.__vcmd1), '%P', '%S')
        vcmd2 = (self.register(self.__vcmd2), '%P')
        vcmd3 = (self.register(self.__vcmd3), '%P', '%S')

        self.v = IntVar()
        self.v.set(1)

        label_det = Label(self,text="Choose Detector")
        self.mcp0 = Radiobutton(self, text="MCP-0",variable=self.v,value=1,command=self.selected)
        self.mcp6 = Radiobutton(self, text="MCP-6",variable=self.v,value=2,command=self.selected)
        self.mpet = Radiobutton(self,text="MCP-MPET",variable=self.v,value=3,command=self.selected)

        label_det.grid(row=0, column=0, columnspan=2)
        self.mcp0.grid(row=1,columnspan=2)
        self.mcp6.grid(row=2,columnspan=2)
        self.mpet.grid(row=3,columnspan=2)

        label_iso = Label(self,text="Isotope A, Element (ex: 133,Cs)")
        label_vol = Label(self, text="Beam Energy (eV)")
        label_range = Label(self, text="Charge Range (ex:1,12)")

        label_iso.grid(row=4, column=0, sticky=E)
        label_vol.grid(row=5, column=0, sticky=E)
        label_range.grid(row=6, column=0, sticky=E)

        self.entry1 = tkinter.Entry(self, validate="key", validatecommand=vcmd1)
        self.entry2 = tkinter.Entry(self, validate="key", validatecommand=vcmd2)
        self.entry3 = tkinter.Entry(self, validate="key", validatecommand=vcmd3)

        self.entry1.grid(row=4, column=1)
        self.entry2.grid(row=5, column=1)
        self.entry3.grid(row=6, column=1)

    def __vcmd1(self, P, S):
        validString = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM,1234567890'
        if not S in validString:
            messagebox.showinfo("Error", "Not valid")
            return False
        if "," in P:
            messagebox.showinfo("Good", "Contain comma")
            if len(P.split(",")) > 2:
                messagebox.showinfo("Error", "Expected Form: ex. 133,Cs")
                return False
        else:
            return True

    def __vcmd2(self, P):
        if P == '':
            return True
        try:
            float(P)
            return True
        except ValueError:
            messagebox.showinfo("Error", "Entry must be a float or integer")
            return False


    def __vcmd3(self, P, S):
        if "," in P:
            if len(P.split(",")) > 2:
                messagebox.showinfo("Error", "Contain multiple commas")
                return False
            a = P.split(",")[0]
            b = P.split(",")[1]
            if a != '' and b != '':
                try:
                    int(a)
                    int(b)
                except ValueError:
                    messagebox.showinfo("Error", "Expected form: ex. 1,12")
                    return False
        else:
            return True

    def selected(self):
        if self.v.get() == 1:
            self.x = 8.0
        elif self.v.get() == 2:
            self.x = 3.0
        else:
            self.x = 9.2
        return self.x

class TimeGenerator:

    def __init__(self,master):

        self.frame = MyClass(master)
        self.frame.grid(columnspan=2)

        self.text = Text(root)
        self.iso = self.frame.entry1
        self.vol = self.frame.entry2
        self.r = self.frame.entry3

        button = Button(root, text='Time Range', command=self.calculateTime)
        button.grid(row=3, columnspan=2)

    def calculateTime(self):
        self.text.delete(1.0, END)

        dict = {'h': 1, 'he': 2, 'li': 3, 'be': 4, 'b': 5, 'c': 6, 'n': 7, 'o': 8, 'f': 9, 'ne': 10,
            'na': 11, 'mg': 12, 'al': 13, 'si': 14, 'p': 15, 's': 16, 'cl': 17, 'ar': 18,
            'k': 19, 'ca': 20, 'sc': 21, 'ti': 22, 'v': 23, 'cr': 24, 'mn': 25, 'fe': 26, 'co': 27, 'ni': 28,
            'cu': 29, 'zn': 30,
            'ga': 31, 'ge': 31, 'as': 33, 'se': 34, 'br': 35, 'kr': 36, 'rb': 37, 'sr': 38, 'y': 39,
            'zr': 40, 'nb': 41, 'mo': 42, 'tc': 43, 'ru': 44, 'rh': 45, 'pd': 46, 'ag': 47, 'cd': 48,
            'in': 49, 'sn': 50, 'sb': 51, 'te': 52, 'i': 53, 'xe': 54, 'cs': 55, 'ba': 56,
            'la': 57, 'ce': 58, 'pr': 59, 'nd': 60, 'pm': 61, 'sm': 62, 'eu': 63, 'gd': 64, 'tb': 65, 'dy': 66,
            'ho': 67, 'er': 68, 'tm': 69, 'yb': 70, 'lu': 71,
            'hf': 72, 'ta': 73, 'w': 74, 're': 75, 'os': 76, 'ir': 77, 'pt': 78, 'au': 79, 'hg': 80, 'tl': 81,
            'pb': 82, 'bi': 83, 'po': 84, 'at': 85, 'rn': 86,
            'fr': 87, 'ra': 88, 'ac': 89, 'th': 90, 'pa': 91, 'u': 92, 'np': 93, 'pu': 94, 'am': 95, 'cm': 96,
            'bk': 97, 'cf': 98, 'es': 99, 'fm': 100, 'md': 101, 'no': 102, 'lr': 103,
            'rf': 104, 'db': 105, 'sg': 106, 'bh': 107, 'hs': 108, 'mt': 109, 'ds': 110, 'rg': 111, 'cn': 112,
            'uut': 113, 'fl': 114, 'uup': 115, 'lv': 116, 'uus': 117, 'uuo': 118}

        if self.r.get() == "" or self.iso.get() == "" or self.vol.get() == "":
            messagebox.showinfo("Error", "No field can be empty")
            return None

        r = self.r.get().replace(" ", "")
        tup = tuple(int(x) for x in r.split(","))

        iso = self.iso.get().replace(" ", "")
        list = []
        for e in iso.split(","):
            list.append(e)

        if (dict[list[1].lower()] < tup[0]) or (dict[list[1].lower()] < tup[1]):
            messagebox.showinfo("Error", "Change state range does not exist for given element")
            return None

        f = open("/Users/LazyLinh/PycharmProjects/TimeGenGUI/mass.mas12.txt", "r")
        i = 0
        while (i < 40):
            header = f.readline()
            i += 1
        self.mass = 0

        #iterate through text file

        for line in f:
            line = line.strip()
            columns = line.split()
            if (list[0] == columns[3]):
                if (list[1].lower() == columns[4].lower()):
                    if (len(columns) == 16):
                        self.mass = float(columns[13].replace("#","")) + float(columns[14].replace("#",""))/10e6
                    else:
                        self.mass = float(columns[12].replace("#","")) + float(columns[13].replace("#",""))/10e6

        #Calculation

        list = []
        for q in range(tup[0], tup[1] + 1):
            y = (self.frame.selected() * math.sqrt(self.mass * 1.6605402e-27 / (2 * q *1.6022e-19 * float(self.vol.get())))) * 10e6
            list.append(y)
        i = tup[0]

        #inserting to text widget
        for time in list:
            self.text.insert("end", "%d: %s\n" % (i, time))
            i = i + 1
        self.text.grid()

        self.iso = self.frame.entry1
        self.vol = self.frame.entry2
        self.r = self.frame.entry3


root = Tk()
b = TimeGenerator(root)
root.mainloop()

observed errors include being able to add characters not from validString for entry 1, or being able to add multiple ',' for entry 3...

EDIT: I have included my full code and what I tried

Upvotes: 1

Views: 102

Answers (1)

Bryan Oakley
Bryan Oakley

Reputation: 385880

The code is technically working, though you have logic errors that perhaps are preventing it from working like you expect.

For the validation function to work, it must return either True or False. Any other valid will disable the validation. In your commands there are paths through the code that do not return True or False, causing the validation to be turned off.

For example, in _vcmd1, if P contains a comma but the expression len(P.split(",")) > 2 is False, you end up returning None which will cancel any future validation.

Similarly, in _vcmd3 you have a path through the code that returns None. Ask yourself what happens when you enter "4,". The function returns None which, again, cancels future validation.

Bottom line: your functions must always return either True or False, or validation will be turned off.

Upvotes: 1

Related Questions