mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add system tray to demo app
This commit is contained in:
parent
fce224af80
commit
1b1f1b0cc8
@ -8,7 +8,7 @@ __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
|
|
||||||
from PyQt5.Qt import (
|
from PyQt5.Qt import (
|
||||||
QApplication, QMainWindow, QVBoxLayout, Qt, QKeySequence, QAction,
|
QApplication, QMainWindow, QVBoxLayout, Qt, QKeySequence, QAction,
|
||||||
QActionGroup, QMenu)
|
QActionGroup, QMenu, QIcon)
|
||||||
|
|
||||||
from calibre.gui2.dbus_export.utils import setup_for_cli_run
|
from calibre.gui2.dbus_export.utils import setup_for_cli_run
|
||||||
from calibre.gui2.dbus_export.widgets import factory
|
from calibre.gui2.dbus_export.widgets import factory
|
||||||
@ -22,6 +22,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QMainWindow.__init__(self)
|
QMainWindow.__init__(self)
|
||||||
|
f = factory()
|
||||||
self.setMinimumWidth(400)
|
self.setMinimumWidth(400)
|
||||||
self.setWindowTitle('Demo of DBUS menu exporter and systray integration')
|
self.setWindowTitle('Demo of DBUS menu exporter and systray integration')
|
||||||
self.statusBar().showMessage(self.windowTitle())
|
self.statusBar().showMessage(self.windowTitle())
|
||||||
@ -32,6 +33,11 @@ class MainWindow(QMainWindow):
|
|||||||
m = self.menu_one = mb.addMenu('&One')
|
m = self.menu_one = mb.addMenu('&One')
|
||||||
m.aboutToShow.connect(self.about_to_show_one)
|
m.aboutToShow.connect(self.about_to_show_one)
|
||||||
s = self.style()
|
s = self.style()
|
||||||
|
self.q = q = QAction('&Quit', self)
|
||||||
|
q.setShortcut(QKeySequence.Quit)
|
||||||
|
q.triggered.connect(QApplication.quit)
|
||||||
|
self.addAction(q)
|
||||||
|
QApplication.instance().setWindowIcon(QIcon(I('debug.png')))
|
||||||
for i, icon in zip(xrange(3), map(s.standardIcon, (s.SP_DialogOkButton, s.SP_DialogCancelButton, s.SP_ArrowUp))):
|
for i, icon in zip(xrange(3), map(s.standardIcon, (s.SP_DialogOkButton, s.SP_DialogCancelButton, s.SP_ArrowUp))):
|
||||||
ac = m.addAction('One - &%d' % (i + 1))
|
ac = m.addAction('One - &%d' % (i + 1))
|
||||||
ac.setShortcut(QKeySequence(Qt.CTRL | (Qt.Key_1 + i), Qt.SHIFT | (Qt.Key_1 + i)))
|
ac.setShortcut(QKeySequence(Qt.CTRL | (Qt.Key_1 + i), Qt.SHIFT | (Qt.Key_1 + i)))
|
||||||
@ -57,6 +63,32 @@ class MainWindow(QMainWindow):
|
|||||||
ac.triggered.connect(self.action_triggered)
|
ac.triggered.connect(self.action_triggered)
|
||||||
for m in mb.findChildren(QMenu):
|
for m in mb.findChildren(QMenu):
|
||||||
m.aboutToShow.connect(self.about_to_show)
|
m.aboutToShow.connect(self.about_to_show)
|
||||||
|
self.systray = f.create_system_tray_icon(parent=self, title=self.windowTitle())
|
||||||
|
if self.systray is not None:
|
||||||
|
self.systray.activated.connect(self.tray_activated)
|
||||||
|
self.sm = m = QMenu()
|
||||||
|
m.addAction('Show/hide main window').triggered.connect(self.tray_activated)
|
||||||
|
m.addAction(q)
|
||||||
|
self.systray.setContextMenu(m)
|
||||||
|
self.update_tray_toggle_action()
|
||||||
|
print ('DBUS connection unique name:', f.bus.get_unique_name())
|
||||||
|
|
||||||
|
def update_tray_toggle_action(self):
|
||||||
|
if hasattr(self, 'sm'):
|
||||||
|
self.sm.actions()[0].setText('Hide main window' if self.isVisible() else 'Show main window')
|
||||||
|
|
||||||
|
def hideEvent(self, ev):
|
||||||
|
if not ev.spontaneous():
|
||||||
|
self.update_tray_toggle_action()
|
||||||
|
return QMainWindow.hideEvent(self, ev)
|
||||||
|
|
||||||
|
def showEvent(self, ev):
|
||||||
|
if not ev.spontaneous():
|
||||||
|
self.update_tray_toggle_action()
|
||||||
|
return QMainWindow.showEvent(self, ev)
|
||||||
|
|
||||||
|
def tray_activated(self):
|
||||||
|
self.setVisible(not self.isVisible())
|
||||||
|
|
||||||
def action_triggered(self, checked=False):
|
def action_triggered(self, checked=False):
|
||||||
ac = self.sender()
|
ac = self.sender()
|
||||||
@ -74,8 +106,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.menu_two.addAction('Action added by about to show')
|
self.menu_two.addAction('Action added by about to show')
|
||||||
|
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
f = factory()
|
app.setApplicationName('com.calibre-ebook.DBusExportDemo')
|
||||||
mw = MainWindow()
|
mw = MainWindow()
|
||||||
mw.show()
|
mw.show()
|
||||||
print ('DBUS connection unique name:', f.bus.get_unique_name())
|
|
||||||
app.exec_()
|
app.exec_()
|
||||||
|
@ -68,12 +68,12 @@ class DBusMenu(QObject):
|
|||||||
|
|
||||||
handle_event_signal = pyqtSignal(object, object, object, object)
|
handle_event_signal = pyqtSignal(object, object, object, object)
|
||||||
|
|
||||||
def __init__(self, object_path, **kw):
|
def __init__(self, object_path, parent=None, bus=None):
|
||||||
QObject.__init__(self, kw.get('parent'))
|
QObject.__init__(self, parent)
|
||||||
# Unity barfs is the Event DBUS method does not return immediately, so
|
# Unity barfs is the Event DBUS method does not return immediately, so
|
||||||
# handle it asynchronously
|
# handle it asynchronously
|
||||||
self.handle_event_signal.connect(self.handle_event, type=Qt.QueuedConnection)
|
self.handle_event_signal.connect(self.handle_event, type=Qt.QueuedConnection)
|
||||||
self.dbus_api = DBusMenuAPI(self, object_path, **kw)
|
self.dbus_api = DBusMenuAPI(self, object_path, bus=bus)
|
||||||
self.set_status = self.dbus_api.set_status
|
self.set_status = self.dbus_api.set_status
|
||||||
self._next_id = 0
|
self._next_id = 0
|
||||||
self.action_changed_timer = t = QTimer(self)
|
self.action_changed_timer = t = QTimer(self)
|
||||||
@ -82,6 +82,10 @@ class DBusMenu(QObject):
|
|||||||
t.setInterval(0), t.setSingleShot(True), t.timeout.connect(self.layouts_changed)
|
t.setInterval(0), t.setSingleShot(True), t.timeout.connect(self.layouts_changed)
|
||||||
self.init_maps()
|
self.init_maps()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def object_path(self):
|
||||||
|
return self.dbus_api._object_path
|
||||||
|
|
||||||
def init_maps(self, qmenu=None):
|
def init_maps(self, qmenu=None):
|
||||||
self.action_changes = set()
|
self.action_changes = set()
|
||||||
self.layout_changes = set()
|
self.layout_changes = set()
|
||||||
@ -241,10 +245,9 @@ class DBusMenuAPI(Object):
|
|||||||
|
|
||||||
IFACE = 'com.canonical.dbusmenu'
|
IFACE = 'com.canonical.dbusmenu'
|
||||||
|
|
||||||
def __init__(self, menu, object_path, **kw):
|
def __init__(self, menu, object_path, bus=None):
|
||||||
bus = kw.get('bus')
|
|
||||||
if bus is None:
|
if bus is None:
|
||||||
bus = kw['bus'] = dbus.SessionBus()
|
bus = dbus.SessionBus()
|
||||||
Object.__init__(self, bus, object_path)
|
Object.__init__(self, bus, object_path)
|
||||||
self.status = 'normal'
|
self.status = 'normal'
|
||||||
self.menu = menu
|
self.menu = menu
|
||||||
|
@ -16,7 +16,7 @@ import os
|
|||||||
|
|
||||||
import dbus
|
import dbus
|
||||||
from PyQt5.Qt import (
|
from PyQt5.Qt import (
|
||||||
QApplication, QObject, pyqtSignal, Qt, QPoint, QRect, QMenu, QIcon)
|
QApplication, QObject, pyqtSignal, Qt, QPoint, QRect, QMenu)
|
||||||
|
|
||||||
from calibre.gui2.dbus_export.menu import DBusMenu
|
from calibre.gui2.dbus_export.menu import DBusMenu
|
||||||
from calibre.gui2.dbus_export.utils import qicon_to_sni_image_list
|
from calibre.gui2.dbus_export.utils import qicon_to_sni_image_list
|
||||||
@ -36,7 +36,7 @@ class StatusNotifierItem(QObject):
|
|||||||
self.context_menu = None
|
self.context_menu = None
|
||||||
self.is_visible = True
|
self.is_visible = True
|
||||||
self.tool_tip = ''
|
self.tool_tip = ''
|
||||||
self._icon = QIcon()
|
self._icon = QApplication.instance().windowIcon()
|
||||||
self.show_menu.connect(self._show_menu, type=Qt.QueuedConnection)
|
self.show_menu.connect(self._show_menu, type=Qt.QueuedConnection)
|
||||||
_sni_count += 1
|
_sni_count += 1
|
||||||
kw['num'] = _sni_count
|
kw['num'] = _sni_count
|
||||||
@ -85,6 +85,8 @@ class StatusNotifierItem(QObject):
|
|||||||
def icon(self):
|
def icon(self):
|
||||||
return self._icon
|
return self._icon
|
||||||
|
|
||||||
|
_status_item_menu_count = 0
|
||||||
|
|
||||||
class StatusNotifierItemAPI(Object):
|
class StatusNotifierItemAPI(Object):
|
||||||
|
|
||||||
'See http://www.notmart.org/misc/statusnotifieritem/statusnotifieritem.html'
|
'See http://www.notmart.org/misc/statusnotifieritem/statusnotifieritem.html'
|
||||||
@ -92,6 +94,7 @@ class StatusNotifierItemAPI(Object):
|
|||||||
IFACE = 'org.kde.StatusNotifierItem'
|
IFACE = 'org.kde.StatusNotifierItem'
|
||||||
|
|
||||||
def __init__(self, notifier, **kw):
|
def __init__(self, notifier, **kw):
|
||||||
|
global _status_item_menu_count
|
||||||
self.notifier = notifier
|
self.notifier = notifier
|
||||||
bus = kw.get('bus')
|
bus = kw.get('bus')
|
||||||
if bus is None:
|
if bus is None:
|
||||||
@ -103,7 +106,8 @@ class StatusNotifierItemAPI(Object):
|
|||||||
self.title = kw.get('title') or self.app_id
|
self.title = kw.get('title') or self.app_id
|
||||||
self.icon_serialization = qicon_to_sni_image_list(notifier.icon())
|
self.icon_serialization = qicon_to_sni_image_list(notifier.icon())
|
||||||
Object.__init__(self, bus, '/' + self.IFACE.split('.')[-1])
|
Object.__init__(self, bus, '/' + self.IFACE.split('.')[-1])
|
||||||
self.dbus_menu = DBusMenu('/StatusItemMenu', **kw)
|
_status_item_menu_count += 1
|
||||||
|
self.dbus_menu = DBusMenu('/StatusItemMenu/%d' % _status_item_menu_count, bus=bus, parent=kw.get('parent'))
|
||||||
|
|
||||||
def publish_new_menu(self):
|
def publish_new_menu(self):
|
||||||
menu = self.notifier.contextMenu()
|
menu = self.notifier.contextMenu()
|
||||||
@ -166,7 +170,7 @@ class StatusNotifierItemAPI(Object):
|
|||||||
|
|
||||||
@dbus_property(IFACE, signature='o')
|
@dbus_property(IFACE, signature='o')
|
||||||
def Menu(self):
|
def Menu(self):
|
||||||
return dbus.ObjectPath(self.dbus_menu._object_path)
|
return dbus.ObjectPath(self.dbus_menu.object_path)
|
||||||
|
|
||||||
@dbus_property(IFACE, signature='i')
|
@dbus_property(IFACE, signature='i')
|
||||||
def WindowId(self):
|
def WindowId(self):
|
||||||
|
@ -8,7 +8,7 @@ __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
|
|
||||||
import time, sys
|
import time, sys
|
||||||
|
|
||||||
from PyQt5.Qt import QObject, QMenuBar, QAction, QEvent, QSystemTrayIcon
|
from PyQt5.Qt import QObject, QMenuBar, QAction, QEvent, QSystemTrayIcon, QApplication
|
||||||
|
|
||||||
from calibre.constants import iswindows, isosx
|
from calibre.constants import iswindows, isosx
|
||||||
|
|
||||||
@ -82,8 +82,9 @@ class ExportedMenuBar(QMenuBar):
|
|||||||
|
|
||||||
class Factory(QObject):
|
class Factory(QObject):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, app_id=None):
|
||||||
QObject.__init__(self)
|
QObject.__init__(self)
|
||||||
|
self.app_id = app_id or QApplication.instance().applicationName() or 'unknown_application'
|
||||||
if iswindows or isosx:
|
if iswindows or isosx:
|
||||||
self.dbus = None
|
self.dbus = None
|
||||||
else:
|
else:
|
||||||
@ -144,7 +145,7 @@ class Factory(QObject):
|
|||||||
'See http://www.notmart.org/misc/statusnotifieritem/statusnotifierwatcher.html'
|
'See http://www.notmart.org/misc/statusnotifieritem/statusnotifierwatcher.html'
|
||||||
self.status_notifier = False
|
self.status_notifier = False
|
||||||
if self.bus.name_has_owner(STATUS_NOTIFIER[0]):
|
if self.bus.name_has_owner(STATUS_NOTIFIER[0]):
|
||||||
args = STATUS_NOTIFIER + ('Get', 'ss', (STATUS_NOTIFIER[-1], 'IsStatusNotifierHostRegistered'))
|
args = STATUS_NOTIFIER[:2] + (self.dbus.PROPERTIES_IFACE, 'Get', 'ss', (STATUS_NOTIFIER[-1], 'IsStatusNotifierHostRegistered'))
|
||||||
self.status_notifier = bool(self.bus.call_blocking(*args, timeout=0.1))
|
self.status_notifier = bool(self.bus.call_blocking(*args, timeout=0.1))
|
||||||
|
|
||||||
def create_window_menubar(self, parent):
|
def create_window_menubar(self, parent):
|
||||||
@ -152,12 +153,12 @@ class Factory(QObject):
|
|||||||
return ExportedMenuBar(parent, self.menu_registrar, self.bus)
|
return ExportedMenuBar(parent, self.menu_registrar, self.bus)
|
||||||
return QMenuBar(parent)
|
return QMenuBar(parent)
|
||||||
|
|
||||||
def create_system_tray_icon(self, parent=None, title=None, app_id=None, category=None):
|
def create_system_tray_icon(self, parent=None, title=None, category=None):
|
||||||
if self.has_status_notifier:
|
if self.has_status_notifier:
|
||||||
from calibre.gui2.dbus_export.tray import StatusNotifierItem
|
from calibre.gui2.dbus_export.tray import StatusNotifierItem
|
||||||
ans = StatusNotifierItem(parent=parent, title=title, app_id=app_id, category=category)
|
ans = StatusNotifierItem(parent=parent, title=title, app_id=self.app_id, category=category)
|
||||||
self.bus.call_blocking(
|
args = STATUS_NOTIFIER + ('RegisterStatusNotifierItem', 's', (ans.dbus_api.name,))
|
||||||
self.SERVICE, self.PATH, self.IFACE, 'RegisterStatusNotifierItem', 's', (ans.dbus_api.name,), timeout=1)
|
self.bus.call_blocking(*args, timeout=1)
|
||||||
return ans
|
return ans
|
||||||
if iswindows or isosx:
|
if iswindows or isosx:
|
||||||
return QSystemTrayIcon(parent)
|
return QSystemTrayIcon(parent)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user