Tkinter: проблема с экземпляром класса, когда указан StringVar - PullRequest
0 голосов
/ 29 декабря 2018

Все, я немного сбит с толку этим и надеюсь, что кто-то там сможет объяснить, что происходит.

Пример немного запутан, но он служит иллюстрацией для иллюстрации проблемы, с которой я сталкиваюсьмое приложение.У меня есть три класса, которые расширяют Tkinter.Frame;A, B и X. B содержит экземпляр A и переменную (метку), значение которой зависит от состояния меню параметров в A. X имеет несколько экземпляров B. Когда я устанавливаю name = 'opt_var' вВ строке ниже (из класса A) b1 и b2 в X действуют так, как если бы они были одним и тем же экземпляром B.

self.opt_var = StringVar(name='opt_var')

Однако, когда я удаляю name = 'opt_var' из объявления StringVar, я получаюожидаемое (т.е. независимое) поведение двух отдельных экземпляров.

Что меня еще больше смущает, так это то, что я делаю сравнение двух экземпляров B, используя «b1 is b2» - который, как я понимаю, должен проверять, указывают ли две переменные на один и тот же объект, - и я получаю True, которыйнеожиданно, поскольку b1 и b2 создаются отдельно.

Любое понимание происходящего приветствуется.

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()

1 Ответ

0 голосов
/ 29 декабря 2018

Tkinter - это тонкая оболочка для встроенного интерпретатора Tcl.Этот интерпретатор ничего не знает о пространствах имен python.

Когда вы создаете экземпляр StringVar, в этом интерпретаторе Tcl создается новая глобальная переменная.Обычно Tkinter выбирает уникальное имя переменной.Когда вы явно даете ему имя, оно становится именем глобальной переменной.Если вы создадите несколько экземпляров StringVar и дадите каждому из них одно и то же имя, все они будут привязаны к одной и той же переменной Tcl.

Решение простое: каждому экземпляру вашего класса необходимо создать экземплярStringVar с уникальным именем.

...