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. Widget deletion process on app close?
QtWS25 Last Chance

Widget deletion process on app close?

Scheduled Pinned Locked Moved Unsolved Qt for Python
13 Posts 5 Posters 1.5k 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
    swpease
    wrote on last edited by
    #1

    I have data that I want to save when the user closes the last window QMainWindow. I initially set things to save in an overridden closeEvent method, but it takes a couple of seconds to save, so I moved it into a slot connected to the lastWindowClosed signal to avoid having a frozen window.

    I don't understand how this works. When is the object actually deleted? I would've thought immediately after the closeEvent was accepted, but I seem to be able to access things (e.g. said data) in my slot.

    Further, I see that I can still access my own member variables in my inherited class after returning from app.exec_(), though inherited member variables seem to be deleted. Why?

    jsulmJ JonBJ 2 Replies Last reply
    0
    • S swpease

      I have data that I want to save when the user closes the last window QMainWindow. I initially set things to save in an overridden closeEvent method, but it takes a couple of seconds to save, so I moved it into a slot connected to the lastWindowClosed signal to avoid having a frozen window.

      I don't understand how this works. When is the object actually deleted? I would've thought immediately after the closeEvent was accepted, but I seem to be able to access things (e.g. said data) in my slot.

      Further, I see that I can still access my own member variables in my inherited class after returning from app.exec_(), though inherited member variables seem to be deleted. Why?

      jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @swpease said in Widget deletion process on app close?:

      I can still access my own member variables in my inherited class

      What inherited class? How is its life-time? Can you show your code?

      "When is the object actually deleted?" - that depends. If it does not have parent you are responsible for it's deletion. If it has a parent it is deleted when parent is deleted. Take a loop at https://doc.qt.io/qt-5/objecttrees.html

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      S 1 Reply Last reply
      2
      • S swpease

        I have data that I want to save when the user closes the last window QMainWindow. I initially set things to save in an overridden closeEvent method, but it takes a couple of seconds to save, so I moved it into a slot connected to the lastWindowClosed signal to avoid having a frozen window.

        I don't understand how this works. When is the object actually deleted? I would've thought immediately after the closeEvent was accepted, but I seem to be able to access things (e.g. said data) in my slot.

        Further, I see that I can still access my own member variables in my inherited class after returning from app.exec_(), though inherited member variables seem to be deleted. Why?

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

        @swpease

        When is the object actually deleted?

        In addition to @jsulm, there is also the Qt::WA_DeleteOnClose widget attribute (https://doc.qt.io/qt-5/qt.html#WidgetAttribute-enum), which you may or may not be using on your various windows etc.

        1 Reply Last reply
        1
        • S Offline
          S Offline
          swpease
          wrote on last edited by
          #4

          @Denni-0 Where is/are appropriate places to do that? If I put it in an overridden closeEvent(), it doesn't work, which makes sense to me. Maybe check for QCloseEvent in an overridden event()?

          1 Reply Last reply
          0
          • jsulmJ jsulm

            @swpease said in Widget deletion process on app close?:

            I can still access my own member variables in my inherited class

            What inherited class? How is its life-time? Can you show your code?

            "When is the object actually deleted?" - that depends. If it does not have parent you are responsible for it's deletion. If it has a parent it is deleted when parent is deleted. Take a loop at https://doc.qt.io/qt-5/objecttrees.html

            S Offline
            S Offline
            swpease
            wrote on last edited by
            #5

            @jsulm Here is a pared down, working equivalent:

            import json
            
            from PySide2.QtCore import Qt
            from PySide2.QtWidgets import QApplication, QMainWindow
            
            
            class MainWindow(QMainWindow):
                def __init__(self, data, data_src='data.json'):
                    super().__init__()
            
                    self.setAttribute(Qt.WA_DeleteOnClose)
                    self.data_modified = False
                    self.data_src = data_src
                    self.data = data
            
                    QApplication.instance().lastWindowClosed.connect(self.save_dict)
            
                def save_dict(self):
                    print(self.data)  # {'foo': 'bar'}
                    print(self)  # <__main__.MainWindow(0x7fdf6ac551d0) at 0x109de7200>
            
            
            if __name__ == '__main__':
            
                import sys
            
                app = QApplication(sys.argv)
            
                data_src = 'data.json'
                with open(data_src) as f:
                    data: dict = json.load(f)
            
                mainWin = MainWindow(data, data_src=data_src)
                mainWin.show()
                x = app.exec_()
            
                print(mainWin.data)  # {'foo': 'bar'}
                print(mainWin)  # RuntimeError: Internal C++ object (MainWindow) already deleted.
            
                sys.exit(x)
            

            where data.json is just {"foo": "bar"}

            In the actual program, there could be multiple instances of MainWindow, mimicking the sdi example, if that matters.

            1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #6

              Hi,

              Just a silly idea but if the saving takes that long, you can use a QSplashScreen to tell the user there's something going on. That would make it explicit that your app is not frozen.

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              S 1 Reply Last reply
              2
              • SGaistS SGaist

                Hi,

                Just a silly idea but if the saving takes that long, you can use a QSplashScreen to tell the user there's something going on. That would make it explicit that your app is not frozen.

                S Offline
                S Offline
                swpease
                wrote on last edited by
                #7

                @SGaist Thanks for the suggestion. It goes in line with the hide() suggestion. I was under the impression that it wouldn't work because I had initially tried using QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)), which didn't work. Now I'm getting confused. I have:

                def closeEvent(self, event):
                    if self.maybe_save():
                        self.write_settings()
                        # All work sometimes if `self.text_edit.document().isModified()` is True.
                
                        # Doesn't work (ever?) if `self.text_edit.document().isModified()` is False.
                        # QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
                        # QApplication.instance().processEvents()
                
                        # Both work always(?) if `self.text_edit.document().isModified()` is False.
                        # self.hide()
                        # QApplication.instance().processEvents()
                
                        # splash = QSplashScreen(QPixmap(':/images/new.png'))
                        # splash.show()
                        # QApplication.instance().processEvents()
                
                        if self.dict_modified:
                            with open(self.data_src, 'w') as f:
                                json.dump(self.data, f)
                        event.accept()
                    else:
                        event.ignore()
                

                with:

                def maybe_save(self):
                    if self.text_edit.document().isModified():
                        ret = QMessageBox.warning(self, "x",
                                "The document has been modified.\nDo you want to save your changes?",
                                QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
                
                        if ret == QMessageBox.Save:
                            return self.save()
                        elif ret == QMessageBox.Cancel:
                            return False
                
                    return True
                

                Why would the call sometimes work?

                JonBJ 1 Reply Last reply
                0
                • S swpease

                  @SGaist Thanks for the suggestion. It goes in line with the hide() suggestion. I was under the impression that it wouldn't work because I had initially tried using QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)), which didn't work. Now I'm getting confused. I have:

                  def closeEvent(self, event):
                      if self.maybe_save():
                          self.write_settings()
                          # All work sometimes if `self.text_edit.document().isModified()` is True.
                  
                          # Doesn't work (ever?) if `self.text_edit.document().isModified()` is False.
                          # QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
                          # QApplication.instance().processEvents()
                  
                          # Both work always(?) if `self.text_edit.document().isModified()` is False.
                          # self.hide()
                          # QApplication.instance().processEvents()
                  
                          # splash = QSplashScreen(QPixmap(':/images/new.png'))
                          # splash.show()
                          # QApplication.instance().processEvents()
                  
                          if self.dict_modified:
                              with open(self.data_src, 'w') as f:
                                  json.dump(self.data, f)
                          event.accept()
                      else:
                          event.ignore()
                  

                  with:

                  def maybe_save(self):
                      if self.text_edit.document().isModified():
                          ret = QMessageBox.warning(self, "x",
                                  "The document has been modified.\nDo you want to save your changes?",
                                  QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
                  
                          if ret == QMessageBox.Save:
                              return self.save()
                          elif ret == QMessageBox.Cancel:
                              return False
                  
                      return True
                  

                  Why would the call sometimes work?

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

                  @swpease said in Widget deletion process on app close?:

                  Why would the call sometimes work?

                  Please define what you mean by "(sometimes) work"? Crashes? Hangs? Does not save? Saves but does not close? ....

                  def closeEvent(self, event):
                      if self.maybe_save():
                  
                  ...
                  
                  def maybe_save(self):
                          if ret == QMessageBox.Save:
                              return self.save()
                  

                  So what does your save() method return?

                  Using a Python debugger to step through your code, or even just print() statements, is a really useful skill.

                  1 Reply Last reply
                  0
                  • S Offline
                    S Offline
                    swpease
                    wrote on last edited by
                    #9

                    hide() only works sometimes for me in the following:

                    import json
                    import time
                    
                    from PySide2.QtCore import Qt
                    from PySide2.QtWidgets import QApplication, QMainWindow, QMessageBox
                    
                    
                    class MainWindow(QMainWindow):
                        def __init__(self, data, data_src='data.json'):
                            super().__init__()
                    
                            self.setAttribute(Qt.WA_DeleteOnClose)
                            self.data_modified = False
                            self.data_src = data_src
                            self.data = data
                    
                        def closeEvent(self, event):
                            if self.maybe_save():
                    
                                # MainWindow doesn't always hide
                                self.hide()
                                QApplication.instance().processEvents()
                    
                                time.sleep(10)
                                # with open(self.data_src, 'w') as f:
                                #     json.dump(self.data, f)
                    
                                event.accept()
                            else:
                                event.ignore()
                    
                        def maybe_save(self):
                            if True:
                                ret = QMessageBox.warning(self, "x",
                                        "The document has been modified.\nDo you want to save your changes?",
                                        QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
                    
                                if ret == QMessageBox.Save:
                                    return True
                                elif ret == QMessageBox.Cancel:
                                    return False
                    
                            return True
                    
                    
                    if __name__ == '__main__':
                    
                        import sys
                    
                        app = QApplication(sys.argv)
                    
                        data_src = 'data.json'  # Mine is 21 MB; takes a couple of seconds to `dump`
                        with open(data_src) as f:
                            data: dict = json.load(f)
                    
                        mainWin = MainWindow(data, data_src=data_src)
                        mainWin.show()
                        sys.exit(app.exec_())
                    
                    1 Reply Last reply
                    0
                    • SGaistS Offline
                      SGaistS Offline
                      SGaist
                      Lifetime Qt Champion
                      wrote on last edited by
                      #10

                      You are trying to hide your window while you are already in the process do to it. That's not a good idea. Even more a bad idea is to block the thread with sleeping.

                      Don't try to hide your widget in the close event, just show the splash screen, do your saving, and then everything will close nicely with the user knowing that things are going on.

                      Interested in AI ? www.idiap.ch
                      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                      S 1 Reply Last reply
                      2
                      • SGaistS SGaist

                        You are trying to hide your window while you are already in the process do to it. That's not a good idea. Even more a bad idea is to block the thread with sleeping.

                        Don't try to hide your widget in the close event, just show the splash screen, do your saving, and then everything will close nicely with the user knowing that things are going on.

                        S Offline
                        S Offline
                        swpease
                        wrote on last edited by swpease
                        #11

                        @SGaist You mentioned blocking as a bad thing. Do you have a simple elaboration that could help me understand? From what I can determine, the QMessageBox is the source of the problem. I still have inconsistent behavior using a splash screen (it displays only sometimes), but only if the QMessageBox warning modal (blocking, right?) gets used.

                        Could this be an OS/version sort of issue?

                        Also, I'm not actually calling sleep in my code, I just plugged it in in case someone wanted to try running the code without needing to find a big JSON object to input. I was under the impression that it would behave the same as a lengthy file write.

                        JonBJ 1 Reply Last reply
                        0
                        • S swpease

                          @SGaist You mentioned blocking as a bad thing. Do you have a simple elaboration that could help me understand? From what I can determine, the QMessageBox is the source of the problem. I still have inconsistent behavior using a splash screen (it displays only sometimes), but only if the QMessageBox warning modal (blocking, right?) gets used.

                          Could this be an OS/version sort of issue?

                          Also, I'm not actually calling sleep in my code, I just plugged it in in case someone wanted to try running the code without needing to find a big JSON object to input. I was under the impression that it would behave the same as a lengthy file write.

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

                          @swpease
                          I don't see how QMesssageBox is relevant to your issue. You are just using that to ask a question. Your issue lies in the time taken to do the save when the user tells you to do so.

                          I agree with @SGaist. If you dump your idea of hiding the window while the save proceeds and just went for a QProgressDialog or maybe a QSplashScreen, or even just a Qt::WaitCursor, for the duration of the save, it seems to me you would have resolved this by now. (I'm surprised a 21MB save even takes 2 seconds, but there you are --- hmm, maybe I'm over-optimistic.)

                          1 Reply Last reply
                          0
                          • S Offline
                            S Offline
                            swpease
                            wrote on last edited by
                            #13

                            Hmm... Well, I'm not sure what's going wrong on my end, so I'll just go with my original solution. I'll just have to live with the probability that somewhere out there, functional programming and containers fans are laughing at me. Thanks for the input.

                            Jon: if you're going to criticize someone for asking for an explanation to a response, I suggest you find a new hobby.

                            1 Reply Last reply
                            0

                            • Login

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