使用 PyGObject 正确构造和突出显示 GtkPopoverMenu

Properly structure and highlight a GtkPopoverMenu using PyGObject

我正在尝试使用 Gtk.PopoverMenus 制作一个适当的 Gtk.HeaderBar 示例,以显示如何使用不同的小部件。我看了很多示例和代码,但无法弄清楚 Gtk.ModelButton.

如何工作

特别是this sentence对我来说毫无意义:

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.

无论如何,这是我在 PopoverMenu 中仅使用普通小部件的尝试,导致突出显示不一致(应突出显示整行):

所以我的问题是: ModelButtons 是如何子类化的,所以它们显示为正确的 PopoverMenu-widgets?

这里是 Python 代码:

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()

下面是 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>

我设法解决了我的大部分问题。最重要的是,似乎必须使用 Gtk.ApplicationGtk.ApplicationWindow。我不能说我完全理解为什么,但这使得可以将 ActionsGio.SimpleActions 的形式添加到应用程序中。 Gtk.Builder 解析菜单的 XML,可以简单地使用 set_popover 方法将其添加到菜单中。然后对于 XML 中定义的每个动作(不要忘记 XML 中的 app.- 前缀)创建一个 Gio.SimpleAction(没有 app.-前缀作为名称)在 Python 代码中并添加到应用程序中。我有一个普通按钮和一个可以工作的复选按钮。仍在与单选按钮作斗争,但这可能是另一个问题。

这里是 Python-代码:

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)

和菜单的 XML 文件:

<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>