Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I dynamically add actions to a QMenu while it is open on a Mac?

I have QSystemTrayIcon with a QMenu. In order to fill the menu, I need to fetch some things from the network, so I want to do that in the background.

So I have a QThread with a slot that is connected to the activated signal of the tray icon. Then the thread fetches the resources and updates the menu using another signal.

However, these updates do not show until I close and reopen the menu.

This seems to be a Mac specific problem. I ran my code on Windows, and there it updated more or less correctly. Is there any workaround?

Below is an extracted version of the problem. When the menu is opened, it will sleep 1 second in a thread and then change the menu. This change is not seen.

import sys
import time
from PySide import QtCore, QtGui

class PeerMenu(QtGui.QMenu):

    def __init__(self):
        QtGui.QMenu.__init__(self)
        self.set_peers("prestine")

    @QtCore.Slot(object)
    def set_peers(self, label):
        self.clear()

        self.addAction(QtGui.QAction(label, self))
        self.addSeparator()
        self.addAction(QtGui.QAction("Hello", self))

class GUIListener(QtCore.QObject):

    files = QtCore.Signal(object)

    def __init__(self):
        QtCore.QObject.__init__(self)
        self.counter = 0

    @QtCore.Slot()
    def check(self):
        time.sleep(1)
        self.counter += 1
        self.files.emit(str(self.counter))

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)

    icon = QtGui.QSystemTrayIcon(QtGui.QIcon('images/glyphicons-206-electricity.png'), app)

    listener = GUIListener()
    t = QtCore.QThread()
    t.start()
    listener.moveToThread(t)

    menu = PeerMenu()
    icon.activated.connect(listener.check)
    listener.files.connect(menu.set_peers)

    icon.setContextMenu(menu)

    icon.show()

    app.exec_()
like image 898
Pepijn Avatar asked Sep 30 '22 00:09

Pepijn


1 Answers

After a few hours of extensive googling I finally figured it out.

You can create a borderless window using QtGui.QMainWindow(parent=None, flags=QtCore.Qt.Popup) and then find the location of the icon with icon.geometry().center() and finally move the window there with window.move(icon_point).

There is some hackery involved in deciding how to place the window relative to the icon. Full code can be found at https://github.com/pepijndevos/gierzwaluw/blob/master/gui.py

like image 156
Pepijn Avatar answered Oct 11 '22 13:10

Pepijn