【问题标题】:Tkinter optionmenu add_command strings do not show on widget when selected选择时,Tkinter optionmenu add_command 字符串不会显示在小部件上
【发布时间】:2018-07-31 13:43:36
【问题描述】:
from tkinter import *


main = Tk()

var = StringVar()
options = OptionMenu(main, var, 'option1')
options.grid()
options['menu'].add_command(label='option2')


main.mainloop()

当您在菜单中选择“option1”时,它会在小部件上显示该字符串。如果您选择“option2”,它不会在小部件上显示该字符串。如果您为 option2 设置命令,它将运行。为什么选择'option2'时它没有显示在小部件中?

python 3.5

更新:

通过 for 循环添加字符串,我遇到了一个问题,即每个字符串的命令使用相同的变量。

from tkinter import *

def _print(var, string):
    print(string)
    var.set(string)

lst = ['hello', 'bob', 'testing']

main = Tk()
var = StringVar()
options = OptionMenu(main, var, 'option1')
options.grid()

for string in lst:
    options['menu'].add_command(label=string, command=lambda: _print(var, string))

main.mainloop()

我尝试在循环中使用lambda event, i=string: _print(var, i)),但 lamdba 不喜欢它。说它缺少的“事件”。我已将事件放入 _print 函数和对 _print 的调用中,但我得到与 lamdba 缺少事件相同的错误。知道如何为循环中的每个命令传递正确的变量吗?

【问题讨论】:

  • 请包含通用的python 标签,除非问题仅与版本 3 有关。

标签: python tkinter optionmenu


【解决方案1】:

当您在 Optionmenu() 小部件中声明“option2”时,它可以工作。更多示例可以阅读herehere

from tkinter import *

main = Tk()

var = StringVar()
options = OptionMenu(main, var, 'option1', 'option2')
options.grid()
#options['menu'].add_command(label='option2')


main.mainloop()

我发现menu 选项用于在Menu() 小部件中创建项目,但不适用于Optionmenu() 小部件。这是here。您可以阅读有关如何声明 .Menu() 小部件 here 的更多信息。祝您的 tkinter 开发顺利。

编辑1:

失败的原因是您没有声明与.add_command 方法关联的command 选项。单独完成此操作后,您会注意到 Optionmenu 仍然没有使用添加的菜单项的新标签进行更新。要解决此问题,您必须使用控制变量StringVar.set() 方法进行更新。请参阅下面的修订脚本。

from tkinter import *

def _print1(value):
    # By default, callback to OptionMenu has a positional argument for the 
    #  menu item's label. 
    print(value)
    print(var.get())
    
def _print2():
    var.set('option2')
    print(var.get())

main = Tk()

var = StringVar()
options = OptionMenu(main, var, 'option1',command=_print1)
options.grid()
options['menu'].add_command(label='option2', command=_print2)
# To add more clickable menu items, the 'add_command' method requires you to
# to declare it's options 'label' and 'command'.

main.mainloop()

编辑2:

  1. 备注,我已在 Edit1 中将command 方面添加到options。一世 之前没有解决,但认为需要显示 完整性。
  2. 在您的 UPDATE 中回答您的问题,做您想做的事,我 将脚本重写为类对象。我也使用了内部类 _setit 在 tkinter 中找到,OptionMenu 小部件曾用于 配置command 使用的回调。这种方法克服了您遇到的问题。

修改后的代码:

from tkinter import *

class App(Tk):

    def __init__(self, parent=None):
        Tk.__init__(self, parent)
        self.parent=parent
        self.createOM()
        self.grid()

    def createOM(self):
        # Create OptionMenu
        omlist=['option1']
        self.var = StringVar()
        self.options = OptionMenu(self, self.var, *omlist,
                                  command=self._print1)
        self.options.grid()
        
        values = ['hello', 'bob', 'testing']
        for v in values:
            self.options['menu'].add_command(
                label=v, command=_setit(self.var, v, self._print2))
        # To add more clickable menu items, the 'add_command' method requires you to
        # to declare it's options 'label' and 'command'.
       
    def _print1(self, value, *args):
        #callback for OptionMenu has these arguments because inherently it
        #uses the _setit class to configure the callback with these arguments. 
        print()        
        print(value)        
        #self.var.set('option10') #Uncomment to change OptionMenu display
        print(self.var.get())

    def _print2(self, value, *args):
        print()        
        print(value)        
        #self.var.set('option20') #Uncomment to change OptionMenu display
        print(self.var.get())
   
#The following class is extracted from tkinter.
class _setit:
    """Internal class. It wraps the command in the widget OptionMenu."""
    def __init__(self, var, value, callback=None):
        self.__value = value
        self.__var = var
        self.__callback = callback
    def __call__(self, *args):
        self.__var.set(self.__value)
        if self.__callback:
            self.__callback(self.__value, *args)


if __name__ == "__main__":
    app = App()
    app.mainloop()

【讨论】:

  • 我很欣赏这些信息,但我已经知道如果我在创建选项菜单时将其包含在选项菜单中而不是稍后添加它,它会起作用。我的问题是为什么稍后添加它时会失败。
  • @sidnical 我已经附加了一个答案来回答你的问题。最好的祝福。
  • @sidnical 我刚刚看到@Nae 已在 10 小时前回复了您,但该答案不正确。你正在做的不是黑客攻击。您的方法是允许/合理的,因为OptionMenuMenu 的继承。出现问题是因为您忘记提供 add_command 方法中所需的 command 选项。
  • 所以你说它需要为每个手动设置小部件到该字符串的选项设置一个命令?我想这是您创建小部件时默认配置所做的事情。
  • @sidnical 我之前评论说OptionMenuMenu 的继承。这是不正确的。它实际上从 Menubutton 继承了 it 属性,Widget 继承自 Widget 类。但是从 Tk8.0 开始,Menubutton 已经过时了。所以目前,OptionMenu 确实从 tkinter 中的 Widget 类继承了它的属性。
【解决方案2】:

它没有选择任何东西,因为它不应该选择。你基本上是在破解OptionMenu 的视觉部分。您作为values 传递的项目首先被添加到菜单中,然后被赋予修改选择的命令选项。当您调用 options['menu'].add_command(label='option2') 时,您基本上只是在修改小部件的可视部分,'option2' 添加到值中,因此命令选项 设置.自己看源码here

【讨论】:

  • 所以他们没有办法添加/修改选项菜单内容?令人惊讶的是有多少页面说使用 add_command、insert 和 .entryconfig(我认为是旧版本)来做这种事情。猜猜我会破坏对象并使用更新的值列表重新创建它。谢谢。
  • @sidnical 您可以通过一些变通方法修改其内容,但我认为这太费力了。 OptionMenu 的工作方式也让我感到惊讶。或许见ttk.Combobox
猜你喜欢
  • 2017-12-27
  • 1970-01-01
  • 1970-01-01
  • 2012-07-23
  • 2017-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多