Функция tkinter повторяется дважды при включении виджетов ttk - PullRequest
0 голосов
/ 06 ноября 2018

Программа работает как задумано, когда я просто использую виджеты tkinter. Когда я использую ttk виджеты, программа повторяется дважды. Я попробовал почти все, что в моих знаниях, чтобы это исправить, я считаю, что *args как-то связано с этим. Есть ли способ, чтобы мой function не запускался дважды?

from tkinter import *
from tkinter import ttk
root = Tk()

first = StringVar(root)
second = StringVar(root)
Ore = {'Options': [''], 'Yes': ['One'], 'No': ['Two']}
entry1 = ttk.OptionMenu(root, first, *Ore.keys())
entry2 = ttk.OptionMenu(root, second, '')
entry1.pack()
entry2.pack()


def _up_options(*args):
    print('update_options')
    ores = Ore[first.get()]
    second.set(ores[0])
    menu = entry2['menu']
    menu.delete(0, 'end')

    for line in ores:
        print('for')
        menu.add_command(label=line, command=lambda choice=line: second.set(choice))


first.trace('w', _up_options)

root.mainloop()

PS, я вставил *args в свою функцию для работы. Если кто-нибудь может объяснить это, я был бы очень признателен

Ответы [ 3 ]

0 голосов
/ 07 ноября 2018

Я думаю, я понял это. Проблема в том, что переменная на самом деле устанавливается дважды с помощью ttk OptionMenu.

Взгляните на этот кусок кода из tkinter OptionMenu:

for v in values:
    menu.add_command(label=v, command=_setit(variable, v, callback))

Добавляет кнопку в меню для каждого значения с помощью команды _setit. Когда вызывается _setit, он устанавливает переменную и другой обратный вызов, если предоставляется:

def __call__(self, *args):
    self.__var.set(self.__value)
    if self.__callback:
        self.__callback(self.__value, *args)

Теперь посмотрите на этот кусок кода из ttk OptionMenu:

for val in values:
    menu.add_radiobutton(label=val,
        command=tkinter._setit(self._variable, val, self._callback),
        variable=self._variable)

Вместо command это добавляет radiobutton в меню. Все радиокнопки «сгруппированы», связав их с одной и той же переменной. Поскольку у радиокнопок есть переменная, при нажатии одной из них для переменной устанавливается значение кнопки. Кроме того, добавляется та же команда, что и в tkinter OptionMenu. Как уже говорилось, это устанавливает переменную, а затем запускает другую команду обеспечивает. Как видите, теперь переменная обновляется дважды, один раз, потому что она связана с радиокнопкой, и еще раз, потому что она установлена ​​в функции _setit. Поскольку вы отслеживаете изменение переменной и переменная задается дважды, ваш код также выполняется дважды.

Поскольку переменная задается дважды из кода ttk, я думаю, что вы ничего не можете с этим поделать. Если вы не измените переменную из какой-либо другой части вашего кода, кроме как из OptionMenu, вы можете не отслеживать переменную, а вместо этого добавить свою функцию как command в OptionMenu:

entry1 = ttk.OptionMenu(root, first, *Ore.keys(), command=_up_options)

P.S. это было введено с this commit после this bugreport .
Я предполагаю, что при добавлении variable=self._variable команда должна была быть изменена на command=self._callback.

0 голосов
/ 23 марта 2019

Вместо трассировки StringVar добавьте обратный вызов в качестве аргумента команды для конструктора OptionMenu.

0 голосов
/ 06 ноября 2018

Вы можете понять проблему в сообщении об ошибке:

Исключение в обратном вызове Tkinter Traceback (последний вызов был последним): Файл "C: \ Users \ user \ AppData \ Local \ Programs \ Python \ Python36 \ lib \ tkinter__init __. Py", строка 1699, в вызов вернуть self.func (* аргументы) Ошибка типа: _up_options () принимает 0 позиционных аргументов, но 3 было дано

Изначально вы не используете _up_options Когда вы изменяете параметры, которые вы вызываете _up_options, чтобы отследить первый StringVar и изменить его на значение следующего объекта в словаре.

Теперь, когда вы делаете это, вы запускаете все объекты в словаре, поэтому вам нужен *args, поэтому функция lambda будет работать на всех заданных аргументах!

Что касается вашей проблемы:

Когда я использую виджеты ttk, программа повторяется дважды.

EDIT

См. Ответ @ fhdrsdg!

Решение состоит в том, чтобы просто изменить command=tkinter._setit(self._variable, val, self._callback) на command=self._callback.

Надеюсь, вы найдете это полезным!

...