当使用ttk小部件时,tkinter函数将自身重复两次

时间:2018-11-06 11:53:40

标签: python-3.x tkinter ttk

当我仅使用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 :(得分:3)

我想我明白了。问题在于该变量实际上是由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

答案 1 :(得分:2)

您可以通过错误消息了解问题所在:

  

Tkinter回调中的异常   追溯(最近一次通话):     调用中的文件“ C:\ Users \ user \ AppData \ Local \ Programs \ Python \ Python36 \ lib \ tkinter__init __。py”,行1699       返回self.func(* args)   TypeError:_up_options()接受0个位置参数,但给出了3个

最初,您不使用_up_options来更改选项时调用_up_options来跟踪第一个StringVar并将其更改为字典中下一个对象的值。

现在,当您在字典中的所有对象上运行时,因此,您需要*args以便lambda函数将在给定的所有args上运行!

关于您的问题:

  

当我使用ttk的小部件时,程序会重复两次。

编辑

请参阅@fhdrsdg的答案!

解决方案是将command=tkinter._setit(self._variable, val, self._callback)更改为command=self._callback

希望这对您有所帮助!

答案 2 :(得分:0)

不是跟踪StringVar,而是为OptionMenu构造函数添加回调作为命令参数。