All, I am a little baffled by this one and hope that someone out there can explain what is going on.
The example is little convoluted but it serves the point of illustrating the problem I am seeing in my app. I have three classes that extend Tkinter.Frame; A, B and X. B contains an instance of A and a variable (a label) whose value is dependent on the state of an Option Menu in A. X has multiple instances of B. When I set name='opt_var' in the line below (from Class A), b1 and b2 within X act as if they are the same instance of B.
self.opt_var = StringVar(name='opt_var')
However, when I remove name='opt_var' from the StringVar declaration, I get the expected (i.e. independent) behavior of two separate instances.
What confuses me even further is I do a comparison of the two instances of B using "b1 is b2" - which I understand is supposed to check if two variables point to the same object - and I get True which is unexpected as b1 and b2 are instantiated separatly.
Any insight into what is going is appreciated.
from Tkinter import *
import collections
# A simple class with an Option Menu
class A(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.opt_dict = collections.OrderedDict()
self.opt_dict['Option A'] = 0
self.opt_dict['Option B'] = 1
keys = self.opt_dict.keys()
self.opt_var = StringVar(name='opt_var')
self.opt_var.set(keys[0])
opt_om = OptionMenu(self, self.opt_var, *keys).pack()
# A simple class with a Label whose value is determined by Class A's option
class B(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.lbl = Label(self, text='What did you choose?')
self.lbl.pack()
self.a = A(self)
self.a.pack()
# set a 'trace' on a's StringVar with a local callback
self.a.opt_var.trace('w', self.myfunction)
def myfunction(self, *args):
# this seems a little convoluted here but in the app where I
# came across this issue, there are more than just two choices
optB = bool(self.a.opt_dict[self.a.opt_var.get()])
if (optB):
self.lbl.config(text='Option B')
else:
self.lbl.config(text='Option A')
# a simple class with multiple instances of B
class X(Frame):
def __init__(self, master):
Frame.__init__(self, master)
b1 = B(self).pack()
b2 = B(self).pack()
print(b1 is b2)
root = Tk()
frame = X(root).pack()
root.mainloop()
Tkinter is a thin wrapper around an embedded Tcl interpreter. This interpreter knows nothing about python namespaces.
When you create an instance of StringVar
, this creates a new global variable in that Tcl interpreter. Normally a unique variable name is picked by Tkinter. When you explicitly give it a name, that becomes the name of the global variable. If you create multiple instances of StringVar
and give each the same name, they will all be tied to the same single Tcl variable.
The solution is simple: each instance of your class needs to create an instance of a StringVar
with a unique name.