Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

I write a class TrayIcon inherit QSystemTrayIcon, why I can't call show() directly? TrayIcon().show()



  • I write a class TrayIcon inherit QSystemTrayIcon, why I can't call show() directly after init TrayIcon? TrayIcon().show()

    # It works:
    # my_tray_icon = TrayIcon(my_widget)
    # my_tray_icon.show()
    
    # But why doesn't this work? The icon does not appear in the system tray.
    TrayIcon(my_widget).show()
    
    import sys
    
    from PySide2.QtGui import QIcon
    from PySide2.QtWidgets import QSystemTrayIcon, QAction, QMenu, QApplication, QWidget, qApp
    
    
    class TrayIcon(QSystemTrayIcon):
        def __init__(self, app):
            super().__init__()
            self.app = app
            self.setIcon(QIcon('icon.png'))
            self.activated.connect(self.icon_activated)
            self.tray_menu = QMenu(QApplication.desktop())
            self.show_action = QAction('&show')
            self.quit_action = QAction('&exit')
            self.show_action.triggered.connect(app.show)
            self.quit_action.triggered.connect(qApp.quit)
            self.tray_menu.addAction(self.show_action)
            self.tray_menu.addAction(self.quit_action)
            self.setContextMenu(self.tray_menu)
    
        def icon_activated(self, reason):
            if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick):
                self.app.show()
                # for Windows
                self.app.activateWindow()
                # for macOS
                self.app.raise_()
    
    
    def main():
        app = QApplication(sys.argv)
        app.setQuitOnLastWindowClosed(False)
        my_widget = QWidget()
    
        # It works:
        # my_tray_icon = TrayIcon(my_widget)
        # my_tray_icon.show()
    
        # But why doesn't this work?
        TrayIcon(my_widget).show()
    
        sys.exit(app.exec_())
    
    
    if __name__ == '__main__':
        main()
    


  • @ideaplus said in I write a class TrayIcon inherit QSystemTrayIcon, why I can't call show() directly? TrayIcon().show():

    But why doesn't this work? The icon does not appear in the system tray.

    TrayIcon(my_widget).show()

    Because you are not holding a Python reference on the resulting TrayIcon. So Python releases the reference immediately. I'm not sure, but you might find the following behaves similarly (losing the icon):

        my_tray_icon = TrayIcon(my_widget)
        my_tray_icon.show()
        my_tray_icon = None
    


  • @ideaplus said in I write a class TrayIcon inherit QSystemTrayIcon, why I can't call show() directly? TrayIcon().show():

    But why doesn't this work? The icon does not appear in the system tray.

    TrayIcon(my_widget).show()

    Because you are not holding a Python reference on the resulting TrayIcon. So Python releases the reference immediately. I'm not sure, but you might find the following behaves similarly (losing the icon):

        my_tray_icon = TrayIcon(my_widget)
        my_tray_icon.show()
        my_tray_icon = None
    


  • @JonB
    Maybe this is a feature of pyqt. Thanks for your reply.

    from PySide2.QtWidgets import QErrorMessage, QApplication
    
    app = QApplication([])
    
    error_dialog = QErrorMessage()
    error_dialog.showMessage('Oh no!')
    
    QErrorMessage().showMessage('Oh no!')
    
    app.exec_()
    
    class Test:
        def __init__(self):
            self.x = 1
    
        def print(self):
            print(self.x)
    
    
    Test().print()
    


  • from PySide2.QtWidgets import QApplication, QMessageBox
    
    app = QApplication([])
    
    QMessageBox().critical(None, 'Error', "A Error A Error A Error", buttons=QMessageBox.Ok)
    
    app.exec_()
    
    

  • Lifetime Qt Champion

    Hi,

    Can you show the exact code you are using with your custom tray icon class ?

    @JonB is more than likely correct with the object getting garbage collected.



  • @ideaplus
    Not sure what you are proving with your QMessageBox() example, the original was QSystemTrayIcon, QMessageBox() may work differently. In any case, your QErrorMessage() example is assigned to a variable whose scope persists, and your QMessageBox().critical() blocks until the user dismisses it, so in both cases they will not be collected to stop it working, and I would expect to work under PyQt as well as PySide2.

    I can't comment on PySide2 behaviour, may differ from PyQt in this respect. I do know that in PyQt I came a similar cropper when I created a modeless QDialog which had no parent (i.e. application-modeless). I found that if I did not keep a global reference to the modeless dialog --- even though I never actually used it anywhere --- then I simply would not get to see it, as it was being deleted/garbage collected immediately after creation!


  • Banned

    Okay @ideaplus you have issues in your code which is why it does not work... part of those issues are the confusion of what object you are dealing with in TrayIcon because app actually does not actually equal self.app and yet it is -- but due to this you can get rather unexpected results. It is always best to not do things kind of loosey goosey within your programs always be straight-forward and explicit.

    For instance using super( ) is super bad as it introduces 3 potential uncommon issues while not using super( ) means you only have 1 potential rare issue. I suggest you stop and think before you follow that other guy out of the plane without putting on a parachute even if he did not.

    Further sys.exit(app.exec_( )) this is PyQt4 and this app.exec( ) is PyQt5

    I have created a sort of cleaned up functional version of your program that approaches what you were doing a simpler and more straight forward manner (its not high quality but it is better and it works) -- no cramming as many commands as one can on a single line -- that kind of coding went out when they got rid of that limitation a very loooong time ago as it was a bad idea then but they did not have an option -- now it just makes no sense to do at all you save absolutely nothing and just make your program unnecessarily complex and open the door to bugs due to not quite handling it correctly

    USE_PYSIDE2 = True
    if USE_PYSIDE2:
        from PySide2.QtCore    import Slot
        from PySide2.QtGui     import QIcon
        from PySide2.QtWidgets import QApplication, QWidget
        from PySide2.QtWidgets import QSystemTrayIcon, QAction, QMenu
    else:
        from PyQt5.QtCore    import pyqtSlot as Slot
        from PyQt5.QtGui     import QIcon
        from PyQt5.QtWidgets import QApplication, QWidget
        from PyQt5.QtWidgets import QSystemTrayIcon, QAction, QMenu
    
    from sys import exit as sysExit
    
    class TrayIcon(QSystemTrayIcon):
        def __init__(self, parent):
            QSystemTrayIcon.__init__(self)
            self.Parent = parent
    #        self.Parent.hide()
    
            self.setIcon(QIcon('icon.png'))
            self.activated.connect(self.icon_activated)
    
            self.tray_menu = QMenu(QApplication.desktop())
            self.show_action = QAction('&show')
            self.show_action.triggered.connect(self.ShowWdgt)
    
            self.quit_action = QAction('&exit')
            self.quit_action.triggered.connect(self.CloseApp)
    
            self.tray_menu.addAction(self.show_action)
            self.tray_menu.addAction(self.quit_action)
    
            self.setContextMenu(self.tray_menu)
    
        @Slot()
        def ShowWdgt(self):
            print('Show Widget')
            self.Parent.showNormal()
    
        @Slot()
        def CloseApp(self):
            print('Close App')
            sysExit()
    
        def icon_activated(self, reason):
            if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick):
                print('Activating Tray Icon')
                self.Parent.showMaximized()
    
    class Main(QWidget):
        def __init__(self):
            QWidget.__init__(self)
            self.setWindowTitle('Exterior')
            self.hide()
    
            self.Tray = TrayIcon(self)
            self.Tray.show()
    
    if __name__ == '__main__':
        MainEventThread = QApplication([])
    #    MainEventThread.setQuitOnLastWindowClosed(False)
    
        MainApp = Main()
        MainApp.showMinimized()
    
        MainEventThread.exec()
    


  • @Denni-0
    @ideaplus is asking why the following works:

        # It works:
        my_tray_icon = TrayIcon(my_widget)
        my_tray_icon.show()
    

    while the following does not:

        # But why doesn't this work?
        TrayIcon(my_widget).show()
    

    That's all he's asking.


  • Lifetime Qt Champion

    And as @JonB answered, in the second version, there's nothing holding any reference to the underlying object hence it gets garbage collected and destroyed before being even shown.


  • Banned

    @JonB I answered that as well -- by stating that cramming too much into a single line of code rather than doing it simple and explicit will almost always cause a programmer to run into issues sooner or later -- so not only did I say why it did not work -- I showed them how to do it correctly. Further I was not even sure what you were talking about as it was rather confusing to me and I am sure the OP struggled with it even more

    Sure you can dig deeper into that convoluted mess and pick it apart to figure out how its broken exactly or you can glance at it and go -- too much garbage in a single line of code lets make it simpler so we know exactly what is being done.

    You have your method of teaching and and I have mine -- I think mine is better as do my students.

    And actually in neither of them is the code actually holding onto a reference to the underlying objects as self. is not used in either case -- granted in the second one it ignores the returning object so not that it does not have a reference it is more that the reference is never retrieved.

    Oh and TrayIcon(my_widget).show() does work -- it does exactly what it is told to do it is the expectations that were in error



  • @Denni-0 said in I write a class TrayIcon inherit QSystemTrayIcon, why I can't call show() directly? TrayIcon().show():

    And actually in neither of them is the code actually holding onto a reference to the underlying objects as self. is not used in either case

    That's not how referencing works in Python, or at least not as it applies to this case.

    def main():
        # It works:
        my_tray_icon = TrayIcon(my_widget)
        my_tray_icon.show()
    
        sys.exit(app.exec_())
    

    my_tray_icon is only a local variable to a function, so nothing to do with self. However, its scope extends to the end of the function. Even though it is not referenced again after the .show(), its reference persists until the function exits, and will not be garbage collected until then. Since it is still in scope across the app.exec_(), that is why it works.


  • Banned

    @JonB I know how functional, module, and program scopes work and as I stated neither is really holding onto a reference to the underlying objects BECAUSE the reference being discussed here is Class scope not Function scope and that only applies to that one case the other case -- the one that did not work -- does not even create a functional scope -- so instead of trying to educate me on this why did you not try educate the OP on this in a clear non-confusing manner -- for as I also stated your original reply was extremely confusing. Further not a single one of my statements in my example was wrong nor was what I rendered in anyway incorrect. I gave a complete full straight forward non-confusing answer to the problem. So if you are done trying to prove some non-existing point we can just leave this be.



  • @Denni-0
    Thanks for your comment.

    I just wanted the OP to know that what you wrote about "in neither of them is the code actually holding onto a reference to the underlying objects as self. is not used in either case" is incorrect, in case he cares even if you do not.

    I take your comments about the lack of clarity/confusion in my posts compared to yours, and will try to learn.

    All the best for New Year :)


  • Banned

    @JonB why do you persist? As I stated and clarified my statement was not incorrect based on the way it was being used it was completely correct -- if you pull it out of context and confuse the issue then yes it was wrong -- but that is not what I was doing.



  • @Denni-0
    I persist because you persist :)
    Let's move on now, as I said I wish you all the best for 2020!


  • Banned

    @JonB so if I were to post something more you would have to post something more ;-)



  • @Denni-0
    It would probably depend on how much I couldn't live with what you posted, but on this occasion/thread I will try to be good and just say nothing ;-)


  • Banned

    @JonB so if I wanted to just keep messing with all I need to do is keep posting something meaningless like this ;-)


Log in to reply