2037

Properly structure and highlight a GtkPopoverMenu using PyGObject

I am trying to make an example of a proper Gtk.HeaderBar with Gtk.PopoverMenus that shows how the different widgets are used. I looked at a lot of examples and code, but can't figure out, how to work the Gtk.ModelButton.

Especially this sentence makes no sense to me:

When the action is specified via the “action-name” and “action-target” properties, the role of the button (i.e. whether it is a plain, check or radio button) is determined by the type of the action and doesn't have to be explicitly specified with the “role” property.

In any case, here is my attempt to just use normal widgets in the PopoverMenu, leading to inconsistent highlighting (the whole row should be highlighted):

<img src="https://i.stack.imgur.com/Yxb0j.png" alt="enter image description here">

<strong>So my question is:</strong> How are ModelButtons subclassed, so they appear as proper PopoverMenu-widgets?

Here is the Python code:

from gi.repository import Gtk, Gio

class HeaderBarWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="HeaderBar & PopoverMenu")
        self.set_border_width(10)
        self.set_default_size(400, 400)
        builder = Gtk.Builder()
        objects = builder.add_objects_from_file("popovermenu_layout.xml", ("pom_options", ""))
        pom_opt = builder.get_object("pom_options")
        builder.connect_signals(self)

        hb = Gtk.HeaderBar()
        hb.set_show_close_button(True)
        hb.props.title = "HeaderBar & PopoverMenu"
        self.set_titlebar(hb)

        def on_click(button, popovermenu):
            """
            Toggles the respective popovermenu.
            """
            if popovermenu.get_visible():
                popovermenu.hide()
            else:
                popovermenu.show_all()

        button_opt = Gtk.Button()
        icon = Gio.ThemedIcon(name="format-justify-fill-symbolic")
        image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
        button_opt.add(image)
        hb.pack_end(button_opt)
        pom_opt.set_relative_to(button_opt)
        button_opt.connect("clicked", on_click, pom_opt)

        self.add(Gtk.TextView())

    def print_something(self, modelbutton, event):
        print("you pressed a button")

    def night_mode_switcher(self, switch, state):
        Gtk.Settings.get_default().set_property("gtk-application-prefer-dark-theme", state)

win = HeaderBarWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()


And here is the model for the PopoverMenu:

<interface>
    <object class="GtkPopoverMenu" id ="pom_options">
      <child>
        <object class="GtkBox">
          <property name="visible">True</property>
          <property name="margin">10</property>
          <property name="orientation">vertical</property>
          <child>
            <object class="GtkModelButton" id="mb_print">
              <property name="visible">True</property>
              <property name="text" translatable="yes">Print</property>
              <property name="can_focus">True</property>
              <property name="receives_default">True</property>
              <signal name="button-press-event" handler="print_something" swapped="no"/>
            </object>
          </child>
          <child>
            <object class="GtkCheckButton" id="checkbutton1">
              <property name="label" translatable="yes">checkbutton</property>
              <property name="visible">True</property>
              <property name="can_focus">True</property>
              <property name="receives_default">False</property>
              <property name="draw_indicator">True</property>
            </object>
          </child>
          <child>
              <object class="GtkBox" id="box1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <child>
                  <object class="GtkSwitch" id="switch1">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <signal name="state-set" handler="night_mode_switcher" swapped="no"/>
                    <property name="margin_start">9</property>
                    <property name="margin_end">9</property>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">0</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkModelButton" id="mb_night">
                    <property name="text" translatable="yes">Night Mode</property>
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="margin_start">9</property>
                    <property name="margin_end">9</property>
                  </object>
                  <packing>
                    <property name="expand">True</property>
                    <property name="fill">True</property>
                    <property name="position">1</property>
                  </packing>
                </child>
              </object>
          </child>
        </object>
      </child>
    </object>
</interface>

    

Answer1:

I managed to solve most of what my question was. Most importantly it seems that one has to use Gtk.Application and Gtk.ApplicationWindow. I can't say that I fully understand why, but that makes it possible to add Actions in the form of Gio.SimpleActions to the application. Gtk.Builder parses the XML of the menu, and one can simply add it tot the menu using the set_popover-method. Then for each action defined in the XML (don't forget the app.-prefix in the XML) a Gio.SimpleAction is created (without the app.-prefix as name) in the Python-code and added to the application. I got a normal button, and a checkbutton to work. Still struggling with the radiobutton, but that might be another question.

Here is the Python-code:

from gi.repository import Gio, Gtk, GLib import sys class MainApplication(Gtk.Application): def __init__(self): Gtk.Application.__init__(self, application_id="needs.dot", flags=Gio.ApplicationFlags.FLAGS_NONE) #https://developer.gnome.org/gio/unstable/GApplication.html#g-application-id-is-valid self.connect("activate", self.activate_window) def activate_window(self, app): """ The activate signal of Gtk.Application passes the MainApplication class to the window. The window is then set as a window of that class. """ self.window = Gtk.ApplicationWindow() self.window.set_default_size(500, 400) self.hb = Gtk.HeaderBar() self.hb.set_show_close_button(True) self.hb.props.title = "HeaderBar & PopOverMenu" self.window.set_titlebar(self.hb) button_settings = Gtk.MenuButton() icon = Gio.ThemedIcon(name="format-justify-fill-symbolic") image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON) button_settings.add(image) self.hb.pack_end(button_settings) self.builder = Gtk.Builder() self.builder.add_from_file("popovermenu_layout.xml") pom_options = self.builder.get_object("pom_options") button_settings.set_popover(pom_options) #self.builder.connect_signals(self) #Not needed because of using actions? app.add_window(self.window) #Connects to the action. The first part of the XML name is left away: #<property name="action-name">app.print</property> #becomes simply "print". action_print = Gio.SimpleAction.new("print", None) action_print.connect("activate", self.print_something) app.add_action(action_print) #app.toggle becomes -> toggle action_toggle = Gio.SimpleAction.new_stateful("toggle", None, GLib.Variant.new_boolean(False)) action_toggle.connect("change-state", self.toggle_toggled) app.add_action(action_toggle) btn = Gtk.Button("Button") self.window.add(btn) self.window.show_all() def print_something(self, action, variable): print("something") def toggle_toggled(self, action, state): action.set_state(state) Gtk.Settings.get_default().set_property("gtk-application-prefer-dark-theme", state) def on_action_quit_activated(self, action): self.app.quit() if __name__ == "__main__": """ Creates an instance of the MainApplication class that inherits from Gtk.Application. """ app = MainApplication() app.run(sys.argv)

and the XML-file for the menu:

<interface> <object class="GtkPopoverMenu" id ="pom_options"> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="margin">10</property> <property name="orientation">vertical</property> <child> <object class="GtkModelButton" id="mb_print"> <property name="visible">True</property> <property name="action-name">app.print</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="label" translatable="yes">Print</property> </object> </child> <child> <object class="GtkModelButton" id="mp_toggle"> <property name="visible">True</property> <property name="action-name">app.toggle</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="label" translatable="yes">Night Mode</property> </object> </child> </object> </child> </object> </interface>

Recommend

  • What is the gdbus library name? Technique to find it?
  • Leaflet custom icon resize on zoom. Performance icon vs divicon
  • Async HTTP request using GIO
  • Best way to make marker resizable in leaflet
  • My google map (static html with JS) won't show up suddenly?
  • Using AVAudioPlayer across multiple scenes Swift and be able to adjust volume
  • Pin a button to the bottom corner of a container
  • How do I automatically add spacing between divs without using percentage?
  • How to replace a text-align: -webkit-center?
  • Sticky footer with flexbox
  • jQuery: add elements until a particular height is reached
  • Validate child input components on submit with Vee-Validate and vue js 2
  • Button click event not firing in jQuery
  • How do I superscript characters in a UIButton?
  • onBackPressed() not being executed
  • Android fill_parent issue
  • All Classes Conforming to Protocol Inherit Default Implementation
  • Using variable in a value field in jMeter
  • Adding a button at the bottom of a table view
  • Retrieving value from sql ExecuteScalar()
  • Deleting and Updating values from a cusrsor adapter
  • javaw.exe and eclipse startup problems
  • Is possible to count alias result on mysql
  • How to convert from System.Drawing.Color to Excel.ColorFormat in C#? Change comment color
  • Fill an image in a square container while keeping aspect ratio
  • Importing jscolor library in angular 2
  • Font Awesome Showing Box instead of Icons
  • To display the title for the current loaction in map in iphone
  • jquery mobile loadPage not working
  • php design question - will a Helper help here?
  • Matrix multiplication with MKL
  • KeystoneJS: Relationships in Admin UI not updating
  • How can I get HTML syntax highlighting in my editor for CakePHP?
  • JTable with a ScrollPane misbehaving
  • Angular 2 constructor injection vs direct access
  • Java static initializers and reflection
  • Android Google Maps API OnLocationChanged only called once
  • Is it possible to post an object from jquery to bottle.py?
  • UserPrincipal.Current returns apppool on IIS
  • Python/Django TangoWithDjango Models and Databases