Uploaded image for project: 'Qt for Python'
  1. Qt for Python
  2. PYSIDE-3201

Signal-slot failure with same-named parent class

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P5: Not important P5: Not important
    • 6.10.0, 6.10.1, 6.11.0
    • 6.9.2
    • PySide
    • None
    • RockyOS
    • Linux/X11
    • f3ebe9b04 (dev), 6e80c963f (6.10)

      When creating nested subclasses of QObjects using PySide, signal-slot connections fail to create in cases where parent and child classes have the same name.

      Suppose we create a subclass of QTreeView named MyTreeView in module01.py, and add QSignals to its class definition. Then, in a different module02.py, we create a subclass of module01.MyTreeView and also name it MyTreeView, adding further QSignals to its class definition. At runtime, if we attempt to connect to the inherited QSignals, the connection fails with:

      Sort Warning
      Signals and slots in QMetaObject '{objectName}' are not ordered correctly, this may lead to issues.

      If the most-nested class is renamed to anything else (MyTreeView2 etc) everything is working as intended. The issue only occurs when the parent and child have the same name. Also if they are separated by additional intermediate classes everything is OK as well. The issue only occurs when the immediate parent has the same name as its immediate child.

      We looked for existing PySide bugs reported with the Sort Warning error, and found PYSIDE-2033, but it seems to be a different issue. We're also aware that the same warning can be elicited with combinations of dynamically-added signals and slots, particularly where the @QtCore.Slot() decorator is not used. However, this is a separate issue again.

      This is a long-running issue in PySide--we have tested on both PySide 6.x and PySide 2.x and the issue is consistently generated. Interesting, we have no such issue with PyQt (we are migrating an existing codebase from PyQt to PySide).

      As a workaround we can ensure that the class names differ where possible. However, this is not always desirable. In some cases the intermediate class name is fixed via a third-party library and we want to reuse the same class name in our own child classes rather than needing a contrived prefix or suffix to uniquify it. (Side note: In C++ it is not possible to use the same class name for parent and child classes unless they are in different namespaces; in Python a similar rule applies except that we can regard a module as a namespace, so for example we could create, say, class Matrix(math.Matrix)... in cases where we want to create a subclass but reuse the same name from a different module if it is the most natural name for the given problem domain).

      Inspecting the code, it seems that this issue emerged as a by-product of the fix for PYSIDE-315 from April 2016, where signal-slot sorting was added. It seems that MetaObjectBuilderPrivate::parsePythonType() is involved. There is a potentially interesting line of code where parent-child class names are used to determine whether or not to follow a specific code-branch. I suspect something like this is happening here.

      To reproduce:

      pyside1.py

      # Create a subclass of QTreeView named MyTreeView with two signals.
      from PySide6 import QtCore, QtWidgets
      
      class MyTreeView(QtWidgets.QTreeView):
          signal01 = QtCore.Signal()
          signal02 = QtCore.Signal()
      
          def __init__(self, parent=None):
              super(MyTreeView, self).__init__(parent=parent)

      pyside2.py

      from PySide6 import QtCore, QtWidgets
      import pyside1
      
      # Create a subclass of pyside1.MyTreeView, also called MyTreeView.
      # In the initialiser, attempt to create signal-slot connections to
      # both inherited class signals and current class signals.
      class MyTreeView(pyside1.MyTreeView):
          signal03 = QtCore.Signal()
      
          def __init__(self, parent=None):
              super(MyTreeView, self).__init__(parent=parent)
              self.signal01.connect(self.slot01)
              self.signal02.connect(self.slot02)
              self.signal03.connect(self.slot03)
      
          @QtCore.Slot()
          def slot01(self):
              print("slot01 invoked...")
          
          @QtCore.Slot()
          def slot02(self):
              print("slot02 invoked...")
          
          @QtCore.Slot()
          def slot03(self):
              print("slot03 invoked...")
      
      # Create a placeholder main window to house an instance of MyTreeView.
      # When the window is shown, invoke various signals from MyTreeView to check
      # whether the slots are currectly invoked.
      class MainWindow(QtWidgets.QMainWindow):
          def __init__(self):
              super(MainWindow, self).__init__()
              self._control = MyTreeView()
              self.setCentralWidget(self._control)
              self.show()
              self._control.signal01.emit()
              self._control.signal02.emit()
              self._control.signal03.emit()
      
      if __name__ == "__main__":
          # Launch the main window.
          import sys
          app = QtWidgets.QApplication(sys.argv)
          window = MainWindow()
          sys.exit(app.exec()) 

      At a bare minimum, if this is a hard-and-fast limitation of PySide6 that is unlikely to be addressed in the near future, documenting this limitation and including a note regarding this issue in the Sort Warning would help save developer time.

       

        1. pyside3201.zip
          3 kB
        2. pyside3201_log.txt
          39 kB
        3. pyside3201_diag.diff
          7 kB
        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

            kleint Friedemann Kleint
            jpcollins JP Collins
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved:

                There are no open Gerrit changes