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?
Forum Updated to NodeBB v4.3 + New Features

Widget deletion process on app close?

Scheduled Pinned Locked Moved Unsolved Qt for Python
13 Posts 5 Posters 1.8k Views 3 Watching
  • 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 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