1

I seem to misunderstand how trace works for tkinter.StringVar.

I want to create a class that inherits from StringVar. One of the reasons is to have a different behavior if the value is changed "programmatically" i.e. using StringVar.set(), or "via interface", i.e. by user modifying a linked tkinter.Entry.

In general, I want something of the following sort:

import tkinter


class Str(tkinter.StringVar):

    def __init__(self, value):
        super().__init__(value=value)
        self.trace_add("write", self.entry_changed)

    def set(self, new_value):
        # Do things
        super().set(new_value)

    def entry_changed(self, *args):
        new_value = super().get()
        # Do things
        super().set(new_value)

since I was hoping that StringVar.set does not trigger trace - unfortunately it does. Now, I have in mind a workaround with a bool flag that would skip entry_changed one time after changing the value "programmatically", but then I realized I clearly don't understand something about how trace triggers.

Q. Why does the super().set(new_value) in Str.entry_changed does not trigger trace? Since super().set() triggers trace in Str.set, I'd expect it to also trigger in entry_changed, creating an endless loop.

Below is an example. entry_changed additionally changes the new_value just to make sure that trace isn't triggered because the value remains the same:

import tkinter


class Str(tkinter.StringVar):

    def __init__(self, value):
        super().__init__(value=value)
        self.trace_add("write", self.entry_changed)

    def set(self, new_value):
        print("Set value")
        super().set(new_value)

    def entry_changed(self, *args):
        print("Entry changed")
        new_value = super().get()
        super().set(new_value + "_")


root = tkinter.Tk()
var = Str("0")
var.set("a")
print(var.get())

outputs:

Set value
Entry changed
a_
yassem
  • 41
  • 6
  • 2
    Traces are automatically disabled while a trace is being handled, because this would be an infinite recursion. And before you get any further with this project, let me point out that your override of `.set()` ISN'T going to be called when the Var is changed by user action, such as typing into the linked Entry - that's happening entirely in the Tk environment, and doesn't touch the Python Var object at all. – jasonharper Apr 19 '23 at 13:17
  • Indeed, trace being disabled automatically makes sense. If you post this as an answer I'll accept as answered. As for `.set` not being called when Entry is modified - that's exactly the idea. – yassem Apr 19 '23 at 13:24
  • Why not answer your own question? You can refer [the official documentation](https://www.tcl.tk/man/tcl8.4/TclCmd/trace.html#M17) that says "While command is executing during a read or write trace, traces on the variable are temporarily disabled." – relent95 Apr 21 '23 at 02:02

0 Answers0