classification
Title: Widget variable binding does not work if mainloop is called from a different function
Type: behavior Stage:
Components: Tkinter, Windows Versions: Python 3.5
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Javier Dehesa, louielu, paul.moore, steve.dower, tim.golden, zach.ware
Priority: normal Keywords:

Created on 2017-06-15 14:37 by Javier Dehesa, last changed 2017-06-16 11:19 by Javier Dehesa.

Files
File name Uploaded Description Edit
checkbutton_bad.png Javier Dehesa, 2017-06-15 14:37
Messages (7)
msg296100 - (view) Author: Javier Dehesa (Javier Dehesa) Date: 2017-06-15 14:37
When you build a Tkinter interface with variables, if tkinter.Tk.mainloop is called from a different function that the one creating the variable objects, then in some cases the widgets associated with the variables will not be set to the right value. I have not been able to find a consistent pattern, but I have some examples.

The following script works correctly:
```
import tkinter as tk
from tkinter import ttk

def make_var_cb(root):
    v = tk.BooleanVar(root, True)
    cb = ttk.Checkbutton(root, text='Checkbutton', variable=v)
    cb.pack()
    root.mainloop()

if __name__ == '__main__':
    root = tk.Tk()
    make_var_cb(root)
```

But the following does not (the result is shown in the attached image):
```
import tkinter as tk
from tkinter import ttk

def make_var_cb(root):
    v = tk.BooleanVar(root, True)
    cb = ttk.Checkbutton(root, text='Checkbutton', variable=v)
    cb.pack()

if __name__ == '__main__':
    root = tk.Tk()
    make_var_cb(root)
    root.mainloop()
```

However, the following _does_ work again:
```

def make_var(root):
    return tk.BooleanVar(root, True)

def make_cb(root, v):
    return ttk.Checkbutton(root, text='Checkbutton', variable=v)

if __name__ == '__main__':
    root = tk.Tk()
    v = make_var(root)
    cb = make_cb(root, v)
    cb.pack()
    root.mainloop()
```
msg296167 - (view) Author: Javier Dehesa (Javier Dehesa) Date: 2017-06-16 09:25
Note, this is not something specific to check buttons, the same happens with other widgets such as entries.
msg296168 - (view) Author: Louie Lu (louielu) * Date: 2017-06-16 09:32
Sorry, but I can't see the different between your first and second code. On my Linux, it all set to check the box when it run up. Do I miss anything?
msg296169 - (view) Author: Javier Dehesa (Javier Dehesa) Date: 2017-06-16 09:36
Yeah is quite subtle, I should have pointed it out...
The difference is that `root.mainloop()` is called inside `make_var_cb(root)` in the first example and under `if __name__ == '__main__'` in the second one. I have experience the problem on Windows 10, and I'd say there is a good chance it's a platform-specific thing...
msg296171 - (view) Author: Paul Moore (paul.moore) * (Python committer) Date: 2017-06-16 10:04
I suspect that the problem is somehow related to garbage collection - in your failing example you don't keep any references to v or cb. I don't know what references tkinter keeps internally, it may be that this is OK because the root window is guaranteed to keep references to everything, but it's certainly worth a look.
msg296172 - (view) Author: Javier Dehesa (Javier Dehesa) Date: 2017-06-16 10:12
I see what you mean. Looking at TkDocs (not sure if this is an "official" or "officially endorsed" source or not), one of the Python examples in the "Tk Concepts" section (http://www.tkdocs.com/tutorial/concepts.html) says:

> Whether or not you save the widget object in a variable is entirely up to you, and depends of course whether you'll need to refer to it later. Because the object is inserted into the widget hierarchy, it won't be garbage collected even if you don't keep your own reference to it.

However, it very much looks like it may be related to what you said.
msg296178 - (view) Author: Javier Dehesa (Javier Dehesa) Date: 2017-06-16 11:19
With the additional hint of garbage collection I have found now a number of examples of this behaviour (e.g. https://stackoverflow.com/questions/7439432/python-themed-tkinter-entry-variable-will-not-set).
However, I haven't found actual documentation warning of this, so I'm not sure if this is really expected or just something people has gotten used to live with. One could argue that you normally wouldn't need a variable in the first place if you are not keeping a reference to it, but I'm not sure what is the benefit of having a (I assume) weak reference in the widget. Maybe there are solid technical reasons but, from what I have seen, it seems to have caused more than one confusion.
History
Date User Action Args
2017-06-16 11:19:51Javier Dehesasetmessages: + msg296178
2017-06-16 10:12:05Javier Dehesasetmessages: + msg296172
2017-06-16 10:04:43paul.mooresetmessages: + msg296171
2017-06-16 09:36:00Javier Dehesasetmessages: + msg296169
2017-06-16 09:32:56louielusetnosy: + louielu
messages: + msg296168
2017-06-16 09:25:38Javier Dehesasetmessages: + msg296167
2017-06-15 14:37:56Javier Dehesacreate