Правильно структурируйте и выделите GtkPopoverMenu, используя PyGObject

Я пытаюсь сделать пример правильного Gtk.HeaderBar с Gtk.PopoverMenus это показывает, как используются разные виджеты. Я посмотрел на множество примеров и кода, но не могу понять, как работать Gtk.ModelButton,

Особенно это предложение не имеет смысла для меня:

Когда действие указывается через свойства "action-name" и "action-target", роль кнопки (т. Е. Является ли она простой, флажок или переключатель) определяется типом действия и не должны быть явно указаны с помощью свойства "роль".

В любом случае, вот моя попытка просто использовать обычные виджеты в 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>

1 ответ

Решение

Мне удалось решить большую часть моего вопроса. Самое главное, кажется, что нужно использовать Gtk.Application а также Gtk.ApplicationWindow, Я не могу сказать, что я полностью понимаю, почему, но это позволяет добавить Actions в виде Gio.SimpleActions к заявке. Gtk.Builder разбирает XML меню, и его можно просто добавить в меню, используя set_popover-метод. Затем для каждого действия, определенного в XML (не забудьте app.префикс в XML) 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>
Другие вопросы по тегам