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. PySide2 - Qt Designer with custom signals and slots of various Python types

PySide2 - Qt Designer with custom signals and slots of various Python types

Scheduled Pinned Locked Moved Unsolved Qt for Python
9 Posts 2 Posters 2.5k 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.
  • LimeyLimeL Offline
    LimeyLimeL Offline
    LimeyLime
    wrote on last edited by
    #1

    Hi,
    I am trying to develop a Qt application by relying on the Qt Designer and custom PySide2 widgets. The philosophy is that everything that can happen in the Designer should remain there so as to make the Python scripts shorter and more readable (and easier to maintain). I would like to manage all signals and slots connections in the Designer but I run into various problems that includes signal arguments and types. Let me explain further.

    Expected Workflow
    The workflow I envision is the following:

    • Design the UI as a QMainWindow using Qt Designer and native Qt widgets,
    • If necessary, write custom widgets in Python by subclassing PySide 2 objects binded to native Qt widgets
    • Create the custom signals and slots of those custom widgets,
    • Promote native Qt widgets in Qt Designer to use the custom widgets
    • Write my Python application as a subclass of Pyside2 QMainWindow, import the UI file created with the Designer (without tweaking the XML manually) and only write a little bit of application logic in there,
    • Manage all signals and slots connections between the custom widgets and the main window (or application) in Qt Designer.

    The Problems & the Questions
    While I can add the custom signals and slots in the Designer (according to the link above), I seem to run into two problems:

    • Some Python types simply don't work with signals: bool and int work, assuming that the custom signal has been defined as such in the Designer but float does not. I first thought that it was because there was a straightforward correspondence between C++ types and Python types but it does not seem to be the case as float does not work. I get an error message such as:
      QObject::connect: No such signal CustomButton::buttonClicked(float)
      Python data structure types such as listand tuple do not work either. I did not test with more advanced structures such as NumPy arrays for instance.
    • I cannot pass arguments to the custom slot of a custom widget even if I specify the type (such as a bool for instance) in the Designer. I get the following error message:
      QObject::connect: No such slot CustomLabel::setCustomText(bool)

    Is there a way for me to manage all the custom signals & slots connections directly in the Designer even if said signals carry Python objects and slots receive said Python objects as arguments? I know it is possible to code it in my main application but this creates a ton of boilerplate code as I retrieve all widgets and connect the signals and slots.

    Example Code

    # test_window.py
    from PySide2.QtWidgets import QMainWindow
    from PySide2.QtUiTools import QUiLoader
    from PySide2.QtCore import QFile
    # Custom widgets
    from custom_label import CustomLabel
    from custom_button import CustomButton
    
    class TestWindow(QMainWindow):
    
        def __init__(self, path_to_ui_file):
            super(TestWindow, self).__init__()
            # Load the UI file
            self.loadUIFile(path_to_ui_file)
            self.ui.show()
    
        def loadUIFile(self, path_to_UI_file):
            loader = QUiLoader()
            loader.registerCustomWidget(CustomLabel)
            loader.registerCustomWidget(CustomButton)
            ui_file = QFile(path_to_UI_file)
            ui_file.open(QFile.ReadOnly)
            self.ui = loader.load(ui_file, self)
            ui_file.close()
    
    if __name__ == "__main__":
        import sys
        from PySide2.QtWidgets import QApplication
        app = QApplication(sys.argv)
        _ = TestWindow('test_window.ui')
        sys.exit(app.exec_())
    
    #custom_button.py
    from PySide2.QtWidgets import QPushButton
    from PySide2.QtCore import Signal
    
    class CustomButton(QPushButton):
    
        buttonClicked = Signal()
        # Signal() works with no type in the Designer
        # Signal(bool) works with bool type in the Designer
        # Signal(int) works with int type in the Designer
        # Signal(float) does not work
        # Signal(list) does not with list in the Designer
        # Signal(tuple) does not with tuple in the Designer
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.setText('Press Me!')  # Not displayed for some reason
    
        def mousePressEvent(self, event):
            print("Button was clicked")  # Just to check the event
            self.buttonClicked.emit()  # Add an argument according to signal type
    
    # custom_label.py
    from PySide2.QtWidgets import QLabel
    from PySide2.QtCore import Slot
    
    class CustomLabel(QLabel):
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
        @Slot()
        def setCustomText(self):  # Add an extra argument here to test
            self.setText(f"The button was pushed!")
    

    And finally, the UI file produced by the Designer: test_window.ui

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>800</width>
        <height>600</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralwidget">
       <layout class="QHBoxLayout" name="horizontalLayout">
        <item>
         <widget class="CustomButton" name="pushButton">
          <property name="text">
           <string/>
          </property>
         </widget>
        </item>
        <item>
         <widget class="CustomLabel" name="label">
          <property name="text">
           <string/>
          </property>
         </widget>
        </item>
       </layout>
      </widget>
     </widget>
     <customwidgets>
      <customwidget>
       <class>CustomLabel</class>
       <extends>QLabel</extends>
       <header location="global">custom_label.h</header>
       <slots>
        <slot>setCustomText()</slot>
       </slots>
      </customwidget>
      <customwidget>
       <class>CustomButton</class>
       <extends>QPushButton</extends>
       <header location="global">custom_button.h</header>
       <slots>
        <signal>buttonClicked()</signal>
       </slots>
      </customwidget>
     </customwidgets>
     <resources/>
     <connections>
      <connection>
       <sender>pushButton</sender>
       <signal>buttonClicked()</signal>
       <receiver>label</receiver>
       <slot>setCustomText()</slot>
       <hints>
        <hint type="sourcelabel">
         <x>202</x>
         <y>299</y>
        </hint>
        <hint type="destinationlabel">
         <x>596</x>
         <y>299</y>
        </hint>
       </hints>
      </connection>
     </connections>
    </ui>
    
    
    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      If you really want your signal to be emitted with different argument types, then you need to declare that signal with all the possible signatures.

      See this wiki page.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • LimeyLimeL Offline
        LimeyLimeL Offline
        LimeyLime
        wrote on last edited by LimeyLime
        #3

        Thank you @SGaist for leaning in and pointing to this useful wiki page. I better understand how to specify types and overload, both on the signal side and the slot side. Unfortunately, it does not solve the problem that Qt Designer does not handle them.

        My example above works perfectly by declaring the signal and slot signatures (for instance Signal(list) and @Slot(list)) and simply adding the following code in my main class __init__():

        self.custom_button = self.ui.findChild(CustomButton, "pushButton")
        self.custom_label = self.ui.findChild(CustomLabel, "label")
        self.custom_button.buttonClicked.connect(self.custom_label.setCustomText)
        

        But this is exactly what I want to avoid in my main class as such lines of code quickly grow up with the application complexity and make the code difficult to read.

        I define the custom signal and slot in Qt Designer for each custom widget (as mentioned in this post) and then configure the connection using the embedded Signal & Slot Editor (sorry, somebody installed this in French):
        96c899ac-f1f8-4e1b-85ed-d198a809a504-image.png
        This configuration is reflected in the XML file produced by the Designer. See below:

        <?xml version="1.0" encoding="UTF-8"?>
        <ui version="4.0">
         <class>MainWindow</class>
         <widget class="QMainWindow" name="MainWindow">
          [...]
         <customwidgets>
          <customwidget>
           <class>CustomLabel</class>
           <extends>QLabel</extends>
           <header location="global">custom_label.h</header>
           <slots>
            <slot>setCustomText(list)</slot>
           </slots>
          </customwidget>
          <customwidget>
           <class>CustomButton</class>
           <extends>QPushButton</extends>
           <header location="global">custom_button.h</header>
           <slots>
            <signal>buttonClicked(list)</signal>
           </slots>
          </customwidget>
         </customwidgets>
         <resources/>
         <connections>
          <connection>
           <sender>pushButton</sender>
           <signal>buttonClicked(list)</signal>
           <receiver>label</receiver>
           <slot>setCustomText(list)</slot>
           <hints>
            [...]
          </connection>
         </connections>
        </ui>
        

        Unfortunately, all I get when running my program is the error message below, meaning that the signal is not even recognized properly:

        QObject::connect: No such signal CustomButton::buttonClicked(list)
        QObject::connect:  (sender name:   'pushButton')
        QObject::connect:  (receiver name: 'label')
        

        Any idea? Should I go ask this question somewhere dedicated to the Qt Designer? Thanks.

        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #4

          Would it be possible for you to provide a minimal project that shows that behaviour ?

          I currently don't know how the connection for custom signals and slots from the uic file is handled in Python.

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          LimeyLimeL 1 Reply Last reply
          0
          • SGaistS SGaist

            Would it be possible for you to provide a minimal project that shows that behaviour ?

            I currently don't know how the connection for custom signals and slots from the uic file is handled in Python.

            LimeyLimeL Offline
            LimeyLimeL Offline
            LimeyLime
            wrote on last edited by LimeyLime
            #5

            @SGaist Sure thing! There you go! Thank you for your commitment to solving this.

            EDIT: I import the UI file directly but converting it to a Python file could indeed give us a hint about what's wrong.
            I did pyside2-uic test.ui > test.py and inspected the test.py file. The connection appears properly:

            ...
            self.pushButton.buttonPressed.connect(self.label.setCustomText)
            ...
            

            I haven't tested using the generated Python class though. It may work but it breaks a little bit the workflow I envisioned :-/ I wish I could just import the UI file directly without conversion.

            1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #6

              So from what you wrote, I would say that the loader class has an issue.

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              LimeyLimeL 1 Reply Last reply
              0
              • SGaistS SGaist

                So from what you wrote, I would say that the loader class has an issue.

                LimeyLimeL Offline
                LimeyLimeL Offline
                LimeyLime
                wrote on last edited by LimeyLime
                #7

                Thank you @SGaist,
                I have updated my example repository with the two ways to use UI files: direct import and Python conversion. Long story short: direct import fails but Python conversion works!

                Any idea if I could report the QUiLoader issue somewhere ?

                1 Reply Last reply
                0
                • SGaistS Offline
                  SGaistS Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  Sure: the bug report system

                  Don't forget to post back the link here, it will make the ticket easier to find :-)

                  Interested in AI ? www.idiap.ch
                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                  LimeyLimeL 1 Reply Last reply
                  0
                  • SGaistS SGaist

                    Sure: the bug report system

                    Don't forget to post back the link here, it will make the ticket easier to find :-)

                    LimeyLimeL Offline
                    LimeyLimeL Offline
                    LimeyLime
                    wrote on last edited by LimeyLime
                    #9

                    @SGaist here it is: https://bugreports.qt.io/browse/PYSIDE-1659
                    Any suggestion to improve this report is welcome of course.

                    EDIT: Not supported yet, use UI file conversion :-/

                    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