Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. [PySide6] QSystemTrayIcon bug when using Class
Forum Updated to NodeBB v4.3 + New Features

[PySide6] QSystemTrayIcon bug when using Class

Scheduled Pinned Locked Moved Solved Qt for Python
9 Posts 3 Posters 926 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    Salamafet
    wrote on last edited by
    #1

    Hi,

    I am trying to create a multiplatform system tray application.

    My code is tested on macOS 14.2 with Python 3.11.4 and PySide 6.6.1 .

    Here is my code :

    import sys
    from PySide6 import QtCore, QtWidgets, QtGui
    
    class Tray(QtWidgets.QSystemTrayIcon):
        def __init__(self):
            super().__init__()
    
            self.setIcon(QtGui.QIcon("logo.icns"))
            self.setVisible(True)
    
            menu = QtWidgets.QMenu()
    
            action = QtGui.QAction("Hello World !")
            menu.addAction(action)
    
            quit = QtGui.QAction("Quit")
            quit.triggered.connect(app.quit)
            menu.addAction(quit)
    
            self.setContextMenu(menu)
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication([])
        app.setQuitOnLastWindowClosed(False)
        app.setWindowIcon(QtGui.QIcon("logo.icns"))
    
        tray = Tray()
    
        sys.exit(app.exec())
    

    In this example, the icon in the system tray appears but nothing append when I click on it.

    If I omit the class and put the code directly in my __name__ block, I works great.

    import sys
    from PySide6 import QtCore, QtWidgets, QtGui
    
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication([])
        app.setQuitOnLastWindowClosed(False)
        app.setWindowIcon(QtGui.QIcon("logo.icns"))
    
        tray = QtWidgets.QSystemTrayIcon()
    
        tray.setIcon(QtGui.QIcon("logo.icns"))
        tray.setVisible(True)
    
        menu = QtWidgets.QMenu()
    
        action = QtGui.QAction("Hello World !")
        menu.addAction(action)
    
        quit = QtGui.QAction("Quit")
        quit.triggered.connect(app.quit)
        menu.addAction(quit)
    
        tray.setContextMenu(menu)
    
        sys.exit(app.exec())
    

    How can I use QSystemTrayIcon with a class to keep a code clean and structured ?

    1 Reply Last reply
    0
    • F Offline
      F Offline
      friedemannkleint
      wrote on last edited by
      #2

      Try keeping a reference to the menu in the class:

      self._menu = QMenu()

      ...

      JonBJ S 2 Replies Last reply
      0
      • F friedemannkleint

        Try keeping a reference to the menu in the class:

        self._menu = QMenu()

        ...

        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by
        #3

        @friedemannkleint
        You are doubtless correct (your posts usually are!) for some Python reference counting reason this is correct. (It is certainly worth the OP trying.) But why doesn't the existing line self.setContextMenu(menu) keep the required reference count to menu above 0 here anyway?

        1 Reply Last reply
        1
        • F friedemannkleint

          Try keeping a reference to the menu in the class:

          self._menu = QMenu()

          ...

          S Offline
          S Offline
          Salamafet
          wrote on last edited by
          #4

          @friedemannkleint
          Same problem with self.menu.

          JonBJ 1 Reply Last reply
          0
          • S Salamafet

            @friedemannkleint
            Same problem with self.menu.

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by
            #5

            @Salamafet
            Hmm, then as I suggested it's not that.

            In this example, the icon in the system tray appears but nothing append when I click on it.

            You see the tray icon? When you click on it you do not get to see any Quit action on it, or you see the action but nothing happens when you click on it?

            Like you I am not clear what difference the class is making. If you are saying the item appears but the quit action is not triggered try

            quit.triggered.connect(lambda: print("action triggered"))
            

            If you want to get further any QObject-derived object (most of the ones you create) can have object.destroyed.connect(lambda: print("object destroyed")) attached. Would tell you if anything unexpected is getting destroyed after you created it.

            S 1 Reply Last reply
            0
            • JonBJ JonB

              @Salamafet
              Hmm, then as I suggested it's not that.

              In this example, the icon in the system tray appears but nothing append when I click on it.

              You see the tray icon? When you click on it you do not get to see any Quit action on it, or you see the action but nothing happens when you click on it?

              Like you I am not clear what difference the class is making. If you are saying the item appears but the quit action is not triggered try

              quit.triggered.connect(lambda: print("action triggered"))
              

              If you want to get further any QObject-derived object (most of the ones you create) can have object.destroyed.connect(lambda: print("object destroyed")) attached. Would tell you if anything unexpected is getting destroyed after you created it.

              S Offline
              S Offline
              Salamafet
              wrote on last edited by
              #6

              @JonB

              I see the tray icon, but there is no dropdown menu when I click on it.
              This is like there is no action in the menu.

              The behavior is the same as if I used this code :

              import sys
              from PySide6 import QtCore, QtWidgets, QtGui
              
              if __name__ == "__main__":
                  app = QtWidgets.QApplication([])
                  app.setQuitOnLastWindowClosed(False)
                  app.setWindowIcon(QtGui.QIcon("logo.icns"))
              
                  tray = QtWidgets.QSystemTrayIcon()
              
                  tray.setIcon(QtGui.QIcon("logo.icns"))
                  tray.setVisible(True)
              
                  sys.exit(app.exec())
              
              JonBJ 1 Reply Last reply
              0
              • S Salamafet

                @JonB

                I see the tray icon, but there is no dropdown menu when I click on it.
                This is like there is no action in the menu.

                The behavior is the same as if I used this code :

                import sys
                from PySide6 import QtCore, QtWidgets, QtGui
                
                if __name__ == "__main__":
                    app = QtWidgets.QApplication([])
                    app.setQuitOnLastWindowClosed(False)
                    app.setWindowIcon(QtGui.QIcon("logo.icns"))
                
                    tray = QtWidgets.QSystemTrayIcon()
                
                    tray.setIcon(QtGui.QIcon("logo.icns"))
                    tray.setVisible(True)
                
                    sys.exit(app.exec())
                
                JonBJ Offline
                JonBJ Offline
                JonB
                wrote on last edited by JonB
                #7

                @Salamafet
                Yet you say if you move the action/addAction() code out of the class and into the __main__ area you do see it?

                Although I absolutely do not see why this should be necessary, try

                self._menu = QtWidgets.QMenu()
                self._action = QtGui.QAction("Hello World !")    # this line too
                self._menu.addAction(self._action)
                

                when inside the class?

                S 1 Reply Last reply
                1
                • JonBJ JonB

                  @Salamafet
                  Yet you say if you move the action/addAction() code out of the class and into the __main__ area you do see it?

                  Although I absolutely do not see why this should be necessary, try

                  self._menu = QtWidgets.QMenu()
                  self._action = QtGui.QAction("Hello World !")    # this line too
                  self._menu.addAction(self._action)
                  

                  when inside the class?

                  S Offline
                  S Offline
                  Salamafet
                  wrote on last edited by
                  #8

                  @JonB

                  Yet you say if you move the action/addAction() code out of the class and into the main area you do see it?

                  Yes, in this case it's works.

                  By adding self on each variable in class, it's works. I don't understand why, but it works.

                  Thanks a lot

                  JonBJ 1 Reply Last reply
                  0
                  • S Salamafet has marked this topic as solved on
                  • S Salamafet

                    @JonB

                    Yet you say if you move the action/addAction() code out of the class and into the main area you do see it?

                    Yes, in this case it's works.

                    By adding self on each variable in class, it's works. I don't understand why, but it works.

                    Thanks a lot

                    JonBJ Offline
                    JonBJ Offline
                    JonB
                    wrote on last edited by JonB
                    #9

                    @Salamafet said in [PySide6] QSystemTrayIcon bug when using Class:

                    I don't understand why, but it works.

                    Well the reason is that when Python detects nothing is refencing any Python object it destroys it --- that's the object collection the language gices you unlike plain C++.

                    The mystery is that one would have thought that self.setContextMenu(menu) would itself increment the reference count on menu and its action added to that. I don't know why it does not. I don't know whether menu = QtWidgets.QMenu(self) inside Tray() would have improved the situation. You might like to add that anyway, and then maybe see if the other 2 changes are no longer required.

                    Ah, see (the C++ explanation for) [void]QSystemTrayIcon::setContextMenu(QMenu *menu) (or PySide6 PySide6.QtWidgets.QSystemTrayIcon.setContextMenu(menu)):

                    Note: The system tray icon does not take ownership of the menu. You must ensure that it is deleted at the appropriate time by, for example, creating the menu with a suitable parent object.

                    This explains everything. Whenever it says "something does not take ownership of this QObject" that will mean the Python code will not increment a reference count. setContextMenu() leaves you in charge of the menu, which e.g. you might be using elsewhere. Consequently any QObjects documented like that will require (typically) a Python self._variable = TheObject() to keep it "alive" for the duration of the class instance, not just a local variable. Or, by the looks of it even better, pass self to the top-level object's constructor (your QMenu()) so it is fully owned by the instance.

                    1 Reply Last reply
                    2

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved