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.
  • jsulmJ jsulm

    @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 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

      @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

      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on 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 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

        JonBJ 1 Reply Last reply
        0
        • S sylvalas

          @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

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on 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 last edited by changyuheng
            #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.

            JonBJ 1 Reply Last reply
            0
            • C changyuheng

              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.

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on 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 last edited by changyuheng
                #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?

                JonBJ 1 Reply Last reply
                0
                • C changyuheng

                  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?

                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by JonB
                  #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
                  0
                  • JonBJ JonB

                    @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 last edited by changyuheng
                    #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 last edited by changyuheng
                      #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

                        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 last edited by changyuheng
                        #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