Gio.Menu 项始终禁用布尔操作

Gio.Menu items always disabled for boolean actions

我似乎无法使 GMenu 项适用于布尔状态操作。

这是一个 MCVE(灵感来自 LiuLang’s example 如何使用 GMenu 和 GAction):

#!/usr/bin/env python3

# Copyright (C) 2013 LiuLang <gsushzhsosgsu@gmail.com>

# Use of this source code is governed by GPLv3 license that can be found
# in http://www.gnu.org/licenses/gpl-3.0.html

from gi.repository import Gio, Gtk, GLib
import sys

menus_str ='''
<?xml version="1.0"?>
<interface>
<menu id="menubar">
<submenu id="appmenu">
  <attribute name="label" translatable="yes">App</attribute>
  <section>
    <item>
      <attribute name="label" translatable="yes">Quit</attribute>
      <attribute name="action">app.quit</attribute>
    </item>
  </section>
  <section>
    <item>
      <attribute name="label" translatable="yes">Toggle broken</attribute>
      <attribute name="action">app.toggle</attribute>
    </item>
  </section>
  <section>
    <item>
      <attribute name="label" translatable="yes">Radios select A</attribute>
      <attribute name="action">app.radio</attribute>
      <attribute name="target">A</attribute>
    </item>
    <item>
      <attribute name="label" translatable="yes">Radios select B</attribute>
      <attribute name="action">app.radio</attribute>
      <attribute name="target">B</attribute>
    </item>
  </section>
</submenu>
</menu>
</interface>
'''

class App:
    def __init__(self):
        self.app = Gtk.Application.new('org.liulang.test', 0)
        self.app.connect('startup', self.on_app_startup)
        self.app.connect('activate', self.on_app_activate)

    def run(self, argv):
        self.app.run(argv)

    def on_app_activate(self, app):
        self.window.show_all()

    def on_app_startup(self, app):
        self.window = Gtk.ApplicationWindow.new(app)
        self.window.set_default_size(640, 480)
        self.window.set_title('Gio Actions Demo')
        self.window.set_border_width(5)

        app.add_window(self.window)

        builder = Gtk.Builder()
        # It is better to load ui from a seperate file
        builder.add_from_string(menus_str)
        builder.connect_signals(self)

        menubar = builder.get_object('menubar')
        app.set_menubar(menubar)

        # No-state no-parameter action 'quit'
        action = Gio.SimpleAction.new('quit', None)
        action.connect('activate', lambda *args: self.app.quit())
        self.app.add_action(action)

        # Boolean state action 'toggle' − should display as a checkbox ?
        action = Gio.SimpleAction.new_stateful('toggle', GLib.VariantType.new('b'), GLib.Variant('b', True))
        action.connect('activate', lambda action, target: print('toggling boolean action to', target.get_boolean()) or action.change_state(target))
        self.app.add_action(action)

        # String state action 'radio' − correctly displays as a radio
        action = Gio.SimpleAction.new_stateful('toggle', GLib.VariantType.new('b'), GLib.Variant('b', True))
        action = Gio.SimpleAction.new_stateful('radio', GLib.VariantType.new('s'), GLib.Variant('s', 'A'))
        action.connect('activate', lambda action, target: print('toggling radio action to', target.get_string()) or action.change_state(target))
        self.app.add_action(action)



if __name__ == '__main__':
    app = App()
    app.run(sys.argv)

我希望菜单中的第二项是反映 toggled 操作状态的复选框。相反,我得到的项目始终显示为灰色,即使操作处于活动状态也是如此。

菜单项似乎与操作紧密相连,因为它的完成方式与其他有效操作相同,并且使用 Gtk.Application.set_accels_for_action 添加快捷方式会在菜单中显示加速器。交互式调试(使用 GTK_DEBUG=interactive)确认操作处于活动状态。

请看下面的菜单截图:

我似乎无法弄清楚我做错了什么。文档很少,只包含简单的无状态操作示例。 google 这个问题也很难解决,因为 Gtk 有 3 种或 4 种不同的菜单和操作范例以及诸如此类的东西,这会扰乱任何搜索尝试。

我在 some source code 中找到了以下评论:

 * ## Boolean State
 * 
 * An action with a boolean state will most typically be used with a "toggle"
 * or "switch" menu item. The state can be set directly, but activating the
 * action (with no parameter) results in the state being toggled.
 * 
 * Selecting a toggle menu item will activate the action. The menu item should
 * be rendered as "checked" when the state is true.

但是 GMenuModel wiki documentation 中列出的属性似乎都不允许 select 切换项目。这也不是其他操作的工作方式,请参阅从未指定样式的单选示例。

事实证明,布尔可切换动作不带参数,动作中包含的状态就足够了。

即代码必须是:

action = Gio.SimpleAction.new_stateful('toggle', GLib.VariantType.new('b'), None)