如何在菜单中插入滚动条?

How to insert a scrollbar to a menu?

我想在 tkinter 的菜单小部件中添加另一个小部件,在本例中为比例小部件。 现在我看到的唯一解决方案是创建一个新命令并使用比例小部件打开一个新的 window 或在其他地方创建比例小部件。两者似乎都不太吸引我。

欢迎提出任何关于如何存档的想法:)

您无法为其添加滚动条,但是。这是一种 hacky 方式,也许很难理解,但我可以尝试解释一下。

注意 正如 Bryan 在链接线程中提到的,这似乎是一个 Windows 只有 的解决方案。

import tkinter as tk


def my_first_function():
    print('first')

def my_second_function():
    print('second')

def check_for_scroll(event):
    check = root.call(event.widget, "index","active")
    if check == 0: #index of button up
        scroll_up()
        root.after(100,lambda e=event:check_for_scroll(e)) # check again after 100ms
    if check == file_menu.index('end'):
        scroll_down()
        root.after(100,lambda e=event:check_for_scroll(e))

def scroll_up():
    index_of_first_command=1
    index_of_last_command=1
    label_of_last_command = file_menu.entrycget(index_of_first_command, 'label')
    try:
        for i, k in enumerate(dict_of_commands):
            if k == label_of_last_command:
                previous_command_label = list(dict_of_commands)[i-1]
                previous_command = list(dict_of_commands.values())[i-1]
                if i != 0: #avoid to get the last as first
                    file_menu.delete(index_of_first_command) #first before pull down button
                    file_menu.insert_command(index_of_first_command,
                                             label=previous_command_label,
                                             command=previous_command)
    except Exception as e:
        print(e)

def scroll_down():
    index_of_first_command=1
    index_of_last_command=1
    label_of_last_command = file_menu.entrycget(index_of_last_command, 'label')
    try:
        for i, k in enumerate(dict_of_commands):
            if k == label_of_last_command:
                next_command_label = list(dict_of_commands)[i+1]
                next_command = list(dict_of_commands.values())[i+1]

                file_menu.delete(index_of_first_command) #first after pull up button
                file_menu.insert_command(index_of_last_command,
                                         label=next_command_label,
                                         command=next_command)
    except:
        pass

space = '         '
dict_of_commands = {'first' : my_first_function,
                    'second': my_second_function}

root = tk.Tk()
menubar = tk.Menu(root)
root.config(menu=menubar)

file_menu = tk.Menu(menubar,tearoff=0)
menubar.add_cascade(label='File', menu=file_menu)
file_menu.bind('<<MenuSelect>>', check_for_scroll)

file_menu.add_command(label=space+u'\u25B2'+space, font=["Arial", 8,'bold'])

file_menu.add_command(label='first', command=my_first_function)

file_menu.add_command(label=space+u'\u25BC'+space, font=["Arial", 8,'bold'])


root.mainloop()

所以这段代码照常创建了您的 window 和一个菜单栏:

root = tk.Tk()
menubar = tk.Menu(root)
root.config(menu=menubar)
file_menu = tk.Menu(menubar,tearoff=0)
menubar.add_cascade(label='File', menu=file_menu)
file_menu.add_command(label=space+u'\u25B2'+space, font=["Arial", 8,'bold'])
file_menu.add_command(label='first', command=my_first_function)
file_menu.add_command(label=space+u'\u25BC'+space, font=["Arial", 8,'bold'])
root.mainloop()

这行对你来说很重要:

file_menu.bind('<<MenuSelect>>', check_for_scroll)

此行绑定事件 MenuSelect,如果您的光标悬停在菜单命令上,它会 happens/triggers。对于这个事件,我绑定了一个名为 check_for_scroll 的函数,它看起来像这样:

def check_for_scroll(event):
    check = root.call(event.widget, "index","active")
    if check == 0: #index of button up
        scroll_up()
        root.after(100,lambda e=event:check_for_scroll(e)) # check again after 100ms
    if check == file_menu.index('end'):
        scroll_down()
        root.after(100,lambda e=event:check_for_scroll(e))

下面的行检查触发事件的命令的索引。有了这个,我们用箭头检查我们感兴趣的按钮是第一个还是最后一个。

check = root.call(event.widget, "index","active")

如果它是第一个,例如这里的代码被执行:

if check == 0: #index of button up
        scroll_up()
        root.after(100,lambda e=event:check_for_scroll(e)) # check again after 100ms

它 calls/triggers 函数 scroll_up 然后使用 tkinter 的 after 方法重新触发自身,就像一个循环。 scroll_up 函数的构建方式与 scroll_down 类似,只是方向相反。让我们仔细看看:

def scroll_up():
    index_of_first_command=1
    index_of_last_command=1
    label_of_last_command = file_menu.entrycget(index_of_first_command, 'label')
    try:
        for i, k in enumerate(dict_of_commands):
            if k == label_of_last_command:
                previous_command_label = list(dict_of_commands)[i-1]
                previous_command = list(dict_of_commands.values())[i-1]
                if i != 0: #avoid to get the last as first
                    file_menu.delete(index_of_first_command) #first before pull down button
                    file_menu.insert_command(index_of_first_command,
                                             label=previous_command_label,
                                             command=previous_command)
    except Exception as e:
        print(e)

在这个函数中我们需要知道命令的第一个和最后一个位置,因为我们想删除一个并在那个position/index上插入另一个。为了实现这一点,我创建了一个字典,其中包含标签和 tkinter 命令项的功能,如下所示。 (这可以动态创建,但让我们把它留到另一个问题)

dict_of_commands = {'first' : my_first_function,
                    'second': my_second_function}

所以我们在函数中遍历这个 enumerated/indexed 字典并检查 k/key/label 是我们感兴趣的项目。如果为真,我们通过列出字典键获得 previous_command 并通过以下行获得之前提取的键:

next_command_label = list(dict_of_commands)[i+1]

这行类似于字典的值:

next_command = list(dict_of_commands.values())[i+1]

毕竟我们可以删除一个并在我们喜欢的地方插入一个:

file_menu.delete(index_of_first_command) #first after pull up button
file_menu.insert_command(index_of_last_command,
                         label=next_command_label,
                         command=next_command)

我知道这段代码可以改进很多,但看起来很难理解。所以请不要评判我。

我希望这能解决您的问题,即使它不是您想要的那样。但是这段代码避免了你几乎没有编写自己的菜单栏。

如果我的回答还有问题,请告诉我。