Gnome环境下点击通知后如何正确显示窗口?
解决Gnome环境下点击通知后通知中心遮挡窗口的问题
我用PyQt结合DBus实现点击通知唤起窗口的功能,核心代码如下:
def callback(*_): mainWindow.show() mainWindow.raise_() mainWindow.activateWindow() notification.addAction('default', '', callback) # 通过dbus发送通知 notification.show()
但在Gnome环境下出现异常:点击通知后,窗口虽能正常唤起,但通知中心会保持在窗口上方,不会自动收起。完整可复现代码如下:
import sys from PyQt6.QtWidgets import QApplication, QWidget, QPushButton from PyQt6.QtCore import pyqtSlot import dbus from collections import OrderedDict DBusGMainLoop = None try: from dbus.mainloop.glib import DBusGMainLoop except: print("Could not import DBusGMainLoop, is package 'python-dbus.mainloop.glib' installed?") APP_NAME = 'test' DBUS_IFACE = None NOTIFICATIONS = {} class Urgency: """freedesktop.org notification urgency levels""" LOW, NORMAL, CRITICAL = range(3) class UninitializedError(RuntimeError): """Error raised if you try to show an error before initializing""" pass def init(): """Initializes the DBus connection""" global DBUS_IFACE name = "org.freedesktop.Notifications" path = "/org/freedesktop/Notifications" interface = "org.freedesktop.Notifications" mainloop = None if DBusGMainLoop is not None: mainloop = DBusGMainLoop(set_as_default=True) bus = dbus.SessionBus(mainloop) proxy = bus.get_object(name, path) DBUS_IFACE = dbus.Interface(proxy, interface) if mainloop is not None: DBUS_IFACE.connect_to_signal('ActionInvoked', _onActionInvoked) DBUS_IFACE.connect_to_signal('NotificationClosed', _onNotificationClosed) def _onActionInvoked(nid, action): """Called when a notification action is clicked""" nid, action = int(nid), str(action) try: notification = NOTIFICATIONS[nid] except KeyError: return notification._onActionInvoked(action) def _onNotificationClosed(nid, reason): """Called when the notification is closed""" nid, reason = int(nid), int(reason) try: notification = NOTIFICATIONS[nid] except KeyError: return notification._onNotificationClosed(notification) del NOTIFICATIONS[nid] class Notification(object): """Notification object""" id = 0 timeout = -1 _onNotificationClosed = lambda *args: None def __init__(self, title, body='', icon='', timeout=-1): self.title = title self.body = body self.icon = icon self.timeout = timeout self.hints = {} self.actions = OrderedDict() self.data = {} def show(self): if DBUS_IFACE is None: raise UninitializedError("You must call 'notify.init()' before 'notify.show()'") nid = DBUS_IFACE.Notify(APP_NAME, self.id, self.icon, self.title, self.body, self._makeActionsList(), self.hints, self.timeout, ) self.id = int(nid) NOTIFICATIONS[self.id] = self return True def close(self): """Ask the notification server to close the notification""" if self.id != 0: DBUS_IFACE.CloseNotification(self.id) def onClosed(self, callback): """Set the callback called when the notification is closed""" self._onNotificationClosed = callback def addAction(self, action, label, callback, user_data=None): """Add an action to the notification.""" self.actions[action] = (label, callback, user_data) def _makeActionsList(self): """Make the actions array to send over DBus""" arr = [] for action, (label, callback, user_data) in self.actions.items(): arr.append(action) arr.append(label) return arr def _onActionInvoked(self, action): """Called when the user activates a notification action""" try: label, callback, user_data = self.actions[action] except KeyError: return if user_data is None: callback(self, action) else: callback(self, action, user_data) def window(): app = QApplication(sys.argv) widget = QWidget() button = QPushButton("Notify", widget) button.move(110,85) def button_callback(): notification = Notification("Test") def notification_callback(*_): widget.show() widget.raise_() widget.activateWindow() notification.close() notification.addAction("default", "", notification_callback) notification.show() button.clicked.connect(button_callback) widget.setGeometry(50,50,320,200) widget.setWindowTitle("My Window") widget.show() init() sys.exit(app.exec()) if __name__ == '__main__': window()
解决方案
问题根源是Gnome通知中心会抢占焦点,仅靠Qt的窗口激活方法无法触发系统自动收起通知中心。需要通过DBus调用Gnome Shell的接口手动关闭通知中心,同时优化窗口激活逻辑:
- 添加关闭Gnome通知中心的方法:
def close_gnome_notification_center(): try: bus = dbus.SessionBus() shell_proxy = bus.get_object('org.gnome.Shell', '/org/gnome/Shell') shell_interface = dbus.Interface(shell_proxy, 'org.gnome.Shell') # 调用Gnome Shell内部方法切换通知中心状态(打开则关闭) shell_interface.Eval('Main.panel.statusArea.dateMenu._toggle()') except dbus.DBusException: # 非Gnome环境忽略此异常 pass
- 修改通知回调函数,调整执行顺序:
def notification_callback(*_): # 先关闭通知中心 close_gnome_notification_center() # 确保窗口从最小化状态恢复 widget.showNormal() # 提升窗口层级并激活 widget.raise_() widget.activateWindow() notification.close()
说明
close_gnome_notification_center通过DBus调用Gnome Shell的内部API,切换通知中心的显示状态(如果当前是打开状态就会关闭)。- 使用
showNormal()替代show(),确保窗口在最小化时能正常恢复显示。 - 先关闭通知中心再激活窗口,保证窗口能获得最高显示层级,避免被遮挡。
内容的提问来源于stack exchange,提问作者Edson Passos
相关产品推荐
相关产品推荐

