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

Signals blocked from widget in QMdiSubwindow but not in QDilaog



  • I have created a widget with a QPushbutton. I connect to the clicked signal. If I host the widget in a QDialog the signal is sent and received by the handler just fine, but if it is used in a QMdiSubwinow, I press the button and nothing happens. I have poked at the thing a bunch of different times trying to set up parent / child relationships more explicitly, but there is no joy. I am not sure how to really debug this behavior. Is there a way I can watch the signal fire and see who receives it? It would seem to be something with the way the QMdiSubwindow takes ownership, but can't tell. Any advice on this issue is helpful. I am dead in the water.

    Not sure if this is relevant, but the widget is created in the Qt Creator and I am loading it with the QUiLoader.


  • Banned

    Can you supply an MRE (Minimal Reproducible Example) such that we can just copy/paste and run and have the issue you are currently having so that we can help you with your issue -- there are numerous things that could potentially cause things like this.

    Look forward to seeing your MRE and potentially helping you solve your problem

    Note the issue could be with the UI Code as these are notorious of creating issues that need to be overcome that you would not have if you created your GUI using proper Qt code



  • Well shoot, I created the simplest case which had what i thought were all the variables, but I was unable to replicate the issue. Obviously the real code is more complex, so I must have missed something. Still would be happy to entertain any thoughts.

    import sys
    from PySide2 import QtCore, QtWidgets, QtUiTools
    
    widget_xml = """<?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>IntermittentWidget</class>
     <widget class="QWidget" name="IntermittentWidget">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>127</width>
        <height>68</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>Intermittent Widget</string>
      </property>
      <widget class="QPushButton" name="intermittent_button">
       <property name="geometry">
        <rect>
         <x>10</x>
         <y>20</y>
         <width>112</width>
         <height>32</height>
        </rect>
       </property>
       <property name="text">
        <string>Intermittent</string>
       </property>
      </widget>
     </widget>
     <resources/>
     <connections/>
    </ui>
    """
    
    
    class QtViewController(QtCore.QObject):
    
        def __init__(self, ui_file: str, parent: QtWidgets.QWidget = None):
            super().__init__()
            byte_array = QtCore.QByteArray(bytes(ui_file, 'utf_8'))
            file = QtCore.QTemporaryFile()
            file.open()
            file.write(byte_array)
            file.seek(0)
            loader = QtUiTools.QUiLoader()
            self._view = loader.load(file, parent)
            self._init_view()
    
        @property
        def view(self):
            return self._view
    
        def _init_view(self):
            pass
    
    class IntermittentController(QtViewController):
    
        def __init__(self, ui_file: str, parent: QtWidgets.QWidget = None):
            super().__init__(ui_file, parent)
    
        def _init_view(self):
            self.view.intermittent_button.clicked.connect(self.on_click)
    
        def on_click(self):
            msgBox = QtWidgets.QMessageBox()
            msgBox.setWindowTitle('Click Handler')
            msgBox.setText('Handled Click')
            msgBox.exec()
    
    
    def main():
    
        # create app
        QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
        app = QtWidgets.QApplication(sys.argv)
        app.setApplicationName('IssueMRE')
        app.setApplicationDisplayName('Issue MRE')
    
        # create main window
        main_window = QtWidgets.QMainWindow()
        main_window.setWindowTitle('Issue MRE')
    
        # create mdi area
        mdi_area = QtWidgets.QMdiArea()
        mdi_area.setViewMode(QtWidgets.QMdiArea.TabbedView)
        mdi_area.setTabsClosable(True)
        mdi_area.setTabsMovable(True)
        mdi_area.setDocumentMode(True)
        main_window.setCentralWidget(mdi_area)
    
        # create subwindow
        subWindow = QtWidgets.QMdiSubWindow(mdi_area)
    
        # create widget with button
        intermittent_controller = IntermittentController(widget_xml)
    
        # create subwindow with widget
        subWindow.setWidget(intermittent_controller.view)
        subWindow.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        mdi_area.addSubWindow(subWindow)
        intermittent_controller.view.setWindowState(QtCore.Qt.WindowMaximized)
    
        main_window.show()
        # enter app loop
        sys.exit(app.exec_())
    
    main()
    


  • OK. I pretty much cut and pasted this working code into my app and I get the same problem. There has to be something about the context that this thing is running in that is preventing this signal from getting through...



  • This post is deleted!


  • This is really bizarre. I had a typo in my code for some tests I was doing and I actually had left in the line:

    my_pushbutton.connect()

    I called this with no parameters and didn't event try to connect the signal. I got an error, but any time after that when I clicked on my button the clicked signal was sent and received as expected????


  • Banned

    Okay just a quick question is there an important reason that you are making your GUI using XML formatting instead of straight Qt?

    Also I am trying to pick this apart and there are numerous issues with this code that could be causing this issue and a whole lot more. I will do my best to figure out what you are trying to do and create a proper Python-Qt version but you are using a lot of complicated features in this that I have not played with as of yet -- but I love challenges



  • @Denni-0 In my real app I create the ui with Qt Creator. In my example code I just used the xml that was produced by Qt Creator incase it was doing something unique that might be causing the issue.



  • OK. I figured out what is causing this and a work around. I don't exactly know why it is causing the issue. I am creating my widget inside the handler of a menu click event. Something about this is causing my issue. If I create the widget outside of the handler and create the subwindow inside the handler, everything works as expected. Not sure what is going on, everything is on the same thread, maybe something is getting posted to the event loop in a way I don't understand and causing something to go out of order, or maybe qt's signal processing engine is doing some type of weird context thing I am unaware of. Not sure. But I have a work around I can live with.


  • Banned

    Okay I have not finished this -- but this outlines some of the issues I found so that perhaps that will help you some -- again I am trying to rework this to show you how simple creating the proper Qt is so that using the Creator GUI is not necessary because it creates issues when you use it since I am pretty sure it relies on the Designer to implement the code element of you GUI

    class QtViewController(QObject):
        def __init__(self, ui_file: str, parent: QtWidgets.QWidget = None):
          # First do not use super( ) unless you are fully aware of what 3 major issues 
          # you must code for in conjunction with using it and the actual rare issue 
          # that it is meant to solve. Note this rare issue is rare and unless you are 
          # doing some complicated inheritance you will most likely never run into that
          # issue -- however the 3 major issues it creates by using it you are much more 
          # likely to run into than the rare issue its meant for
          #
          # Further this implementation of super() was incorrect
          #  super().__init__()
    
            byte_array = QByteArray(bytes(ui_file, 'utf_8'))
            file = QTemporaryFile()
            file.open()
            file.write(byte_array)
            file.seek(0)
            loader = QUiLoader()
            self._view = loader.load(file, parent)
            self._init_view()
    
        @property
        def view(self):
            return self._view
    
        def _init_view(self):
            pass
    
    # Are you sure this is correct QtViewController and not QViewController??
    class IntermittentController(QtViewController):
        def __init__(self, ui_file: str, parent: QWidget = None):
          # Same as above and again this implementation of super() is incorrect
          #  super().__init__(ui_file, parent)
            QtViewController.__init__(self, ui_file, parent)
    
        def _init_view(self):
            self.view.intermittent_button.clicked.connect(self.on_click)
    
        def on_click(self):
            msgBox = QMessageBox()
            msgBox.setWindowTitle('Click Handler')
            msgBox.setText('Handled Click')
            msgBox.exec()
    
    
    # Create Main Window
    class MainWin(QMainWindow):
        def __init__(self):
            QMainWindow.__init__(self)
            self.setWindowTitle('Issue MRE')
    
            # create mdi area
            mdi_area = QMdiArea()
            mdi_area.setViewMode(QMdiArea.TabbedView)
            mdi_area.setTabsClosable(True)
            mdi_area.setTabsMovable(True)
            mdi_area.setDocumentMode(True)
            main_window.setCentralWidget(mdi_area)
    
            # create subwindow
            subWindow = QMdiSubWindow(mdi_area)
    
            # create widget with button
            intermittent_controller = IntermittentController(widget_xml)
    
            # create subwindow with widget
            subWindow.setWidget(intermittent_controller.view)
            subWindow.setAttribute(QtCore.Qt.WA_DeleteOnClose)
            mdi_area.addSubWindow(subWindow)
            intermittent_controller.view.setWindowState(QtCore.Qt.WindowMaximized)
    
    if __name__ == "__main__":
        QCoreApplication.setAttribute(Qt.AA_ShareOpenGLContexts)
      # If you are not using Command Line objects then you do not need sys.argv
      # However if you are using Command Line objects you should look into 
      # argparser library as it handles them much cleaner and more intuitively
      # app = QApplication(sys.argv)
        MainEventHandler = QApplication([])
    
        App = MainWin()
        App.setApplicationName('IssueMRE')
        App.setApplicationDisplayName('Issue MRE')
        App.show()
        
      # This is the old Python-Qt4 method of calling this
      # sys.exit(app.exec_())
      # This is the current Python-Qt5 method of calling this
      #  MainEventHandler.exec()
      # Although I believe PySide2 is still slightly behind and needs this
        sysExit(MainEventHandler.exec())
    
      # If anyone wants more extensive free help I run an online lab-like classroom-like 
      # message server feel free and drop by you will not be able to post until I clear 
      # you as a student as this prevents spammers so if interested here is the invite
      # https://discord.gg/3D8huKC
    

  • Banned

    @Howard-Swope would you be interested in seeing what this might look like rendered in straight Python-Qt ?


  • Lifetime Qt Champion

    Hi,

    @Howard-Swope said in Signals blocked from widget in QMdiSubwindow but not in QDilaog:

    OK. I figured out what is causing this and a work around. I don't exactly know why it is causing the issue. I am creating my widget inside the handler of a menu click event. Something about this is causing my issue. If I create the widget outside of the handler and create the subwindow inside the handler, everything works as expected. Not sure what is going on, everything is on the same thread, maybe something is getting posted to the event loop in a way I don't understand and causing something to go out of order, or maybe qt's signal processing engine is doing some type of weird context thing I am unaware of. Not sure. But I have a work around I can live with.

    You likely have an object life time issue. If you create it within the slot and it only exist there, it might be garbage collected.


  • Banned

    @SGaist keep in mind that in that original code not only did they use super( ) but they used it incorrectly which means I am sure that had its issues compounded --- also I am wondering about this QtViewController that looks like a library name (like QtCore, etc..) and not an actual Module name as those just start with a Q (like QWidget, etc...)

    This is why I included my partial re-rendering of his original code to help them see those errors and perhaps answer my question about that QtViewController



  • @SGaist : Thank you. You are exactly correct. I thought by calling the connect I was increasing the reference count and would avoid the garbage collection. Issue solved.

    @Denni-0 : I think the use of super() is correct, at least after reviewing the python documentation:

    https://docs.python.org/3.7/library/functions.html?highlight=super#super



  • Oh and @Denni-0 Thanks for your help on this. I am very pleased to have come to a resolution.



  • @Howard-Swope said in Signals blocked from widget in QMdiSubwindow but not in QDilaog:

    have come to a resolution

    so please don't forget to mark your post as solved!


  • Banned

    @Howard-Swope said in Signals blocked from widget in QMdiSubwindow but not in QDilaog:

    @Denni-0 : I think the use of super() is correct, at least after reviewing the python documentation:

    https://docs.python.org/3.7/library/functions.html?highlight=super#super

    Hmm okay went and looked that up again and my bad you did use super() the current python3 way -- However as I pointed out in the code I posted using super() in python carries with it issues and currently bugs while not using super() (aka the explicit form) does neither of these. As such you would do yourself a major favor by no longer using super() because frankly in Python you are gaining nothing by using it and losing a lot. It does by the way work in the static languages C++ and Java but Python is not a static language which is why it does not work the same


Log in to reply