mingxiao
mingxiao

Reputation: 1802

Simulate user clicking in QSystemTrayIcon

Even through the activated slot is being executed, the menu is still not showing. I traced through manually clicking the tray icon and the simulated click, and its going through the same execution logic.

Currently I have

class MyClass(QObject):
   def __init__():
       self._testSignal.connect(self._test_show)
       self.myTrayIcon.activated.connect(lambda reason: self._update_menu_and_show(reason))

   def show():
       self._testSignal.emit()

   @pyqtSlot()
   def _test_show():
       self._trayIcon.activated.emit(QtWidgets.QSystemTrayIcon.Trigger)

   @QtCore.pyqtSlot()
   def _update_menu_and_show(reason):
       if reason in (QtWidgets.QSystemTrayIcon.Trigger):
        mySystemTrayIcon._update_menu()

...
class MySystemTrayIcon(QSystemTrayIcon):

   def _update_menu(self):
      # logic to populate menu
      self.setContextMenu(menu)
...
MyClass().show()

Upvotes: 1

Views: 630

Answers (1)

mingxiao
mingxiao

Reputation: 1802

Here is how I made the context menu associated with the tray icon pop up

class MyClass(QObject):
   def __init__():
       self._testSignal.connect(self._test_show)
       self.myTrayIcon.activated.connect(lambda reason: self._update_menu_and_show(reason))

   def show():
       self._testSignal.emit()

   @pyqtSlot()
   def _test_show():
       self._trayIcon.activated.emit(QSystemTrayIcon.Context)

   @QtCore.pyqtSlot()
   def _update_menu_and_show(reason):
       if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.Context):
           mySystemTrayIcon._update_menu()
           # Trigger means user initiated, Context used for simulated
           # if simulated seems like we have to tell the window to explicitly show

           if reason == QSystemTrayIcon.Context:
               mySystemTrayIcon.contextMenu().setWindowFlags(QtCore.Qt.WindowStaysOnTopHint|QtCore.Qt.FramelessWindowHint)
               pos = mySystemTrayIcon.geometry().bottomLeft()
               mySystemTrayIcon.contextMenu().move(pos)
               mySystemTrayIcon.contextMenu().show()

...
class MySystemTrayIcon(QSystemTrayIcon):

   def _update_menu(self):
      # logic to populate menu
      self.setContextMenu(menu)
...
MyClass().show()

It seems you have to set the WindowStaysOnTopHint on the context menu so that it will appear. This solution is specific to mac since it assumes the taskbar is on the top.

One side effect is that the context menu is always on top, even if the user clicks somewhere else. I placed an event filter on the context menu, the only useful event that it registered was QEvent.Leave

Upvotes: 1

Related Questions