pythontkintercallback

How to get a reference to traced variable in tkinter


I have a tkinter application with variable number of entries and I want to detect changes to the contents and specifically, which entry has been changed

import tkinter as tk
from tkinter import ttk

DOGS = 3

def main() -> None:
    root = tk.Tk()
    root.title('')
    root.geometry('400x400')

    dogs = {}
    entries = {}

    for row in range(DOGS):
        dogs[row] = tk.StringVar()
        dogs[row].trace_add('write', dog_changed)
        entries[row] = ttk.Entry(root, textvariable=dogs[row])
        entries[row].grid(row=row, column=0)

    root.mainloop()

def dog_changed(*args):
    print(args)

if __name__ == '__main__':
    main()

I can see in args that I have the name of the StringVar as a string (e.g."PY_VAR0"), but is there a more elegant way of getting a reference to which entry has been changed rather than taking a substr here.

I have tried this lambda function, but of course it only show the current value of row

    dogs[row].trace_add('write', lambda *args: dog_changed(row, *args))

Solution

  • I'm not sure if this helps, but you can specify the name of your StringVar instances:

    dogs[row] = tk.StringVar(name=f'dog{row}')  # you can set this to whatever you likez
    

    Which prints this:

    ('dog0', '', 'write')
    ('dog1', '', 'write')
    ('dog2', '', 'write')
    

    You could also set an event binding on the Entry widgets themselves, such as '<Key>' which will fire whenever a key is pressed inside the widget. Then you can get the widget's reference from the passed event (you probably don't even need the trace binding if you do it this way):

    # put this in your for loop
    entries[row].bind('<Key>', lambda e: print(e.widget))
    

    Note that you don't have to use a lambda here - you could bind this to a function instead; that function just needs to accept the incoming event parameter.