mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Fix system tray icons not working in Ubuntu
This commit is contained in:
parent
1b1f1b0cc8
commit
7b5bc38d8b
@ -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, QIcon)
|
QActionGroup, QMenu, QPushButton, QWidget)
|
||||||
|
|
||||||
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
|
||||||
@ -26,19 +26,20 @@ class MainWindow(QMainWindow):
|
|||||||
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())
|
||||||
w = self.centralWidget()
|
w = QWidget(self)
|
||||||
self.l = QVBoxLayout(w)
|
self.setCentralWidget(w)
|
||||||
|
self.l = l = QVBoxLayout(w)
|
||||||
mb = f.create_window_menubar(self)
|
mb = f.create_window_menubar(self)
|
||||||
self.setMenuBar(mb)
|
self.setMenuBar(mb)
|
||||||
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)
|
self.q = q = QAction('&Quit', self)
|
||||||
q.setShortcut(QKeySequence.Quit)
|
q.setShortcut(QKeySequence.Quit), q.setIcon(s.standardIcon(s.SP_DialogCancelButton))
|
||||||
q.triggered.connect(QApplication.quit)
|
q.triggered.connect(QApplication.quit)
|
||||||
self.addAction(q)
|
self.addAction(q)
|
||||||
QApplication.instance().setWindowIcon(QIcon(I('debug.png')))
|
QApplication.instance().setWindowIcon(s.standardIcon(s.SP_ComputerIcon))
|
||||||
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_DialogHelpButton, 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)))
|
||||||
ac.setIcon(icon)
|
ac.setIcon(icon)
|
||||||
@ -71,8 +72,20 @@ class MainWindow(QMainWindow):
|
|||||||
m.addAction(q)
|
m.addAction(q)
|
||||||
self.systray.setContextMenu(m)
|
self.systray.setContextMenu(m)
|
||||||
self.update_tray_toggle_action()
|
self.update_tray_toggle_action()
|
||||||
|
self.cib = b = QPushButton('Change system tray icon')
|
||||||
|
l.addWidget(b), b.clicked.connect(self.change_icon)
|
||||||
|
self.hib = b = QPushButton('Show/Hide system tray icon')
|
||||||
|
l.addWidget(b), b.clicked.connect(self.systray.toggle)
|
||||||
print ('DBUS connection unique name:', f.bus.get_unique_name())
|
print ('DBUS connection unique name:', f.bus.get_unique_name())
|
||||||
|
|
||||||
|
def change_icon(self):
|
||||||
|
import random
|
||||||
|
s = self.style()
|
||||||
|
num = s.SP_ComputerIcon
|
||||||
|
while num == s.SP_ComputerIcon:
|
||||||
|
num = random.choice(range(20))
|
||||||
|
self.systray.setIcon(self.style().standardIcon(num))
|
||||||
|
|
||||||
def update_tray_toggle_action(self):
|
def update_tray_toggle_action(self):
|
||||||
if hasattr(self, 'sm'):
|
if hasattr(self, 'sm'):
|
||||||
self.sm.actions()[0].setText('Hide main window' if self.isVisible() else 'Show main window')
|
self.sm.actions()[0].setText('Hide main window' if self.isVisible() else 'Show main window')
|
||||||
|
@ -19,8 +19,9 @@ from PyQt5.Qt import (
|
|||||||
QApplication, QObject, pyqtSignal, Qt, QPoint, QRect, QMenu)
|
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 icon_cache
|
||||||
from calibre.utils.dbus_service import Object, method as dbus_method, BusName, dbus_property, signal as dbus_signal
|
from calibre.utils.dbus_service import (
|
||||||
|
Object, method as dbus_method, BusName, dbus_property, signal as dbus_signal)
|
||||||
|
|
||||||
_sni_count = 0
|
_sni_count = 0
|
||||||
|
|
||||||
@ -61,6 +62,9 @@ class StatusNotifierItem(QObject):
|
|||||||
def hide(self):
|
def hide(self):
|
||||||
self.setVisible(False)
|
self.setVisible(False)
|
||||||
|
|
||||||
|
def toggle(self):
|
||||||
|
self.setVisible(not self.isVisible())
|
||||||
|
|
||||||
def contextMenu(self):
|
def contextMenu(self):
|
||||||
return self.context_menu
|
return self.context_menu
|
||||||
|
|
||||||
@ -104,7 +108,6 @@ class StatusNotifierItemAPI(Object):
|
|||||||
self.app_id = kw.get('app_id') or QApplication.instance().applicationName() or 'unknown_application'
|
self.app_id = kw.get('app_id') or QApplication.instance().applicationName() or 'unknown_application'
|
||||||
self.category = kw.get('category') or 'ApplicationStatus'
|
self.category = kw.get('category') or 'ApplicationStatus'
|
||||||
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())
|
|
||||||
Object.__init__(self, bus, '/' + self.IFACE.split('.')[-1])
|
Object.__init__(self, bus, '/' + self.IFACE.split('.')[-1])
|
||||||
_status_item_menu_count += 1
|
_status_item_menu_count += 1
|
||||||
self.dbus_menu = DBusMenu('/StatusItemMenu/%d' % _status_item_menu_count, bus=bus, parent=kw.get('parent'))
|
self.dbus_menu = DBusMenu('/StatusItemMenu/%d' % _status_item_menu_count, bus=bus, parent=kw.get('parent'))
|
||||||
@ -122,15 +125,15 @@ class StatusNotifierItemAPI(Object):
|
|||||||
|
|
||||||
@dbus_property(IFACE, signature='s')
|
@dbus_property(IFACE, signature='s')
|
||||||
def IconName(self):
|
def IconName(self):
|
||||||
return ''
|
return icon_cache().name_for_icon(self.notifier.icon())
|
||||||
|
|
||||||
@dbus_property(IFACE, signature='s')
|
@dbus_property(IFACE, signature='s')
|
||||||
def IconThemePath(self):
|
def IconThemePath(self):
|
||||||
return ''
|
return icon_cache().icon_theme_path
|
||||||
|
|
||||||
@dbus_property(IFACE, signature='a(iiay)')
|
@dbus_property(IFACE, signature='a(iiay)')
|
||||||
def IconPixmap(self):
|
def IconPixmap(self):
|
||||||
return self.icon_serialization
|
return dbus.Array(signature='(iiay)')
|
||||||
|
|
||||||
@dbus_property(IFACE, signature='s')
|
@dbus_property(IFACE, signature='s')
|
||||||
def OverlayIconName(self):
|
def OverlayIconName(self):
|
||||||
@ -203,7 +206,7 @@ class StatusNotifierItemAPI(Object):
|
|||||||
|
|
||||||
@dbus_signal(IFACE, '')
|
@dbus_signal(IFACE, '')
|
||||||
def NewIcon(self):
|
def NewIcon(self):
|
||||||
self.icon_serialization = qicon_to_sni_image_list(self.notifier.icon())
|
pass
|
||||||
|
|
||||||
@dbus_signal(IFACE, '')
|
@dbus_signal(IFACE, '')
|
||||||
def NewAttentionIcon(self):
|
def NewAttentionIcon(self):
|
||||||
|
@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import sys, array, socket, re
|
import sys, array, re, os, errno
|
||||||
|
|
||||||
import dbus
|
import dbus
|
||||||
|
|
||||||
@ -17,9 +17,57 @@ def log(*args, **kw):
|
|||||||
print('DBusExport:', *args, **kw)
|
print('DBusExport:', *args, **kw)
|
||||||
kw['file'].flush()
|
kw['file'].flush()
|
||||||
|
|
||||||
|
from calibre.ptempfile import PersistentTemporaryDirectory
|
||||||
|
|
||||||
|
class IconCache(object):
|
||||||
|
|
||||||
|
# Avoiding sending status notifier icon data over DBus, makes dbus-monitor
|
||||||
|
# easier to read. Also Canonical's StatusNotifier implementation cannot
|
||||||
|
# handle icon data over DBus, so we have to do this anyway.
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.icon_theme_path = os.path.join(PersistentTemporaryDirectory(prefix='dbus-export-icons-'), 'icons')
|
||||||
|
self.theme_dir = os.path.join(self.icon_theme_path, 'hicolor')
|
||||||
|
os.makedirs(self.theme_dir)
|
||||||
|
self.cached = set()
|
||||||
|
|
||||||
|
def name_for_icon(self, qicon):
|
||||||
|
if qicon.isNull():
|
||||||
|
return ''
|
||||||
|
key = qicon.cacheKey()
|
||||||
|
ans = 'dbus-icon-cache-%d' % key
|
||||||
|
if key not in self.cached:
|
||||||
|
self.write_icon(qicon, ans)
|
||||||
|
self.cached.add(key)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def write_icon(self, qicon, name):
|
||||||
|
sizes = qicon.availableSizes() or [QSize(x, x) for x in (16, 32, 64, 128, 256)]
|
||||||
|
for size in sizes:
|
||||||
|
sdir = os.path.join(self.theme_dir, '%dx%d' % (size.width(), size.height()), 'apps')
|
||||||
|
try:
|
||||||
|
os.makedirs(sdir)
|
||||||
|
except EnvironmentError as err:
|
||||||
|
if err.errno != errno.EEXIST:
|
||||||
|
raise
|
||||||
|
fname = os.path.join(sdir, '%s.png' % name)
|
||||||
|
qicon.pixmap(size).save(fname, 'PNG')
|
||||||
|
# Touch the theme path: GTK icon loading system checks the mtime of the
|
||||||
|
# dir to decide whether it should look for new icons in the theme dir.
|
||||||
|
os.utime(self.icon_theme_path, None)
|
||||||
|
|
||||||
|
_icon_cache = None
|
||||||
|
|
||||||
|
def icon_cache():
|
||||||
|
global _icon_cache
|
||||||
|
if _icon_cache is None:
|
||||||
|
_icon_cache = IconCache()
|
||||||
|
return _icon_cache
|
||||||
|
|
||||||
|
|
||||||
def qicon_to_sni_image_list(qicon):
|
def qicon_to_sni_image_list(qicon):
|
||||||
'See http://www.notmart.org/misc/statusnotifieritem/icons.html'
|
'See http://www.notmart.org/misc/statusnotifieritem/icons.html'
|
||||||
|
import socket
|
||||||
ans = dbus.Array(signature='(iiay)')
|
ans = dbus.Array(signature='(iiay)')
|
||||||
if not qicon.isNull():
|
if not qicon.isNull():
|
||||||
sizes = qicon.availableSizes() or (QSize(x, x) for x in (32, 64, 128, 256))
|
sizes = qicon.availableSizes() or (QSize(x, x) for x in (32, 64, 128, 256))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user