Notes on GmenuModel based DBus menus

This commit is contained in:
Kovid Goyal 2014-10-31 19:20:53 +05:30
parent 4c82321f66
commit 065017ba25
2 changed files with 90 additions and 2 deletions

View File

@ -9,7 +9,7 @@ __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
# Demo program to explore the GTK DBus interface, which is only partially documented
# at https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
import sys, dbus, struct
import sys, dbus, struct, time, signal
from threading import Thread
from pprint import pformat
@ -37,6 +37,11 @@ UI_INFO = """
<menuitem action='ChoiceTwo'/>
<separator />
<menuitem action='ChoiceThree'/>
<separator />
<menuitem action='DisabledAction'/>
<menuitem action='InvisibleAction'/>
<menuitem action='TooltipAction'/>
<menuitem action='IconAction'/>
</menu>
</menubar>
<toolbar name='ToolBar'>
@ -93,6 +98,12 @@ class MenuExampleWindow(Gtk.ApplicationWindow):
self.popup = uimanager.get_widget("/PopupMenu")
self.add(box)
i = Gtk.Image.new_from_stock(Gtk.STOCK_OK, Gtk.IconSize.MENU)
# Currently the menu items image is not exported over DBus, so probably
# best to stick with using dbusmenu
uimanager.get_widget('/MenuBar/ChoicesMenu/IconAction')
uimanager.get_widget('/MenuBar/ChoicesMenu/IconAction').set_image(i)
uimanager.get_widget('/MenuBar/ChoicesMenu/IconAction').set_always_show_image(True)
def add_file_menu_actions(self, action_group):
action_filemenu = Gtk.Action("FileMenu", "File", None, None)
@ -140,6 +151,15 @@ class MenuExampleWindow(Gtk.ApplicationWindow):
three = Gtk.ToggleAction("ChoiceThree", "Three", None, None)
three.connect("toggled", self.on_menu_choices_toggled)
action_group.add_action(three)
ad = Gtk.Action('DisabledAction', 'Disabled Action', None, None)
ad.set_sensitive(False)
action_group.add_action(ad)
ia = Gtk.Action('InvisibleAction', 'Invisible Action', None, None)
ia.set_visible(False)
action_group.add_action(ia)
ta = Gtk.Action('TooltipAction', 'Tooltip Action', 'A tooltip', None)
action_group.add_action(ta)
action_group.add_action(Gtk.Action('IconAction', 'Icon Action', None, None))
def create_ui_manager(self):
uimanager = Gtk.UIManager()
@ -263,6 +283,7 @@ class MyApplication(Gtk.Application):
def print_dbus_data(self):
bus = dbus.SessionBus()
time.sleep(0.5)
self.data = []
self.get_actions_description(bus)
self.print_menu_start(bus)
@ -273,7 +294,9 @@ class MyApplication(Gtk.Application):
print = self.print
print('\nActions description')
self.actions_desc = d = {}
for name, data in bus.call_blocking(self.bus_name, self.object_path, 'org.gtk.Actions', 'DescribeAll', '', ()).iteritems():
adata = bus.call_blocking(self.bus_name, self.object_path, 'org.gtk.Actions', 'DescribeAll', '', ())
for name in sorted(adata):
data = adata[name]
d[name] = {'enabled':convert(data[0]), 'param type': convert(data[1]), 'state':convert(data[2])}
print ('Name:', name)
print (pformat(d[name]))
@ -282,4 +305,5 @@ class MyApplication(Gtk.Application):
Gtk.Application.do_startup(self)
app = MyApplication(application_id='com.calibre-ebook.test-gtk')
signal.signal(signal.SIGINT, signal.SIG_DFL)
sys.exit(app.run(sys.argv))

View File

@ -0,0 +1,64 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
# A implementation of the GMenuModel export of menus/actions on DBus.
# GMenuModel is pretty bad, does not support icons, for instance, so I have not
# bothered to complete it. See gtk.py for an exmaple app that creates a
# GMenuModel menu.
#
# Partial spec: https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
import dbus
from PyQt5.Qt import QObject, pyqtSignal, QTimer, Qt
from calibre.utils.dbus_service import Object, method as dbus_method, signal as dbus_signal
from calibre.gui2.dbus_export.utils import set_X_window_properties
def add_window_properties_for_menu(widget, object_path, bus):
op = unicode(object_path)
set_X_window_properties(widget.effectiveWinId(), _UNITY_OBJECT_PATH=op, _GTK_UNIQUE_BUS_NAME=unicode(bus.get_unique_name()), _GTK_MENUBAR_OBJECT_PATH=op)
class DBusMenu(QObject):
handle_event_signal = pyqtSignal(object, object, object, object)
def __init__(self, object_path, parent=None, bus=None):
QObject.__init__(self, parent)
# Unity barfs is the Event DBUS method does not return immediately, so
# handle it asynchronously
self.handle_event_signal.connect(self.handle_event, type=Qt.QueuedConnection)
self.dbus_api = DBusMenuAPI(self, object_path, bus=bus)
self.set_status = self.dbus_api.set_status
self._next_id = 0
self.action_changed_timer = t = QTimer(self)
t.setInterval(0), t.setSingleShot(True), t.timeout.connect(self.actions_changed)
self.layout_changed_timer = t = QTimer(self)
t.setInterval(0), t.setSingleShot(True), t.timeout.connect(self.layouts_changed)
self.init_maps()
@property
def object_path(self):
return self.dbus_api._object_path
class DBusMenuAPI(Object):
ACTIONS_IFACE = 'org.gtk.Actions'
def __init__(self, menu, object_path, bus=None):
if bus is None:
bus = dbus.SessionBus()
Object.__init__(self, bus, object_path)
self.status = 'normal'
self.menu = menu
self.revision = 0
dbus_method, dbus_signal