Tkinter OptionMenu唯一标识符可重复显示值

时间:2020-06-12 13:31:31

标签: python tkinter optionmenu

以该问题为基础-Tkinter OptionMenu DisplayOptions and Assignment Values

在此处回答代码:

import Tkinter as tk
master = tk.Tk()
variable = tk.StringVar(master)
options = {"one": 1, "two": 2}
tk.OptionMenu(master, variable, *options.keys()).pack()
...
wanted = options[variable.get()]

但是,即使字符串可能相同,我也希望整数值可以用作唯一标识符。请参阅以下内容。

# this will overwrite the "a" value to be 3
# the first entry will be lost
options = {"a": 1, "b": 2, "a": 3}

# so instead switch the order...
options = {1: "a", 2: "b", 3: "a"}
# then we can use values() to provide the options
tk.OptionMenu(master, variable, *options.values()).pack()
...
# but now how do I replace this line 
# to get the correct identifier for the option chosen?
wanted = options[variable.get()]

在OptionMenu中是否可能有重复的显示文本,这些文本链接回正确的唯一标识符?

1 个答案:

答案 0 :(得分:1)

下面是我如何解决此问题的方法。我扩展了自己的StringVar类,并重新实现了OptionMenu类。我将其放在自己的源文件中,我不建议“覆盖” tk实现。

import tkinter as tk

class LinkedIntStringVar(tk.StringVar):
    '''Takes a dictionary of int to strings. default 'get' function
        will return strings as normal, but there is also special function for
        returning based on the integer values 'get_int'.
    Setting the variable requires using the integer value set in int_string_dict'''
    def __init__(self, master=None, int_string_dict=None, value=None, name=None):
        tk.StringVar.__init__(self, master, value, name)
        self.__int_string_dict = int_string_dict
        self.__current_int_value = 0

    def get_int(self):
        """Return value of variable as integer."""
        return self.__current_int_value

    def set(self, value):
        """Set the variable to VALUE."""
        string_value = self.__int_string_dict[value]
        self.__current_int_value = value
        super().set(string_value)

# No changes from tkinter's implementation here, I just like it to be available.
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)

# Changes to this class are commented below
class OptionMenu(tk.Menubutton):
    """OptionMenu which allows the user to select a value from a menu."""
    def __init__(self, master, variable, values, **kwargs): # removed 'value' from args
        """Construct an optionmenu widget with the parent MASTER, with
        the resource textvariable set to VARIABLE, the initially selected
        value VALUE, the other menu values VALUES and an additional
        keyword argument command."""
        kw = {"borderwidth": 2, "textvariable": variable,
              "indicatoron": 1, "relief": tk.RAISED, "anchor": "c",
              "highlightthickness": 2}
        tk.Widget.__init__(self, master, "menubutton", kw)
        self.widgetName = 'tk_optionMenu'
        menu = self.__menu = tk.Menu(self, name="menu", tearoff=0)
        self.menuname = menu._w
        # 'command' is the only supported keyword
        callback = kwargs.get('command')
        if 'command' in kwargs:
            del kwargs['command']
        if kwargs:
            raise tk.TclError('unknown option -'+kwargs.keys()[0])
        # Issues with the variables clashing, 
        # I personally just depend on the variable's value so it was easiest 
        # just to remove this unneeded portion (for my case)
        #menu.add_command(label=value,
        #         command=_setit(variable, value, callback))
        for v in values.keys(): # Change this line to handle dict instead of list
            # Change this line to set to the String value in the dict
            menu.add_command(label=values[v],
                     command=_setit(variable, v, callback))
        self["menu"] = menu

    def __getitem__(self, name): # No changes
        if name == 'menu':
            return self.__menu
        return tk.Widget.__getitem__(self, name)

    def destroy(self): # No changes
        """Destroy this widget and the associated menu."""
        tk.Menubutton.destroy(self)
        self.__menu = None

下面是如何使用它的示例。

# import the custom classes
import tkinter as tk

master = tk.Tk()

options = { 0: 'None', 1: 'Test', 2: 'Example', 3: 'Test'}
variable = LinkedIntStringVar(master, int_string_dict=options)
variable.set(0)

option_menu = OptionMenu(master, variable, options,
                         command=lambda value, *args: print('Changed to', value)).pack()
tk.mainloop()

您将在回调中看到返回的整数值。字符串可以用variable.get()返回,整数值也可以用variable.get_int()返回