Search code examples
python-2.7gtk3glademsys2gtkbuilder

How to populate a GtkMenu with some GtkMenuItem and set its parent in Glade?


In a Gtk.Toolbar I have a Gtk.MenuToolButton with an arrow to which I want to associate a Gtk.Menu, preferably built in Glade.

My code was this:

self.menuToolButtonCalculate.set_menu(self.get_object("menuCalculate"))

where self.menuToolButtonCalculate is obtained through the Gtk.Builder.get_object method, and self is an instance of a subclass of Gtk.Builder.

The program displayed some Gtk/Gdk-CRITICAL warnings in the terminal when the window loaded, and when I clicked on the arrow the program crashed. I also populated the Gtk.Menu defined in Glade in Python code before showing the window and the program still crashes, so I am now sure that the problem is not that the menu is empty.

I replaced that line with this, for testing purposes:

self.menuToolButtonCalculate.set_menu(Gtk.Menu())

The program still displays Gtk/Gdk-CRITICAL warnings in the terminal when loading the window, but when I click the arrow the program does not crash, it displays an empty contextual menu near the arrow.

These Gtk/Gdk-CRITICAL warnings are:

(python2.exe:14672): Gtk-CRITICAL **: gtk_widget_set_size_request: assertion 'GTK_IS_WIDGET (widget)' failed

(python2.exe:14672): Gtk-CRITICAL **: gtk_container_add: assertion 'GTK_IS_CONTAINER (container)' failed

(python2.exe:14672): Gdk-CRITICAL **: gdk_window_get_height: assertion 'GDK_IS_WINDOW (window)' failed

(python2.exe:14672): Gtk-CRITICAL **: gtk_widget_reparent: assertion 'priv->parent != NULL' failed

(python2.exe:14672): Gdk-CRITICAL **: gdk_window_get_width: assertion 'GDK_IS_WINDOW (window)' failed

(python2.exe:14672): Gdk-CRITICAL **: gdk_device_get_position_double: assertion 'GDK_IS_DEVICE (device)' failed

I use Windows 10 Pro x64 with the latest updates installed, MSYS2 with the latest updates installed and inside MSYS2, x86 versions of: Python 2.7.13, Glade 3.20 and GTK+ 3.22.

Update:

Simplified test code that strangely works perfectly without crashes or warnings in terminal:

example1.glade

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkMenu" id="menu">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
  </object>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkToolbar">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkMenuToolButton" id="menuToolButton">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">__glade_unnamed_2</property>
            <property name="use_underline">True</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="homogeneous">True</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

example1.py

# coding=utf-8

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


b = Gtk.Builder()
b.add_from_file("example1.glade")
w = b.get_object("window1")
mtb = b.get_object("menuToolButton")
m = b.get_object("menu")
mtb.set_menu(m)

w.connect("delete-event", Gtk.main_quit)
w.show_all()
Gtk.main()

The original program is too big to put it all here. Please tell me what details from the original code do you want me to post so you can help me.

Update 2:

In the main program's Glade .ui file I have this definition of the GtkMenu:

<object class="GtkMenu" id="menuCalculate">
  <property name="visible">True</property>
  <property name="can_focus">False</property>
  <property name="tearoff_state">True</property>
  <property name="tearoff_title">Ce dorești să calculez?</property>
</object>

If I remove The last two elements (the one that causes change in the behavior of the program is tearoff_state)

<object class="GtkMenu" id="menuCalculate">
  <property name="visible">True</property>
  <property name="can_focus">False</property>
</object>

The program does not crash and it opens an empty menu when I click the arrow of the GtkMenuToolButton, the GtkMenu declared in the Glade file.

So the problem is caused just by the GtkMenu created in Glade which did not have a parent and that is required by the tearoff_state property.

  1. If this is true, what is the recommended way to set the parent of GtkMenu to a GtkMenuToolButton('s arrow), preferably in Glade, but at least in code?
  2. From what I see, it is impossible to populate a GtkMenu with items inside Glade, so I must populate it from code. Is this true?

Solution

  • Picture is worth a 1000 words

    A GtkMenu can be assigned to a GtkMenuToolButton in Glade as can the menus contents


    Populating a GtkMenu

    Having added a GtkMenu to your Glade project select it in the sidebar and you should see an additional toolbar icon appear:

    Edit button

    This will open the Menu editor:

    Menu Editor

    From here you can switch to the 'Hierarchy' tab where you can use the + / - buttons to add remove items from the menu

    Hierarchy Tab


    Assign the GtkMenu to a GtkMentToolButton

    Now unfortuantly Glade doesn't support doing this however the underlying GtkBuilder file format does as explained in the docs

    GtkMenuToolButton as GtkBuildable

    The GtkMenuToolButton implementation of the GtkBuildable interface supports adding a menu by specifying “menu” as the “type” attribute of a element.

    An example for a UI definition fragment with menus:

    <object class="GtkMenuToolButton">
        <child type="menu">
            <object class="GtkMenu"/>
        </child>
    </object>
    

    So hit save (and quit) and switch to a text editor like Gedit, I would expect your file to look something like so:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Generated with glade 3.20.0 -->
    <interface>
      <requires lib="gtk+" version="3.20"/>
      <object class="GtkWindow">
        <property name="can_focus">False</property>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkToolbar">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <child>
                  <object class="GtkMenuToolButton" id="toolbutton">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="label" translatable="yes">Menu</property>
                    <property name="use_underline">True</property>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="homogeneous">True</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
          </object>
        </child>
      </object>
      <object class="GtkMenu" id="menu">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkMenuItem">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">Yes</property>
            <property name="use_underline">True</property>
          </object>
        </child>
        <child>
          <object class="GtkMenuItem">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">You Can</property>
            <property name="use_underline">True</property>
          </object>
        </child>
        <child>
          <object class="GtkMenuItem">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">Define Menus</property>
            <property name="use_underline">True</property>
          </object>
        </child>
        <child>
          <object class="GtkMenuItem">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">In Glade</property>
            <property name="use_underline">True</property>
          </object>
        </child>
      </object>
    </interface>
    

    Now create a <child type="menu"> inside the GtkMenuToolButton like so:

    <object class="GtkMenuToolButton" id="toolbutton">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="label" translatable="yes">Menu</property>
        <property name="use_underline">True</property>
        <child type="menu">
        </child>
    </object>
    

    Then cut & paste the GtkMenu definition inside that <child type="menu">, don't worry about indenting Glade will sort that out next.

    The file should now look something like so:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Generated with glade 3.20.0 -->
    <interface>
        <requires lib="gtk+" version="3.20"/>
        <object class="GtkWindow">
            <property name="can_focus">False</property>
            <child>
                <object class="GtkBox">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="orientation">vertical</property>
                    <child>
                        <object class="GtkToolbar">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <child>
                            <object class="GtkMenuToolButton" id="toolbutton">
                                <property name="visible">True</property>
                                <property name="can_focus">False</property>
                                <property name="label" translatable="yes">Menu</property>
                                <property name="use_underline">True</property>
                                <child type="menu">
                                    <object class="GtkMenu" id="menu">
                                        <property name="visible">True</property>
                                        <property name="can_focus">False</property>
                                        <child>
                                            <object class="GtkMenuItem">
                                                <property name="visible">True</property>
                                                <property name="can_focus">False</property>
                                                <property name="label" translatable="yes">Yes</property>
                                                <property name="use_underline">True</property>
                                            </object>
                                        </child>
                                        <child>
                                            <object class="GtkMenuItem">
                                                <property name="visible">True</property>
                                                <property name="can_focus">False</property>
                                                <property name="label" translatable="yes">You Can</property>
                                                <property name="use_underline">True</property>
                                            </object>
                                        </child>
                                        <child>
                                            <object class="GtkMenuItem">
                                                <property name="visible">True</property>
                                                <property name="can_focus">False</property>
                                                <property name="label" translatable="yes">Define Menus</property>
                                                <property name="use_underline">True</property>
                                            </object>
                                        </child>
                                        <child>
                                            <object class="GtkMenuItem">
                                                <property name="visible">True</property>
                                                <property name="can_focus">False</property>
                                                <property name="label" translatable="yes">In Glade</property>
                                                <property name="use_underline">True</property>
                                            </object>
                                        </child>
                                    </object>
                                </child>
                            </object>
                            <packing>
                                <property name="expand">False</property>
                                <property name="homogeneous">True</property>
                            </packing>
                        </child>
                    </object>
                    <packing>
                        <property name="expand">False</property>
                        <property name="fill">True</property>
                        <property name="position">0</property>
                    </packing>
                    </child>
                    <child>
                        <placeholder/>
                    </child>
                </object>
            </child>
        </object>
    </interface>
    

    Now save and close the file and open it again in Glade.

    You should now see that glade recognises it as the 'menu child', hit save and Glade will sort out the indenting for you as well

    menu child

    Using Glade 3.20.0 on Windows 10