mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-11 09:13:57 -04:00
Skeleton of the DBus API for dbusmenu
This commit is contained in:
parent
4cff896ed4
commit
67ba10d3bf
@ -6,22 +6,111 @@ from __future__ import (unicode_literals, division, absolute_import,
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import dbus
|
||||
# Support for excporting Qt's MenuBars/Menus over DBUS. The API is defined in
|
||||
# dbus-menu.xml from the libdbusmenu project https://launchpad.net/libdbusmenu
|
||||
|
||||
from calibre.utils.dbus_service import Object
|
||||
import dbus
|
||||
from PyQt5.Qt import QApplication, QMenu, QIcon, QKeySequence
|
||||
|
||||
from calibre.utils.dbus_service import Object, BusName, method as dbus_method, dbus_property, signal as dbus_signal
|
||||
from calibre.gui2.dbus_export.utils import setup_for_cli_run
|
||||
|
||||
class DBusMenu(Object):
|
||||
|
||||
IFACE = 'com.canonical.dbusmenu'
|
||||
|
||||
def __init__(self, notifier, object_path, **kw):
|
||||
self.notifier = notifier
|
||||
def __init__(self, object_path, **kw):
|
||||
bus = kw.get('bus')
|
||||
if bus is None:
|
||||
bus = kw['bus'] = dbus.SessionBus()
|
||||
Object.__init__(self, bus, object_path)
|
||||
self.status = 'normal'
|
||||
|
||||
def publish_new_menu(self):
|
||||
def publish_new_menu(self, qmenu):
|
||||
self.qmenu = qmenu
|
||||
|
||||
@dbus_property(IFACE, signature='u')
|
||||
def Version(self):
|
||||
return 3 # GTK 3 uses 3, KDE 4 uses 2
|
||||
|
||||
@dbus_property(IFACE, signature='s', emits_changed_signal=True)
|
||||
def Status(self):
|
||||
return self.status
|
||||
|
||||
def set_status(self, normal=True):
|
||||
self.status = 'normal' if normal else 'notice'
|
||||
self.PropertiesChanged(self.IFACE, {'Status': self.status}, [])
|
||||
|
||||
@dbus_property(IFACE, signature='s')
|
||||
def TextDirection(self):
|
||||
return 'ltr'
|
||||
|
||||
@dbus_property(IFACE, signature='as')
|
||||
def IconThemePath(self):
|
||||
return dbus.Array(signature='s')
|
||||
|
||||
@dbus_method(IFACE, in_signature='iias', out_signature='u(ia{sv}av)')
|
||||
def GetLayout(self, parentId, recursionDepth, propertyNames):
|
||||
pass
|
||||
|
||||
@dbus_method(IFACE, in_signature='aias', out_signature='a(ia{sv})')
|
||||
def GetGroupProperties(self, ids, propertyNames):
|
||||
pass
|
||||
|
||||
@dbus_method(IFACE, in_signature='is', out_signature='v')
|
||||
def GetProperty(self, id, name):
|
||||
pass
|
||||
|
||||
@dbus_method(IFACE, in_signature='isvu', out_signature='')
|
||||
def Event(self, id, eventId, data, timestamp):
|
||||
''' This is called by the applet to notify the application an event happened on a
|
||||
menu item. type can be one of the following::
|
||||
* "clicked"
|
||||
* "hovered"
|
||||
* "opened"
|
||||
* "closed"
|
||||
Vendor specific events can be added by prefixing them with "x-<vendor>-"'''
|
||||
pass
|
||||
|
||||
@dbus_method(IFACE, in_signature='a(isvu)', out_signature='ai')
|
||||
def EventGroup(self, events):
|
||||
''' Used to pass a set of events as a single message for possibily
|
||||
several different menuitems. This is done to optimize DBus traffic.
|
||||
Should return a list of ids that are not found. events is a list of
|
||||
events in the same format as used for the Event method.'''
|
||||
return dbus.Array(signature='u')
|
||||
|
||||
@dbus_method(IFACE, in_signature='i', out_signature='b')
|
||||
def AboutToShow(self, id):
|
||||
pass
|
||||
|
||||
@dbus_method(IFACE, in_signature='ai', out_signature='aiai')
|
||||
def AboutToShowGroup(self, ids):
|
||||
pass
|
||||
|
||||
@dbus_signal(IFACE, 'a(ia{sv})a(ias)')
|
||||
def ItemsPropertiesUpdated(self, updatedProps, removedProps):
|
||||
pass
|
||||
|
||||
@dbus_signal(IFACE, 'ui')
|
||||
def LayoutUpdated(self, revision, parent):
|
||||
pass
|
||||
|
||||
@dbus_signal(IFACE, 'iu')
|
||||
def ItemActivationRequested(self, id, timestamp):
|
||||
pass
|
||||
|
||||
def test():
|
||||
setup_for_cli_run()
|
||||
app = QApplication([])
|
||||
bus = dbus.SessionBus()
|
||||
dbus_name = BusName('com.calibre-ebook.TestDBusMenu', bus=bus, do_not_queue=True)
|
||||
m = QMenu()
|
||||
m.addAction(QIcon(I('window-close.png')), 'Quit', app.quit).setShortcut(QKeySequence(QKeySequence.Quit))
|
||||
menu = DBusMenu('/Menu', bus=bus)
|
||||
menu.publish_new_menu(m)
|
||||
app.exec_()
|
||||
del dbus_name
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
|
@ -19,7 +19,7 @@ from PyQt5.Qt import (
|
||||
QApplication, QObject, pyqtSignal, Qt, QPoint, QRect, QMenu, QIcon)
|
||||
|
||||
from calibre.gui2.dbus_export.menu import DBusMenu
|
||||
from calibre.gui2.dbus_export.utils import log, qicon_to_sni_image_list
|
||||
from calibre.gui2.dbus_export.utils import log, qicon_to_sni_image_list, setup_for_cli_run
|
||||
from calibre.utils.dbus_service import Object, method as dbus_method, BusName, dbus_property, signal as dbus_signal
|
||||
|
||||
class Factory(QObject):
|
||||
@ -187,13 +187,21 @@ class StatusNotifierItemAPI(Object):
|
||||
self.title = kw.get('title', self.app_id)
|
||||
self.icon_serialization = qicon_to_sni_image_list(notifier.icon())
|
||||
Object.__init__(self, bus, '/' + self.IFACE.split('.')[-1])
|
||||
self.dbus_menu = DBusMenu(notifier, '/StatusItemMenu', **kw)
|
||||
self.dbus_menu = DBusMenu('/StatusItemMenu', **kw)
|
||||
for name, val in vars(self.__class__).iteritems():
|
||||
if getattr(val, '_dbus_is_signal', False):
|
||||
getattr(notifier, name).connect(getattr(self, name))
|
||||
|
||||
def publish_new_menu(self):
|
||||
self.dbus_menu.publish_new_menu()
|
||||
menu = self.notifier.contextMenu()
|
||||
if menu is None:
|
||||
menu = QMenu()
|
||||
if len(menu.actions()) == 0:
|
||||
menu.addAction(self.notifier.icon(), _('Show/hide %s') % self.title, self.notifier.activated.emit)
|
||||
# The menu must have at least one entry, namely the show/hide entry.
|
||||
# This is necessary as Canonical in their infinite wisdom decided to
|
||||
# force all tray icons to show their popup menus when clicked.
|
||||
self.dbus_menu.publish_new_menu(menu)
|
||||
|
||||
@dbus_property(IFACE, signature='s')
|
||||
def IconName(self):
|
||||
@ -305,13 +313,9 @@ def factory():
|
||||
return _factory
|
||||
|
||||
def test():
|
||||
import signal
|
||||
from dbus.mainloop.glib import DBusGMainLoop, threads_init
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
threads_init()
|
||||
setup_for_cli_run()
|
||||
app = QApplication([])
|
||||
app.setApplicationName('Testing SNI Interface')
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL) # quit on Ctrl-C
|
||||
tray_icon = factory().create_indicator()
|
||||
tray_icon.setToolTip('A test tooltip')
|
||||
tray_icon.setIcon(QIcon(I('debug.png')))
|
||||
|
@ -38,3 +38,10 @@ def qicon_to_sni_image_list(qicon):
|
||||
ans.append((w, h, dbus.ByteArray(data)))
|
||||
return ans
|
||||
|
||||
def setup_for_cli_run():
|
||||
import signal
|
||||
from dbus.mainloop.glib import DBusGMainLoop, threads_init
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
threads_init()
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL) # quit on Ctrl-C
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user