Search code examples
pythonpython-3.xvalidationtkintertkinter-entry

Passing args to Tkinter Callback validating function in Python3, Is it possible?


I am a newbie on Python3 and trying tkinter for first time, I recently answered to Solving simple ODE system within tkinter GUI using https://www.geeksforgeeks.org/python-tkinter-validating-entry-widget/ tips.

Now I am banging my head about how the reg_str = mainwindow.register(callback_str) and e4.config(validate ="key", validatecommand =(reg_str, '%P')) works.

I googled and tried searches on stackoverflow but wasnt able to find an answer,

below my minimal reproducible example:

import tkinter as tk
from tkinter import IntVar,StringVar

###############################################################################
def callback_int(input,typez=None):
    # print(typez)
    
    if input.isdigit():
        # print(input)
        return True
                          
    elif input == "":
        # print(input)
        return True
  
    else:
        print(input)
        return False

def callback_str(input, typez=None):
    
    if len(input) >=1  and input[0] == '/':
        return False
    
    if all([s.isdigit() or s =='/' for s in input]) and input.count('/') <= 1:
        # print([s.isdigit() or s =='/' for s in input])
        # print(input)
        return True
                          
    elif all([s.isdigit() or s =='.' for s in input]) and input.count('.') <= 1:
        # print([s.isdigit() or s =='.' for s in input])
        # print(input)
        return True

    else:
        print('no valid input : ',input)
        return False

def mainwindow():

    mainwindow = tk.Tk()
    mainwindow.geometry('350x350')
    
    tk.Label(mainwindow, text="enter parameters below").grid(row=1)
    
    getN = IntVar()
    geti0 = IntVar()
    getr0 = IntVar()
    getbeta = StringVar(mainwindow, value='0')
    getgamma = StringVar(mainwindow, value='0')

    tk.Label(mainwindow, text="N").grid(row=2)
    tk.Label(mainwindow, text="i0").grid(row=3)
    tk.Label(mainwindow, text="r0").grid(row=4)
    tk.Label(mainwindow, text="beta").grid(row=5)
    tk.Label(mainwindow, text="gamma").grid(row=6)
    
    e1 = tk.Entry(mainwindow,textvariable = getN)
    e1.grid(row=2, column=1)
    e2 = tk.Entry(mainwindow,textvariable = geti0)
    e2.grid(row=3, column=1)
    e3 = tk.Entry(mainwindow,textvariable = getr0)
    e3.grid(row=4, column=1)
    e4 = tk.Entry(mainwindow,textvariable = getbeta)
    e4.grid(row=5, column=1)
    e5 = tk.Entry(mainwindow,textvariable = getgamma)
    e5.grid(row=6, column=1)
    
    reg_int = mainwindow.register(callback_int)
    reg_str = mainwindow.register(callback_str)
    
    
    e1.config(validate ="key", validatecommand =(reg_int, '%P'))
    e2.config(validate ="key", validatecommand =(reg_int, '%P'))
    e3.config(validate ="key", validatecommand =(reg_int, '%P'))
    e4.config(validate ="key", validatecommand =(reg_str, '%P'))
    e5.config(validate ="key", validatecommand =(reg_str, '%P'))
    
    solve = tk.Button(mainwindow, text='solve!', command=lambda: [values()]).grid(row=7, column=1, sticky=tk.W, pady=4)
 
    
    def values():
        
        readN = getN.get()
        readi0 = geti0.get()
        readr0 = getr0.get()
        readbeta = eval(getbeta.get(),{"builtins": {}})
        readgamma = eval(getgamma.get(), {"builtins": {}})
        

        
        intN = int(readN)
        inti0 = int(readi0)
        intr0 = int(readr0)
        intbeta = float(readbeta)
        intgamma = float(readgamma)
        
        
        print(intN,'\n',inti0,'\n',intr0,'\n',intbeta,'\n',intgamma)

    
 
    mainwindow.mainloop()
    
mainwindow()

the program opens a widget:

enter image description here

that ask for inputs.

first 3 Entry row are validated by callback_int that allows only int digits while last 2 Entry rows use callback_str that allow both floats like 1.5 or 0.5 and fractions like 1/7 or 5/7.

My question (sorry it took so long) is:

is there a way to pass function arguments to callback like callback(typez = str) so that I could shorten my code using only one callback function that acts differently for my int and string values ? If I use reg_int = mainwindow.register(callback_int(input,typez='popo')) I got

line ...
if input.isdigit():

AttributeError: 'function' object has no attribute 'isdigit'

I am guessin the problem is in :

reg_int = mainwindow.register(callback_int(input,typez='popo')) #AttributeError: 'function' object has no attribute 'isdigit'
reg_str = mainwindow.register(callback_str('pipi'))

but wasnt able to find anythiong useful , or maybe my approach is in the wrong direction


Solution

  • This uses the lambda statement to pass either "str" or "int" to the typez argument in callback(), so that callback() knows what type the input is. Here is the full code based on my first comment above:

    import tkinter as tk
    from tkinter import IntVar,StringVar
    
    ###############################################################################
    def callback(input,typez=None):
        
        if typez == "int":
            if input.isdigit():
                # print(input)
                return True
                                
            elif input == "":
                # print(input)
                return True
        
            else:
                print(input)
                return False
    
        if typez == "str":
            if len(input) >=1  and input[0] == '/':
                return False
            
            if all([s.isdigit() or s =='/' for s in input]) and input.count('/') <= 1:
                # print([s.isdigit() or s =='/' for s in input])
                # print(input)
                return True
                                
            elif all([s.isdigit() or s =='.' for s in input]) and input.count('.') <= 1:
                # print([s.isdigit() or s =='.' for s in input])
                # print(input)
                return True
    
            else:
                print('no valid input : ',input)
                return False
    
    def mainwindow():
    
        mainwindow = tk.Tk()
        mainwindow.geometry('350x350')
        
        tk.Label(mainwindow, text="enter parameters below").grid(row=1)
        
        getN = IntVar()
        geti0 = IntVar()
        getr0 = IntVar()
        getbeta = StringVar(mainwindow, value='0')
        getgamma = StringVar(mainwindow, value='0')
    
        tk.Label(mainwindow, text="N").grid(row=2)
        tk.Label(mainwindow, text="i0").grid(row=3)
        tk.Label(mainwindow, text="r0").grid(row=4)
        tk.Label(mainwindow, text="beta").grid(row=5)
        tk.Label(mainwindow, text="gamma").grid(row=6)
        
        e1 = tk.Entry(mainwindow,textvariable = getN)
        e1.grid(row=2, column=1)
        e2 = tk.Entry(mainwindow,textvariable = geti0)
        e2.grid(row=3, column=1)
        e3 = tk.Entry(mainwindow,textvariable = getr0)
        e3.grid(row=4, column=1)
        e4 = tk.Entry(mainwindow,textvariable = getbeta)
        e4.grid(row=5, column=1)
        e5 = tk.Entry(mainwindow,textvariable = getgamma)
        e5.grid(row=6, column=1)
        
        reg_int = mainwindow.register(lambda input, typez="int": callback(input, typez=typez))
        reg_str = mainwindow.register(lambda input, typez="str": callback(input, typez=typez))
        
        
        e1.config(validate ="key", validatecommand =(reg_int, '%P'))
        e2.config(validate ="key", validatecommand =(reg_int, '%P'))
        e3.config(validate ="key", validatecommand =(reg_int, '%P'))
        e4.config(validate ="key", validatecommand =(reg_str, '%P'))
        e5.config(validate ="key", validatecommand =(reg_str, '%P'))
        
        solve = tk.Button(mainwindow, text='solve!', command=lambda: [values()]).grid(row=7, column=1, sticky=tk.W, pady=4)
     
        
        def values():
            
            readN = getN.get()
            readi0 = geti0.get()
            readr0 = getr0.get()
            readbeta = eval(getbeta.get(),{"builtins": {}})
            readgamma = eval(getgamma.get(), {"builtins": {}})
            
    
            
            intN = int(readN)
            inti0 = int(readi0)
            intr0 = int(readr0)
            intbeta = float(readbeta)
            intgamma = float(readgamma)
            
            
            print(intN,'\n',inti0,'\n',intr0,'\n',intbeta,'\n',intgamma)
    
        
     
        mainwindow.mainloop()
        
    mainwindow()