Reliable position to pop context menus for tray icon on left click with Wayland
-
I got from the guidelines that I should not rely on
QCursor::pos
when I pop a menu on Wayland. But how could I get a reliable position when I click a tray icon by left click? I wrote a simple script withQSystemTrayIcon
as below, but the member functiongeometry
always returns a point at (0, 0) insideon_left_click
. I also tried to overrideevent
andeventFilter
to get a position from an event, but the functions were never called.from PyQt6.QtGui import QAction, QCursor, QIcon from PyQt6.QtWidgets import QApplication, QMenu, QSystemTrayIcon class MyTray(QSystemTrayIcon): def __init__(self): super().__init__(QIcon.fromTheme('start-here')) self.activated.connect(lambda reason: self.on_left_click(reason)) def on_left_click(self, reason): if reason == QSystemTrayIcon.ActivationReason.Trigger: self.contextMenu().popup(QCursor.pos()) # not accurate on Wayland def main(): app = QApplication([]) app.setQuitOnLastWindowClosed(False) menu = QMenu() action = QAction() action.setText('Hello') menu.addAction(action) tray = MyTray() tray.setVisible(True) tray.setContextMenu(menu) app.exec() if __name__ == '__main__': main()
-
I installed
MyTray
as the filter ofQApplication
, soMyTray
is updated as follows:class MyTray(QSystemTrayIcon): pos = None def __init__(self, app): super().__init__(QIcon.fromTheme('start-here')) self.activated.connect(lambda reason: self.on_left_click(reason)) app.installEventFilter(self) def on_left_click(self, reason): if reason == QSystemTrayIcon.ActivationReason.Trigger: print('Clicked!!!') self.contextMenu().popup(self.pos if self.pos else QCursor.pos()) def eventFilter(self, _, event): print(event) return False
And I got the following events:
<PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5d80> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5d80> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5d80> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5d80> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5d80> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5ea0> Clicked!!!
It seems none of them can provide a position.
-
I installed
MyTray
as the filter ofQApplication
, soMyTray
is updated as follows:class MyTray(QSystemTrayIcon): pos = None def __init__(self, app): super().__init__(QIcon.fromTheme('start-here')) self.activated.connect(lambda reason: self.on_left_click(reason)) app.installEventFilter(self) def on_left_click(self, reason): if reason == QSystemTrayIcon.ActivationReason.Trigger: print('Clicked!!!') self.contextMenu().popup(self.pos if self.pos else QCursor.pos()) def eventFilter(self, _, event): print(event) return False
And I got the following events:
<PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5d80> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5d80> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5d80> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5d80> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5d80> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5e10> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QChildEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QEvent object at 0x7fc2ac0f5ea0> <PyQt6.QtCore.QDynamicPropertyChangeEvent object at 0x7fc2ac0f5ea0> Clicked!!!
It seems none of them can provide a position.
Hi,
I am unsure about what you are trying to achieve.
Did you take a look at the System Tray Icon example ? Does it do what you want ?
-
There is a restriction in Wayland in that the standard protocol that does allow positioning windows (see https://bugreports.qt.io/browse/QTBUG-110119 ).