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. How can QUiLoader load ui to "self" and trigger closeEvent
Forum Updated to NodeBB v4.3 + New Features

How can QUiLoader load ui to "self" and trigger closeEvent

Scheduled Pinned Locked Moved Unsolved Qt for Python
15 Posts 4 Posters 3.3k 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
    sylvalas
    wrote on 24 Nov 2021, 13:26 last edited by
    #1

    This is how it is used for most ppl I think:

    class Mainwin():#inherit QMainWindow or not wont make any difference. The right logic here is not to I think.
        def __init__(self):
            super().__init__()
            self.LoadUI()
            self.ui.show()
        def LoadUI(self):
                loader=QUiLoader()
                uifile=QFile('youruifile.ui')
                uifile.open(QFile.ReadOnly)
                self.ui = loader.load(uifile,self)
                uifile.close()    
    
    

    Is there a way to use QUiLoader to load ui to "self"?
    so I can access widgets by self.widget instead of self.ui.widget.

    Also, since it is not loading ui to self, the class where I load the ui is just a shell, defining closeEvent in it won't work.

    Since self.ui is the real QMainWindow so I tried override the closeEvent of the class but still it won't work.

    def closeEvent(self, event:QCloseEvent) -> None:
        print('closed')
    QWidget.closeEvent=closeEvent
    QMainWindow.closeEvent=closeEvent
    
    J C 2 Replies Last reply 24 Nov 2021, 13:33
    1
    • S sylvalas
      24 Nov 2021, 13:26

      This is how it is used for most ppl I think:

      class Mainwin():#inherit QMainWindow or not wont make any difference. The right logic here is not to I think.
          def __init__(self):
              super().__init__()
              self.LoadUI()
              self.ui.show()
          def LoadUI(self):
                  loader=QUiLoader()
                  uifile=QFile('youruifile.ui')
                  uifile.open(QFile.ReadOnly)
                  self.ui = loader.load(uifile,self)
                  uifile.close()    
      
      

      Is there a way to use QUiLoader to load ui to "self"?
      so I can access widgets by self.widget instead of self.ui.widget.

      Also, since it is not loading ui to self, the class where I load the ui is just a shell, defining closeEvent in it won't work.

      Since self.ui is the real QMainWindow so I tried override the closeEvent of the class but still it won't work.

      def closeEvent(self, event:QCloseEvent) -> None:
          print('closed')
      QWidget.closeEvent=closeEvent
      QMainWindow.closeEvent=closeEvent
      
      J Offline
      J Offline
      jsulm
      Lifetime Qt Champion
      wrote on 24 Nov 2021, 13:33 last edited by
      #2

      @sylvalas said in How can QUiLoader load ui to "self" and trigger closeEvent:

      Is there a way to use QUiLoader to load ui to "self"?

      Why would you want to do so?
      What is the problem using self.ui? ui is a member of your class (so "inside" self).

      "defining closeEvent in it won't work" - subclass QMainWindow and override closeEvent().
      See https://stackoverflow.com/questions/12365202/how-do-i-catch-a-pyqt-closeevent-and-minimize-the-dialog-instead-of-exiting

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

      1 Reply Last reply
      0
      • S Offline
        S Offline
        sylvalas
        wrote on 24 Nov 2021, 13:44 last edited by sylvalas
        #3

        @jsulm
        I changed from pyqt5 to pyside6 I dont wanna go through it all and rewrite those lol but that's not the biggest problem.

        The biggest problem is closeEvent, with pyside, the window you close is not actually self but self.ui so it somehow just won't trigger the closeEvent if you define it in the class. That was why I tried to override the method of the QMainWindow class, cuz that's what QUiLoader() returns

        J J 2 Replies Last reply 24 Nov 2021, 13:54
        0
        • S sylvalas
          24 Nov 2021, 13:44

          @jsulm
          I changed from pyqt5 to pyside6 I dont wanna go through it all and rewrite those lol but that's not the biggest problem.

          The biggest problem is closeEvent, with pyside, the window you close is not actually self but self.ui so it somehow just won't trigger the closeEvent if you define it in the class. That was why I tried to override the method of the QMainWindow class, cuz that's what QUiLoader() returns

          J Offline
          J Offline
          jsulm
          Lifetime Qt Champion
          wrote on 24 Nov 2021, 13:54 last edited by
          #4

          @sylvalas said in How can QUiLoader load ui to "self" and trigger closeEvent:

          the window you close is not actually self but self.ui

          No. If Mainwin is a QMainWindow subclass then you can override closeEvent() like any other.

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

          S 1 Reply Last reply 24 Nov 2021, 14:09
          0
          • J jsulm
            24 Nov 2021, 13:54

            @sylvalas said in How can QUiLoader load ui to "self" and trigger closeEvent:

            the window you close is not actually self but self.ui

            No. If Mainwin is a QMainWindow subclass then you can override closeEvent() like any other.

            S Offline
            S Offline
            sylvalas
            wrote on 24 Nov 2021, 14:09 last edited by
            #5

            @jsulm
            I can't upload files so if you create a ui file and try this:

            from PySide6.QtWidgets import QApplication
            from PySide6.QtWidgets import QMainWindow
            from PySide6.QtUiTools import QUiLoader
            from PySide6.QtCore import QFile
            
            import sys
            
            class MainThread(QMainWindow):
                def __init__(self):
                    super().__init__()
                    self.LoadUI()
            # This gives you a blank window if you close this one, it'll trigger the closeEvent
                    self.show()
            #This is the real window but won't trigger closeEvent
                    self.ui.show()
            
                def LoadUI(self):
                    loader=QUiLoader()
                    uifile=QFile('testui.ui')
                    uifile.open(QFile.ReadOnly)
                    self.ui = loader.load(uifile,self)
                    uifile.close()
                
                def closeEvent(self, event):
                    super(MainThread, self).closeEvent(event)
                    print('closed')
                
            
            if __name__== '__main__':
                app = QApplication(sys.argv)
                mainthread=MainThread()
                sys.exit(app.exec_())
            
            1 Reply Last reply
            0
            • S sylvalas
              24 Nov 2021, 13:44

              @jsulm
              I changed from pyqt5 to pyside6 I dont wanna go through it all and rewrite those lol but that's not the biggest problem.

              The biggest problem is closeEvent, with pyside, the window you close is not actually self but self.ui so it somehow just won't trigger the closeEvent if you define it in the class. That was why I tried to override the method of the QMainWindow class, cuz that's what QUiLoader() returns

              J Offline
              J Offline
              JonB
              wrote on 24 Nov 2021, 15:31 last edited by JonB
              #6

              @sylvalas said in How can QUiLoader load ui to "self" and trigger closeEvent:

              I changed from pyqt5 to pyside6 I dont wanna go through it all and rewrite those lol but that's not the biggest problem.

              This is the problem area. Unfortunately PyQt and PySide use different ways to implement loading UI from file, and that is why you are having trouble with self.ui.

              If you say self.ui.show() is what "shows the real window" then that means self.ui is your QMainWindow. And so your def closeEvent(self, event): override needs to be in the class of the self.ui, not in your class MainThread(QMainWindow).

              I notice https://doc.qt.io/qt-5/quiloader.html says:

              In addition, you can customize or create your own user interface by deriving your own loader class.

              I don't know if that is the clue to what you are supposed to do to achieve what you want.

              Before you spend too long on this. Are you wedded to using QUiLoader on a .ui file at runtime? When I did Python I preferred to use the uic to generate Python code from .ui files, just like when using C++. It gives you more design/development-time support for your widgets. It is true, however, that the uic must be re-run every time you alter the .ui file. I don't know whether Creator now recognises and supports this if using Python/PySide, I think it might now. If you chose that you have a Python class where you can override methods like in C++.

              This is all covered in Using .ui files from Designer or QtCreator with QUiLoader and pyside6-uic. I like the Option A: Generating a Python class there.

              1 Reply Last reply
              2
              • S Offline
                S Offline
                sylvalas
                wrote on 29 Nov 2021, 19:00 last edited by sylvalas
                #7

                @JonB
                Thank you.
                Actually, with pyqt I used to use "from PyQt5 import uic" and "self.ui=loadUi('GUI.ui',self)" that way it is automatically loaded to self.

                Yeah i might have to go back to pyqt again lol

                J 1 Reply Last reply 29 Nov 2021, 19:20
                0
                • S sylvalas
                  29 Nov 2021, 19:00

                  @JonB
                  Thank you.
                  Actually, with pyqt I used to use "from PyQt5 import uic" and "self.ui=loadUi('GUI.ui',self)" that way it is automatically loaded to self.

                  Yeah i might have to go back to pyqt again lol

                  J Offline
                  J Offline
                  JonB
                  wrote on 29 Nov 2021, 19:20 last edited by JonB
                  #8

                  @sylvalas
                  That's not the uic I mean. You are talking about a PyQt class of that name, and loadUi("file.ui") at runtime. I was talking about the uic "pre-processor command" in PySide, I think it's pyuic in PyQt5. Which takes a different approach, no runtime .ui file, instead it produces Python source code from it when you develop, and you get a dedicated class for your .ui files.

                  1 Reply Last reply
                  1
                  • C Offline
                    C Offline
                    changyuheng
                    wrote on 6 May 2022, 12:50 last edited by changyuheng 5 Jun 2022, 13:01
                    #9

                    I totally get the OP as I encountered the same situation. His question is not directly answered here. I get there are 2 ways to inflate .ui files, pre-compiled to a python file using the uic or compiling it at runtime using the quiloader. Let's focus on the latter option, could someone please let me know how I can hook the closeEvent in this case?

                    I found a potential solution here: https://stackoverflow.com/q/14892713/1592410. But it seems to be too hacky as one have to recreate the UI loader.

                    J 1 Reply Last reply 6 May 2022, 13:16
                    0
                    • C changyuheng
                      6 May 2022, 12:50

                      I totally get the OP as I encountered the same situation. His question is not directly answered here. I get there are 2 ways to inflate .ui files, pre-compiled to a python file using the uic or compiling it at runtime using the quiloader. Let's focus on the latter option, could someone please let me know how I can hook the closeEvent in this case?

                      I found a potential solution here: https://stackoverflow.com/q/14892713/1592410. But it seems to be too hacky as one have to recreate the UI loader.

                      J Offline
                      J Offline
                      JonB
                      wrote on 6 May 2022, 13:16 last edited by
                      #10

                      @changyuheng
                      Well it is possible any solution will be a bit "hacky" for you :)

                      You are wanting to override closeEvent(). Normally that means you must subclass to achieve that, at least in C++.

                      However, untested but from my knowledge of Python: can't you "monkey-patch" to achieve what you want? In your instance can't you go instance.showEvent = myShowEvent or some such, e.g. https://stackoverflow.com/a/6647776/489865

                      from SomeOtherProduct.SomeModule import SomeClass
                      
                      def speak(self):
                          return "ook ook eee eee eee!"
                      
                      SomeClass.speak = speak
                      

                      Not sure whether you can monkey-patch on an instance or only for the whole class --- ah, https://filippo.io/instance-monkey-patching-in-python/ for example shows instance monkey-patching.

                      If you find this too hacky I leave it for another person to propose something else acceptable to you.

                      1 Reply Last reply
                      0
                      • C Offline
                        C Offline
                        changyuheng
                        wrote on 7 May 2022, 10:23 last edited by changyuheng 5 Jul 2022, 10:32
                        #11

                        Thanks for replying, @JonB.

                        Well it is possible any solution will be a bit "hacky" for you :)

                        Well, if I can fix an issue by calling the right API or playing with a few APIs, that's a normal solution. If I have to add customized logics that should be done in the library, that's a hack. If I have to patch QUiLoader or recreate one in order to make the built-in hook function closeEvent of QWidget work, it's obviously a hack.

                        You can't do the "monkey-patch" for the closeEvent if you have read the original post.

                        I can create a subclass of QMainWindow. But is there a way I can make QUiLoader load a .ui file onto my extended QMainWindow?

                        J 1 Reply Last reply 7 May 2022, 10:39
                        0
                        • C changyuheng
                          7 May 2022, 10:23

                          Thanks for replying, @JonB.

                          Well it is possible any solution will be a bit "hacky" for you :)

                          Well, if I can fix an issue by calling the right API or playing with a few APIs, that's a normal solution. If I have to add customized logics that should be done in the library, that's a hack. If I have to patch QUiLoader or recreate one in order to make the built-in hook function closeEvent of QWidget work, it's obviously a hack.

                          You can't do the "monkey-patch" for the closeEvent if you have read the original post.

                          I can create a subclass of QMainWindow. But is there a way I can make QUiLoader load a .ui file onto my extended QMainWindow?

                          J Offline
                          J Offline
                          JonB
                          wrote on 7 May 2022, 10:39 last edited by JonB 5 Jul 2022, 10:48
                          #12

                          @changyuheng said in How can QUiLoader load ui to "self" and trigger closeEvent:

                          You can't do the "monkey-patch" for the closeEvent if you have read the original post.

                          I did read the original post. Did you read the link to "instance monkey-patching" I posted? I can't see anything there which cannot be done by replacing self with the instance you want patched from outside the class, which you have available in self.ui. But perhaps you know Python better than I do.

                          I can create a subclass of QMainWindow. But is there a way I can make QUiLoader load a .ui file onto my extended QMainWindow?

                          I believe you are supposed to do this via PySide6.QtUiTools.QUiLoader.registerCustomWidget(customWidgetType)

                          This is needed when you want to override a virtual method of some widget in the interface, since duck punching will not work with widgets created by QUiLoader based on the contents of the .ui file.

                          I know nothing about hitting birds with gloves. A stackoverflow post may have said problems getting this to work with PySide2, don't know if the coder got it right, don't know if it's better in Qt6.

                          C 1 Reply Last reply 7 May 2022, 13:49
                          0
                          • J JonB
                            7 May 2022, 10:39

                            @changyuheng said in How can QUiLoader load ui to "self" and trigger closeEvent:

                            You can't do the "monkey-patch" for the closeEvent if you have read the original post.

                            I did read the original post. Did you read the link to "instance monkey-patching" I posted? I can't see anything there which cannot be done by replacing self with the instance you want patched from outside the class, which you have available in self.ui. But perhaps you know Python better than I do.

                            I can create a subclass of QMainWindow. But is there a way I can make QUiLoader load a .ui file onto my extended QMainWindow?

                            I believe you are supposed to do this via PySide6.QtUiTools.QUiLoader.registerCustomWidget(customWidgetType)

                            This is needed when you want to override a virtual method of some widget in the interface, since duck punching will not work with widgets created by QUiLoader based on the contents of the .ui file.

                            I know nothing about hitting birds with gloves. A stackoverflow post may have said problems getting this to work with PySide2, don't know if the coder got it right, don't know if it's better in Qt6.

                            C Offline
                            C Offline
                            changyuheng
                            wrote on 7 May 2022, 13:49 last edited by changyuheng 5 Jul 2022, 16:39
                            #13

                            @JonB

                            I did read the original post. Did you read the link to "instance monkey-patching" I posted? I can't see anything there which cannot be done by replacing self with the instance you want patched from outside the class, which you have available in self.ui. But perhaps you know Python better than I do.

                            I know what monkey-patching is. Yes, replacing the closeEvent method insdie self.ui object that was generated by QUiLoader is supposed to work. But I'm telling you it's not working as I have tested it. And this is addressed by the OP at the beginning. You also pointed it out already at:

                            PySide6.QtUiTools.QUiLoader.registerCustomWidget(customWidgetType)
                            This is needed when you want to override a virtual method of some widget in the interface, since duck punching will not work with widgets created by QUiLoader based on the contents of the .ui file.

                            So...

                            I believe you are supposed to do this via PySide6.QtUiTools.QUiLoader.registerCustomWidget(customWidgetType)

                            This is promising. I've thought of this too. I didn't try it yet because I'm currently having the UI loader run inside my own QMainWindow. But I think this is the way to go. Thank you.

                            1 Reply Last reply
                            0
                            • C Offline
                              C Offline
                              changyuheng
                              wrote on 7 May 2022, 16:24 last edited by changyuheng 5 Jul 2022, 16:28
                              #14

                              In case anyone else runs into the same situation. To load (inflate) a .ui file by QUiLoader with a customized QMainWindow is possible, and it should be the way to go if customizing the closeEvent or any other built-in behaviors is the goal.

                              However, promoting QMainWindow is not allowed from Qt Designer at the moment. This has to be done by manually editing the .ui file:

                              - <widget class="QMainWindow" name="MainWindow">
                              + <widget class="QMainWindowExt" name="MainWindow">
                              
                                <customwidgets>
                              +  <customwidget>
                              +   <class>QMainWindowExt</class>
                              +   <extends>QMainWindow</extends>
                              +   <header>QMainWindowExt.h</header>
                              +  </customwidget>
                                </customwidgets>
                              

                              And from the python code, registerCustomWidget(QMainWindowExt) is required before loading the .ui file. An example:

                                  ui_loader: QUiLoader = QUiLoader()
                                  ui_loader.registerCustomWidget(QMainWindowExt)
                                  main_window_ui: pathlib.Path
                                  with importlib.resources.path(__package__, 'main_window.ui') as main_window_ui:
                                      main_window_ui_file: QFile = QFile(str(main_window_ui))
                                      if not main_window_ui_file.open(QIODevice.ReadOnly):
                                          raise RuntimeError(f"Cannot open {main_window_ui}: {main_window_ui_file.errorString()}")
                                      main_window: QMainWindowExt = ui_loader.load(main_window_ui_file)
                                      main_window_ui_file.close()
                              

                              Of course, you'll have to create a QMainWindowExt:

                              class QMainWindowExt(QMainWindow):
                                ...
                              
                              1 Reply Last reply
                              0
                              • S sylvalas
                                24 Nov 2021, 13:26

                                This is how it is used for most ppl I think:

                                class Mainwin():#inherit QMainWindow or not wont make any difference. The right logic here is not to I think.
                                    def __init__(self):
                                        super().__init__()
                                        self.LoadUI()
                                        self.ui.show()
                                    def LoadUI(self):
                                            loader=QUiLoader()
                                            uifile=QFile('youruifile.ui')
                                            uifile.open(QFile.ReadOnly)
                                            self.ui = loader.load(uifile,self)
                                            uifile.close()    
                                
                                

                                Is there a way to use QUiLoader to load ui to "self"?
                                so I can access widgets by self.widget instead of self.ui.widget.

                                Also, since it is not loading ui to self, the class where I load the ui is just a shell, defining closeEvent in it won't work.

                                Since self.ui is the real QMainWindow so I tried override the closeEvent of the class but still it won't work.

                                def closeEvent(self, event:QCloseEvent) -> None:
                                    print('closed')
                                QWidget.closeEvent=closeEvent
                                QMainWindow.closeEvent=closeEvent
                                
                                C Offline
                                C Offline
                                changyuheng
                                wrote on 8 May 2022, 03:41 last edited by changyuheng 5 Aug 2022, 03:41
                                #15

                                @sylvalas
                                With the instructions shown above, should you consider marking this question as resolved?

                                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