Shortcut by initial letter Optionmenu in Python Tkinter

Asked

Viewed 466 times

1

I have an Optionmenu in Tkinter in Python which is a large list, when I press the letter G for example it does not direct me to the elements with the letter G and also does not allow the use of pageUp and Pagedown. Is there a way to make this work? Or some other type of Optionmenu in Tkinter that allows these functions? Using the mouse scroll for this would be a good alternative too.

My Optionmenu:

ent7 = tk.OptionMenu(janela,EntradaEnergia, *energias)

1 answer

1

Tkinter, although it caters well for basic needs, has very few active developers, and sometimes the best documentation is to look directly into the source code (in addition to the semi-official documentation that is on http://effbot.org/tkinterbook/)

Well, doing these two things I discovered that

  1. no support for scrollbars in menus (including Optionmenu)
  2. There is how to configure accelerators for each option, but I haven’t found a way for the accelerator to actually work directly.

You can try to explore the accelerators - from the Optionmenu it is possible to recover an internal menu using Mapping annotation. You can do: menu_interno = ent7["menu"] and will have a menu object that will have all the methods here: http://effbot.org/tkinterbook/menu.htm

At this point it is possible to configure an "Accelerator" for the inputs, for example: menu_interno.entryconfig(0, accelerator=energias[0][0]), and the first letter of the first option in the "energies" list will be displayed to the right of the option in the menu (0 is the index of the input). But he just does it: the throttle didn’t work for me.

Solution

it is possible to use the bind Tkinter normal to call a callback to a typed key when the menu is open, and make that callback set the variable and adjust the Optionbutton display "manually". As the function to do this will need the Optionmenu, the options and the initial variable, it is more practical to make the function itself create the Optionmenu and configure all this:

import tkinter

def accelerated_menu(root, variable, *options, command=None):
    accelerators = {option[0]: option for option in options}

    menu = tkinter.OptionMenu(root, variable, *options, command=command)

    def callback(event):
        key = getattr(event, "char", None)
        if not key in accelerators:
            return
        value = accelerators[key]
        variable.set(value)
        menu["text"] = value
        if command:
            command(value)

    for index, option in enumerate(options):
        menu["menu"].entryconfig(index, accelerator=option[0])

    menu["menu"].bind("<Key>", callback)

    return menu

Just use the above function instead of calling the OptionMenu directly, that the first letter of each option will be a shortcut. Of course, this is not ideal, since basically you limit yourself to always having a different letter in the first position- and in the case of repeated first letters, the top options are forgotten and the last ones are valid. But it is easy to create a logic with "if" to accept tuples of two items instead of strings in the options, allowing you to explicitly pass the shortcuts.

Alternative

The OptionMenu just like some other widgets, it is built in pure Python from other more primitive widgets (in this case, the same "Menu" and a "Menubutton"). You can build a class by extending a Tkinter Widget that has not only the requirements you want, but is even nicer.

I would suggest a class that contains a grid of buttons, configured without relief and without border, that appears/disappears in a frame when clicking a button in permanent display. You would have complete control of the frame (so you can add scroll bar, show options in multiple columns), and buttons (you can configure keyboard shortcuts with "bind" of events).

Creating such a class will require a few hours of work and is not within the scope of this site - but is perfectly feasible.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.