Gtk3 Python环境下为右键上下文菜单关联GMenuModel动作的问题
嘿,我看了你的代码,问题其实很明确:你的右键菜单能正常显示,但选项都是灰的,是因为从GMenuModel创建的Gtk.Menu没有关联到应用的动作组,找不到app.option1这些动作的实现,所以才会置灰不可用。
给你几个具体的修改点,改完就能正常使用了:
1. 给上下文菜单绑定应用动作组
在AppWindow的__init__方法里,创建完self.context_menu之后,加一行代码,把应用的动作组和菜单里的app前缀关联起来:
# 把应用的动作组插入到菜单,对应菜单里动作的"app"前缀 self.context_menu.insert_action_group("app", self.get_application())
这一步是关键,菜单里的动作写的是app.option1,其中app是动作组的前缀,我们需要告诉菜单这个前缀对应的是你的应用实例(它是动作的载体),这样菜单才能找到对应的动作实现。
2. 删掉多余的错误代码
在Application的do_startup方法里,有一行多余的代码:
self.set_app_menu(builder.get_object("app-menu"))
你的MENU_XML里根本没有定义app-menu这个菜单,这行代码运行时会报错,直接删掉就行。
3. 可选:去掉冗余的显示代码
在on_right_click方法里,self.context_menu.show_all()其实是多余的,popup_at_pointer会自动处理菜单的显示,删掉这行也不影响功能。
修改后的完整代码
import sys import os import gi gi.require_version("Gtk", "3.0") from gi.repository import GLib, Gio, Gtk _curr_dir = os.path.split(__file__)[0] # This would typically be its own file MENU_XML = """ <?xml version="1.0" encoding="UTF-8"?> <interface> <menu id="context_menu"> <section> <item> <attribute name="label" translatable="yes">Option 1</attribute> <attribute name="action">app.option1</attribute> </item> <item> <attribute name="label" translatable="yes">Option 2</attribute> <attribute name="action">app.option2</attribute> </item> </section> </menu> </interface> """ class AppWindow(Gtk.ApplicationWindow): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.set_default_size(400, 300) self.connect("destroy", Gtk.main_quit) # Load the UI from the Glade file builder = Gtk.Builder() builder = Gtk.Builder.new_from_string(MENU_XML, -1) # Get the context menu defined in the XML self.context_menu = Gtk.Menu.new_from_model(builder.get_object("context_menu")) # 新增:关联应用的动作组到菜单 self.context_menu.insert_action_group("app", self.get_application()) # Add a simple label self.label = Gtk.Label(label="Right-click to open context menu") self.add(self.label) self.label.show() # Connect the button press event (right-click) self.connect("button-press-event", self.on_right_click) def on_right_click(self, widget, event): # Check if it's the right-click (button 3) if event.button == 3: # Show the context menu self.context_menu.popup_at_pointer(event) class Application(Gtk.Application): def __init__(self, *args, **kwargs): super().__init__( *args, application_id="org.example.myapp", **kwargs ) self.window = None def do_startup(self): Gtk.Application.do_startup(self) action = Gio.SimpleAction.new("option1", None) action.connect("activate", self.on_option1_activated) self.add_action(action) action = Gio.SimpleAction.new("option2", None) action.connect("activate", self.on_option2_activated) self.add_action(action) # 删掉了多余的set_app_menu代码 def do_activate(self): # We only allow a single window and raise any existing ones if not self.window: # Windows are associated with the application # when the last one is closed the application shuts down self.window = AppWindow(application=self, title="Main Window") self.window.present() def on_quit(self, action, param): self.quit() def on_option1_activated(self, widget): print("Option 1 selected") def on_option2_activated(self, widget): print("Option 2 selected") if __name__ == "__main__": app = Application() app.run()
原理补充一下
GMenuModel里的动作标识符(比如app.option1)是[动作组前缀].[动作名]的格式,你已经在应用里注册了option1和option2这两个动作,但菜单不知道app这个前缀对应的是哪个对象,所以通过insert_action_group把应用实例和app前缀绑定后,菜单就能正确找到动作的实现,自然就可以点击了。
现在运行修改后的代码,右键点击窗口里的标签,菜单选项就可以正常触发对应的打印输出啦!
备注:内容来源于stack exchange,提问作者DobbyTheElf

