Wiki

Gtk Menus

Menus are lists of commands, accessed from a drop-down or pull-down list. Menus are typically found within a menubar, which displays the title of each menu.

Creating the Menu Bar

The menu bar itself is created as an instance of 'MenuBar'. The menubar is a regular Gtk widget and can be located anywhere you choose, but for the classic menu-at-the-top, place the menubar at the top of a non-expandable 'VBox' before adding it to the window.

The following 'createMenu' definition shows how to construct and add the menubar to a window class. (A complete example is given at the bottom of this page.)

    def createMenu
        # create the main menubar
        mainMenu = MenuBar ()

        # -- the menu construction code goes here --

        # Add the menubar to the top of the window
        box = VBox(false, 0) # make this 'false' so menubar stays compact
        box.packStart(mainMenu, false, false, 0)
        .add(box)

Adding Menus to the Menu Bar

The menubar displays instances of 'MenuItems'. To make the headings for menus such as 'File', 'Edit', 'Help' we would write:

        menuItem = MenuItem("_File")
        mainMenu.append(menuItem)

        menuItem = MenuItem("_Edit") # (it is convenient to reuse the 'menuItem' variable)
        mainMenu.append(menuItem)

        menuItem = MenuItem("_Help")
        mainMenu.append(menuItem)

Notice the underscore in the title - this gets converted into a mnemonic for accessing the menu, and is shown as an underlined letter in the menu.

To get an actual menu under each heading, we first create an instance of 'Menu' and attach it as the submenu for the respective menuitem:

        menuItem = MenuItem("_File")
        mainMenu.append(menuItem)
        fileMenu = Menu()
        menuItem.submenu = fileMenu

        menuItem = MenuItem("_Edit")
        mainMenu.append(menuItem)
        editMenu = Menu()
        menuItem.submenu = editMenu

        menuItem = MenuItem("_Help")
        mainMenu.append(menuItem)
        helpMenu = Menu()
        menuItem.submenu = helpMenu

If you include the above code in the 'createWidgets' method of our example program, you will see a menubar on the window with nothing in the menus.

Adding Items to Menus

For our menus to be useful, we need to include items to launch commands or store information. There are several categories of items we can include, but here we look at: simple items (rather like buttons), check boxes, and radio buttons.

Simple Items

A simple menu item shows as a text label. The following code shows the three steps of creating a menu item, attaching a method to call when activated, and adding it to a menu:

        # create a menu item for 'show values'
        menuItem = MenuItem("Show values")
        # attach method to call when menu item is selected
        listen menuItem.activated, ref .showValues
        # add the menu item to the editMenu
        editMenu.append(menuItem)

Check Boxes

Check boxes can be placed onto a menu, using the 'CheckMenuItem' class. We need to store a reference to the checkbox to later retrieve its state, by accessing its 'active' attribute (see the method 'showValues' at the bottom of this page).

        # create a check box item and add to the edit menu
        _checkbox = CheckMenuItem("Select me")
        editMenu.append(_checkbox)

Radio Buttons

Radio buttons on menus are similar to regular radio buttons: the user is allowed to pick one option out of a pre-defined set.

To store the state of radio buttons we can use a variable to hold the currently selected option, and provide some methods to change that variable as different options are selected. For example, to select a language from three options:

    var _language as String

    def selectCobra(obj, e)
        _language = "cobra"

    def selectJava(obj, e)
        _language = "java"

    def selectPython(obj, e)
        _language = "python"

The radio buttons can then be set up to call the respective 'selectN' method when toggled:

        # create some radio buttons
        _language = "cobra" # set initial option
        radioItem1 = RadioMenuItem("Cobra") # this one selected by default
        editMenu.append(radioItem1)
        listen radioItem1.toggled, ref .selectCobra
        radioItem2 = RadioMenuItem(radioItem1, "Java")
        editMenu.append(radioItem2)
        listen radioItem2.toggled, ref .selectJava
        radioItem3 = RadioMenuItem(radioItem1, "Python")
        editMenu.append(radioItem3)
        listen radioItem3.toggled, ref .selectPython

As with standard radio buttons, radio buttons are placed into groups by including the first member of the group in the constructor of subsequent members.

Stock Items and Accelerator Groups

There are several standard menu items that you find in many applications: these are listed in Gtk as a set of  Stock items. Stock items can be used on menus by creating an instance of an 'ImageMenuItem'. Because these standard items are also usually linked with keyboard shortcuts (such as CTRL-Q to quit the application), the menu items must also be linked to an accelerator group. The following code creates an accelerator group, and then adds 'new', 'open', 'save' and 'quit' items to the file menu:

        # create an accelerator group
        acceleratorGroup = AccelGroup()
        .addAccelGroup(acceleratorGroup)
        # -- new item
        menuItem = ImageMenuItem(Stock.new, acceleratorGroup)
        listen menuItem.activated, ref .newAction
        fileMenu.append(menuItem)
        # -- open item
        menuItem = ImageMenuItem(Stock.open, acceleratorGroup)
        listen menuItem.activated, ref .openAction
        fileMenu.append(menuItem)
        # -- save item
        menuItem = ImageMenuItem(Stock.save, acceleratorGroup)
        listen menuItem.activated, ref .saveAction
        fileMenu.append(menuItem)
        # -- separator
        fileMenu.append(SeparatorMenuItem())
        # -- quit item
        menuItem = ImageMenuItem(Stock.quit, acceleratorGroup)
        listen menuItem.activated, ref .quit
        fileMenu.append(menuItem)

A submenu is a menu contained within another menu; a submenu is shown on the screenshot at the top of this page. Submenus are simply menus added to an existing menu instance rather than the menubar. We can create a submenu for the language selector as follows:

        languageMenu = Menu()                               # instance of menu on which to build submenu
        radioItem1 = RadioMenuItem("Cobra")
        languageMenu.append(radioItem1)                     # attach the radio items to the submenu
        listen radioItem1.toggled, ref .selectCobra
        radioItem2 = RadioMenuItem(radioItem1, "Java")
        languageMenu.append(radioItem2)
        listen radioItem2.toggled, ref .selectJava
        radioItem3 = RadioMenuItem(radioItem1, "Python")
        languageMenu.append(radioItem3)
        listen radioItem3.toggled, ref .selectPython
        languageMenuItem = MenuItem("Language")             # create a menuitem labelled 'Language'
        languageMenuItem.submenu = languageMenu             # and attach the languageMenu to it as a submenu
        editMenu.append(languageMenuItem)                   # before adding to the edit menu

Complete Example

# @args -pkg:gtk-sharp-2.0   # remove initial '#'

use Gtk

class ExampleWindow inherits Window
    cue init
        base.init("Menus Example")
        .setDefaultSize(300,200)
        listen .deleteEvent, ref .quit
        .createMenu

    def quit(obj, e)
        Application.quit

    def newAction(obj, e)
        print "Clicked new"

    def openAction(obj, e)
        print "Clicked open"
    
    def saveAction(obj, e)
        print "Clicked save"

    def aboutAction(obj, e)
        print "Clicked about"

    var _checkbox as CheckMenuItem

    var _language as String

    def selectCobra(obj, e)
        _language = "cobra"

    def selectJava(obj, e)
        _language = "java"

    def selectPython(obj, e)
        _language = "python"

    def showValues(obj, e)
        print "Show values: [_checkbox.active] and language [_language]"

    def createMenu
        # create the main menubar
        mainMenu = MenuBar ()
        # create an accelerator group
        acceleratorGroup = AccelGroup()
        .addAccelGroup(acceleratorGroup)

        # -- menu 1: file menu, with three stock buttons and a separator
        menuItem = MenuItem("_File")
        mainMenu.append(menuItem)
        fileMenu = Menu()
        menuItem.submenu = fileMenu

        # create an accelerator group
        acceleratorGroup = AccelGroup()
        .addAccelGroup(acceleratorGroup)
        # -- new item
        menuItem = ImageMenuItem(Stock.new, acceleratorGroup)
        listen menuItem.activated, ref .newAction
        fileMenu.append(menuItem)
        # -- open item
        menuItem = ImageMenuItem(Stock.open, acceleratorGroup)
        listen menuItem.activated, ref .openAction
        fileMenu.append(menuItem)
        # -- save item
        menuItem = ImageMenuItem(Stock.save, acceleratorGroup)
        listen menuItem.activated, ref .saveAction
        fileMenu.append(menuItem)
        # -- separator
        fileMenu.append(SeparatorMenuItem())
        # -- quit item
        menuItem = ImageMenuItem(Stock.quit, acceleratorGroup)
        listen menuItem.activated, ref .quit
        fileMenu.append(menuItem)
        
        # -- menu 2: edit menu, with menu item, check item, and submenu
        menuItem = MenuItem("_Edit")
        mainMenu.append(menuItem)
        editMenu = Menu()
        menuItem.submenu = editMenu

        # create a menu item for 'show values'
        menuItem = MenuItem("Show values")
        # attach method to call when menu item is selected
        listen menuItem.activated, ref .showValues
        # add the menu item to the editMenu
        editMenu.append(menuItem)

        # create a check box item
        _checkbox = CheckMenuItem("Select me")
        editMenu.append(_checkbox)

        # create some radio buttons on a submenu
        _language = "cobra" # set initial item
        languageMenu = Menu()
        radioItem1 = RadioMenuItem("Cobra")
        languageMenu.append(radioItem1)
        listen radioItem1.toggled, ref .selectCobra
        radioItem2 = RadioMenuItem(radioItem1, "Java")
        languageMenu.append(radioItem2)
        listen radioItem2.toggled, ref .selectJava
        radioItem3 = RadioMenuItem(radioItem1, "Python")
        languageMenu.append(radioItem3)
        listen radioItem3.toggled, ref .selectPython
        languageMenuItem = MenuItem("Language")
        languageMenuItem.submenu = languageMenu
        editMenu.append(languageMenuItem)

        # -- menu 3: help menu
        menuItem = MenuItem("_Help")
        mainMenu.append(menuItem)
        helpMenu = Menu()
        menuItem.submenu = helpMenu

        menuItem = ImageMenuItem(Stock.about, acceleratorGroup)
        listen menuItem.activated, ref .aboutAction
        helpMenu.append(menuItem)

        # Add the menubar to the top of the window
        box = VBox(false, 0) # make this 'false' so menubar stays compact
        box.packStart(mainMenu, false, false, 0)
        .add(box)

class MainProgram
    def main
        Application.init
        window = ExampleWindow()
        window.showAll
        Application.run

Attachments