Issue 30678: Widget variable binding does not work if mainloop is called from a different function (original) (raw)

Created on 2017-06-15 14:37 by Javier Dehesa, last changed 2022-04-11 14:58 by admin.

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

2022-04-11 14:58:47

admin

set

github: 74863

2017-06-16 11:19:51

Javier Dehesa

set

messages: +

2017-06-16 10:12:05

Javier Dehesa

set

messages: +

2017-06-16 10:04:43

paul.moore

set

messages: +

2017-06-16 09:36:00

Javier Dehesa

set

messages: +

2017-06-16 09:32:56

louielu

set

nosy: + louielu
messages: +

2017-06-16 09:25:38

Javier Dehesa

set

messages: +

2017-06-15 14:37:56

Javier Dehesa

create