0

In sir Bryan Oakley's detailed answer for validating Entry widget in the post Interactively validating Entry widget content in tkinter, the vcmd is defined as:

 vcmd = (self.register(self.onValidate),
            '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
    self.entry = tk.Entry(self, validate="key", validatecommand=vcmd)

with 'self' being (presumably) the class object. So I'm just wondering if it is possible to have different type of validatecommand for different entry widgets, because what I'm doing now:

vcmd = (self.register(self.onValidate), '%P', '%S')
vcmd1 = (self.register(self.onValidate1), '%P', '%S')
vcmd2 = (self.register(self.onValidate2), '%P', '%S')

is just redefining instead creating new validatecommand, and the first onValidate is just ignored. What could I do?

EDIT: What I've tried so far

from tkinter import *
import math
from tkinter import messagebox


class TimeGenerator:

    def __init__(self,master):
        frame = Frame(master)
        frame.grid()
        label_iso = Label(root, text="Isotope A, Element")
        label_vol = Label(root, text="Voltage")
        label_range = Label(root, text="Charge Range")

        def _register(self, func, subst=None, needcleanup=1):
            f = CallWrapper(func, subst, self).__call__
            name = repr(id(f))
            try:
                func = func.im_func
            except AttributeError:
                pass
            try:
                name = name + func.__name__
            except AttributeError:
                pass
            self.tk.createcommand(name,f)
            if needcleanup:
                if self._tclCommands is None:
                    self._tclCommands = []
                self.tclCommands.append(name)
            return name
        register = _register

        vcmd = (self.register(self.onValidate), '%P', '%S')
        vcmd1 = (self.register(self.onValidate1), '%P', '%S')
        vcmd2 = (self.register(self.onValidate2), '%P', '%S')

        entry_iso = Entry(self,validate="key", validatecommand=vcmd)
        entry_vol = Entry(self,validate="key", validatecommand=vcmd1)
        entry_range = Entry(self,validate="key",validatecommand=vcmd2)

        def onValidate(self, P, S):
            validString = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM,1234567890'
            if not S in validString:
                return False
            if str.__contains__(',', P):
                if len(P.split(",")) >= 2:
                    return False
                messagebox.showinfo("Error", "Expected Form: ex. 133,Cs")
            return True

        label_iso.grid(row=0, sticky=E)
        label_vol.grid(row=1, sticky=E)
        label_range.grid(row=2, sticky=E)

        entry_iso.grid(row=0, column=1)
        entry_vol.grid(row=1, column=1)
        entry_range.grid(row=2,column=1)

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

        self.text = Text(root)
        self.iso = entry_iso
        self.vol = entry_vol
        self.r = entry_range

    def calculateTime(self):
        x = 5
        if self.r.get() == "" or self.iso.get() == "" or self.vol.get() == "":
            messagebox.showinfo("Error", "No field can be empty")

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

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

        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("#",""))
                    else:
                        self.mass = float(columns[12].replace("#","")) + float(columns[13].replace("#",""))

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

        list = []
        for q in range(tup[0], tup[1] + 1):
            y = x * math.sqrt(self.mass / (2 * q * float(self.vol.get())))
            list.append(y)
        i = tup[0]
        for time in list:
            self.text.insert("end", "%d: %s\n" % (i, time))
            i = i + 1
        self.text.pack()

root = Tk()
b = TimeGenerator(root)
root.mainloop()
Community
  • 1
  • 1
Linh Phan
  • 83
  • 1
  • 9

1 Answers1

0

From the sources:

(File Tkinter.py, class Misc)

def _register(self, func, subst=None, needcleanup=1):
    """Return a newly created Tcl function. If this
       function is called, the Python function FUNC will
       be executed. An optional function SUBST can
       be given which will be executed before FUNC."""
    f = CallWrapper(func, subst, self).__call__
    name = repr(id(f))
    try:
        func = func.im_func
    except AttributeError:
        pass
    try:
        name = name + func.__name__
    except AttributeError:
        pass
    self.tk.createcommand(name, f)
    if needcleanup:
        if self._tclCommands is None:
            self._tclCommands = []
        self._tclCommands.append(name)
    return name
register = _register

The most important line from the above code for your question:

self._tclCommands.append(name)

This means that each time you register a new callback, the function is registered as a completely different callback, so you do have a new validatecommand.

You can also add different parameters to them.

Edit 1

python 3:

in /lib/tkinter/__init__.py the source code for register is the same, so the functionality is still the same.

What issues exactly do you encounter?

Edit 2

the code i posted is not meant to be implemented into your code. It shows the implementation inside the tkinter libraries.

Your issue is that your class is not derived from a tkinter based class like tkinter.Frame, tkinter.Toplevel or tkinter.Tk.

Only tkinter derived classes can use self.register. Do not implement the function inside your code.

If you would have a class derived from tkinter it would work.

Example:

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

        vmcd1 = (self.register(self.__vcmd1), '%P', '%S')
        vmcd2 = (self.register(self.__vcmd2), '%P', '%S')
        vmcd3 = (self.register(self.__vcmd3), '%P', '%S')

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

        entry1.grid()
        entry2.grid()
        entry3.grid()


    def __vcmd1(self, P, S):
        print("__vcmd1")

    def __vcmd2(self, P, S):
        print("__vcmd2")

    def __vcmd2(self, P, S):
        print("__vcmd2")

This is how you would set sth. like this up. Also, your definition of functions would not be matching as self.function_name only works is inside your class, not inside a class method.

R4PH43L
  • 2,122
  • 3
  • 18
  • 30
  • I'm just wondering which python are you using? I don't know if it's a syntax thing, but I can't seem to write register = _register. It just takes register as a variable that is not used, and nothing changes with regard to the validatecommand situation... Also, with python 3.x, which import line should I write if anything at all? Thanks! (I'm sorry just very new to python and still don't quite understand things well >.<) – Linh Phan May 18 '16 at 18:47
  • The source code i posted is code from the python libraries. register = _register is just the line that allows you to call self.register on a tkinter derived class (e.g. `class MyClass(tkinter.Frame)`) i took the sources from python 2.7, will update the answer with code from 3.x. – R4PH43L May 19 '16 at 11:14
  • My issue is how to incorporate this code into my own program... I've included the code I've done so far, but when I tried it (knowing that I haven't finished the next 2 validatecommand), I get the error " vcmd = (self.register(self.onValidate), '%P', '%S') AttributeError: 'TimeGenerator' object has no attribute 'register' " What's the reason for that? – Linh Phan May 19 '16 at 16:57