Search code examples
pythoneventstkintertkinter-entry

Tkinter - Trigger an event when two Entry Widgets are written into,regardless of order


I just started Tkinter a week ago and I am trying to link the GUI to an automatic code generation script written in Python. I have covered almost all cases with great help from this forum but this particular one has me stumped.

I need 2 Entry Widgets and calculate their relationship to each other as soon as they are entered (i.e. no Button widget to trigger a function). I realize it must be done with events so after some trying I have come up with the following that works but it feels like I am not being 'pythonic' about it.

from Tkinter import *

root = Tk()
root.title("Test")

def OnValidate(S):
    if S.isdigit():
        return True
    else:
        return False

def insert_text_widg(widg,msg):
    widg.delete(1.0,END)
    widg.insert(INSERT,msg)

# Checks the required conditions
def check_var1(event):
    try:
        temp1=int(var1_ent.get())
        temp2=int(var2_ent.get())
    except ValueError:
        return
    if(temp2 > temp1):
        insert_text_widg(log_text,"ERROR: Var2 cannot be greater than Var1")
        for ent in [var1_ent,var2_ent]:
            ent.configure(bg='indian red',fg='white')
    elif ((temp1 - temp2) % 2) == 1:
        insert_text_widg(log_text,"ERROR: Difference cannot be odd")
        for ent in [var1_ent,var2_ent]:
            ent.configure(bg='indian red',fg='white')
    else:
        log_text.delete(1.0,END)
        for ent in [var1_ent,var2_ent]:
            ent.configure(bg='white',fg='black')

# Duplicate of check_var1
def check_var2(event):
    try:
        temp2=int(var2_ent.get())
        temp1=int(var1_ent.get())
    except ValueError:
        return
    if(temp2 > temp1):
        for ent in [var1_ent,var2_ent]:
            ent.configure(bg='indian red',fg='white')
        insert_text_widg(log_text,"ERROR: Var2 cannot be greater than Var1")
    elif ((temp1 - temp2) % 2) == 1:
        for ent in [var1_ent,var2_ent]:
            ent.configure(bg='indian red',fg='white')
        insert_text_widg(log_text,"ERROR: Difference cannot be odd")
    else:
        log_text.delete(1.0,END)
        for ent in [var1_ent,var2_ent]:
            ent.configure(bg='white',fg='black')


# App Configuration
vcmd = (root.register(OnValidate),'%S')

var1_lab = Label(root,text='Var1 :')
var1_lab.pack()

var1_ent = Entry(root,validate="key",validatecommand=vcmd)
var1_ent.bind("<KeyRelease>",check_var1)
var1_ent.pack()

var2_lab = Label(root,text='Var2 :')
var2_lab.pack()

var2_ent = Entry(root,validate="key",validatecommand=vcmd)
var2_ent.bind("<KeyRelease>",check_var2)
var2_ent.pack()

log_text = Text(root)
log_text.pack()

root.mainloop()

There are two functions(check_var1() & check_var2()) which essentially do the same thing that I am looking to merge into one. The checks need to be performed as soon as any of the Entry widgets register a change.

So in short,I would like a way to call the check_var1() function as soon as either Entry widget is typed in,regardless of the order of they are typed in.

Any help or a nudge in the right direction will be highly appreciated.


Solution

  • If I'm understanding what you need correctly then it sounds to me like you'd be best off using the .trace() function.

    This allows you to raise a callback whenever a variable is written to or read.

    In this case we'd create two variables var1 and var2 (for simplicity's sake) and assign these the value of StringVar().

    We'd then set the attribute textvariable of each Entry widgets to be var1 and var2 respectively.

    We then need to set up the .trace() itself, so we call var1.trace("w", callback) and var2.trace("w", callback) (where the "w" means write) which runs the callback function every time either variable is written to.

    This means that no matter which Entry is modified first, we get a callback and more importantly we get a callback for every modification.

    See below for a working example:

    from tkinter import *
    
    class App:
        def __init__(self, root):
            self.root = root
            self.var1 = StringVar()
            self.var2 = StringVar()
            self.var1.trace("w", self.callback)
            self.var2.trace("w", self.callback)
            self.entry1 = Entry(self.root, text="Ok", textvariable=self.var1)
            self.entry2 = Entry(self.root, text="Ok", textvariable=self.var2)
            self.label = Label(self.root)
            self.entry1.pack()
            self.entry2.pack()
            self.label.pack()
        def callback(self, *args):
            self.label.configure(text=self.entry1.get()+self.entry2.get())
    
    root = Tk()
    App(root)
    root.mainloop()