How to properly preselect a menu item

Hi –

I’d like to bring up a pop-up menu with a preselected item. If I just call GtkMenuItem.select() on the item before popping up the menu, the item is selected as expected. However navigating the menu with the keyboard arrow keys doesn’t start at the selected item. That is, pressing the down arrow selects the first item in the menu, but I’d like it to select the one below the initial selection.

There are other problems involving deselecting the preselected item when using the mouse, so that there don’t appear to be two items selected, but I can work around that by catching enter-notify and focus change events in the other menu items (I think). I don’t know how to get the arrow keys to behave, though.

Here’s a very simple example. Press the button and use the arrow keys to move into the menu.

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk

def popup(button):
    menu = Gtk.Menu()
    for name in ("A", "B", "C", "D", "E"):
        menuitem = Gtk.MenuItem(name)
        menu.append(menuitem)
        if name == "C":
            menuitem.select()
            menuitem.grab_focus()
    menu.show_all()
    menu.popup_at_widget(button, Gdk.Gravity.SOUTH_WEST, Gdk.Gravity.NORTH_WEST,
                         None)

window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
window.connect('delete-event', Gtk.main_quit)

button = Gtk.Button("Click me")
button.connect("clicked", popup)
window.add(button)
window.show_all()
Gtk.main()

Thanks,
Steve

1 Like

FYI, Gtk.Menu is discontinued in GTK 4, so please make changes and migrate to Gio.Menu. You can find more information here : How to create menus for apps using Python?

Can you tell me the use case of pre-selecting a menu item ? It sounds interesting ^^

The pop-up menu is used to switch between a series of options. The user will often progress through the series in order. But the user won’t necessarily go in order, so I want to present a list of all of the options with the current one selected to make it clear where they are in the sequence. I could use a ComboBox, but I also want tooltips, so a pop-up menu is better.

I will look into Gio.Menu. I thought I read somewhere that gtk3 isn’t going away, though. Am I confused (again)? It looks like switching from Gtk.Menu to Gio.Menu is nontrivial.

Thanks.
– Steve

Define “going away”, in terms of active development it’s already gone but in terms of existing and getting the odd bug fix it’s certainly going to be around for a good few years

3 Likes

Are you really sure that a menu is suitable for this use-case ? Also pre-selecting a menu means, automatically clicking it for user, which would cause confusion to users.

Perhaps, you can have a look at Gtk.Assistant and Gtk.Stack with a stack switcher or side bar.

Since you say, users can move not in a proper series, a stack based solution would be perfect.

I just realized that I should be using RadioMenuItems in a pop-up menu. That probably would do almost exactly what I need.

To answer your questions, though, I’m already using a Stack, but there are too many options to put in a side bar or stack switcher. The pop-up menu is controlling the Stack. There’s an image of the gtk2 version here: https://www.ctcms.nist.gov/~langer/oof2man/Chapter-Tasks.html#Section-Tasks-Overview. The widget in question is the one marked “Task Menu”. It has 16 entries in it. Using a pop-up menu allows the user to see all of the options when necessary, but doesn’t take up a lot of screen real estate when it’s not active.

– Steve

1 Like

Could an example like that help you?

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, Gio, GLib

window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
window.connect('delete-event', Gtk.main_quit)

# Gtk.ApplicationWindow has an automatic action group called "win"
action_group = Gio.SimpleActionGroup()
window.insert_action_group ("ui", action_group)

default_value = "C"
variant = GLib.Variant.new_string(default_value);
action = Gio.SimpleAction.new_stateful("menu",GLib.VariantType.new("s"),variant)
action_group.insert(action)

model = Gio.Menu()
for name in ("A", "B", "C", "D", "E"):
    model.append(name,"ui.menu::"+name)
model.freeze()

grid = Gtk.Grid()
grid.set_orientation (Gtk.Orientation.VERTICAL)
grid.set_hexpand(True)
grid.set_halign(Gtk.Align.CENTER)
window.add(grid)

button = Gtk.MenuButton("Click me")
button.set_menu_model(model)
grid.add(button)

label = Gtk.Label(default_value)
grid.add(label)

def on_menu_action_changed(group,name,variant):
    action.set_state(variant)
    label.set_label(variant.get_string())
action_group.connect('action-state-changed::menu',on_menu_action_changed)

window.show_all()

Gtk.main()

Thank you. That will be helpful.

– Steve

Well, using RadioMenuItems solves the problem only in that it shows the user what the current selection is, but it’s been vetoed as clunky and ugly. I’m back to wanting to either be able to preselect a menu item (so that when the menu appears the arrow keys will move the selection, and not make it look like there are two selections), or being able to add tooltips to the pull-down menu in a ComboBox. Thank you for any suggestions!

    • Steve

To clarify: gtk3 is not going away. It is entering a new phase of its life - the golden years.

To answer the concrete question: I don’t think a menu is a great fit for the task at hand as a ui pattern. And GtkMenu just doesn’t do what you want.

You are correct that it’s not a great fit. A combo box does do what I want, except that as far as I can tell it doesn’t allow tool tips on the individual entries, which I need. Is there a way to get around that?

I want the tooltips because there are a large number of places in the program at which the user can choose one of set of options, with anywhere from 2 - 20 choices. The text for the selected option needs to be brief, to avoid cluttering up the GUI, but the user may need more information to make the correct choice, so the tooltip contains a longer description.

Thanks,
Steve

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.