pythonqtwidgetpyside6pyside6-gui

Pyside6-designer: export Python code of custom (promoted) widget


I've created the design for a widget in PySide6 GUI.
This MyCustomWidget is defined in my main UI, which contains many other things.
Please consider that this custom widget can be quite complex and have many sub-widgets, and I find it much more convenient to design it in the UI.

I would like to isolate and export the code of this widget alone, so I can import/programmatically generate copies of it from the main UI code.

However, normally, the UI export can only export the entire code for my main UI (with the custom widget code embedded in it). I tried promoting the widget, which I would expect to also take and isolate the code for the promoted widget in a class or separate module. If I promote the widget, and then I export the code for my main UI, I get something like:

################################################################################
## Form generated from reading UI file 'main_window_uiERYisl.ui'
##
## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################

from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
    QMetaObject, QObject, QPoint, QRect,
    QSize, QTime, QUrl, Qt)

# More imports [...]

from myCustomWidget import MyCustomWidget

Where myCustomWidget is the expected module containing the widget, once it was promoted it. However, this is not exported as part of the main UI code conversion process -- only the main_window.ui content is exported in a single file.

Is there a way to isolate and export the code of a custom widget, promoted or not?


Solution

  • The issue is that PySide6-designer does not export the code of the promoted widget. This is because promoted widgets are not designed to work this way, but to reference external code. This however disallows the design and packaging of custom (promoted or not) widgets with their UI code from the designer itself.

    However, there's a fairly simple workaround that I found in order to design and export the code for a custom widget directly from the PySide6-designer.

    1. Design your widget in the Designer GUI

    Create your widget in PySide6-designer as you would normally do from within your main dialogue form. For example, the following is a widget designed with sub-widgets in it.
    Please consider that this custom widget can be quite complex and have many sub-widgets, and it can be very convenient to design it in the UI.

    enter image description here

    2. Promote the widget.

    Select the widget and right click -> promote to. Set the class name to something like MyCustomWidget and the header file to a name like my_custom_widget (this will be the expected Python module name; i.e. the file containing the widget will need to be named my_custom_widget.py).

    Now, when the main dialogue form code is exported (e.g. by doing pyside6-uic your_file.ui -o ui_your_file.py or Form -> View Python code -> Save), it will reference the promoted widget code by including a line at the top of the file:

    from my_custom_widget import MyCustomWidget
    

    Unfortunately, the export does not include the code of the promoted widget in a separate metadatarenamewidget.py file, as it only exports 1 file containing the main ui and none of the promoted widget code, which is merely expected as a import from a separate file.

    Therefore, we will need the next step in order to get the promoted widget code.

    3. Export the code of the promoted widget

    This involves the following steps:

    The exported code will have a form like:

    
    from PySide6.QtCore import ...
    
    class Ui_Dialog(object):
        def setupUi(self, Dialog):
            if not Dialog.objectName():
                Dialog.setObjectName(u"Dialog")
            Dialog.resize(1138, 300)
    
            # [...code of your promoted-demoted widget...]
    
    

    Now, replace the class signature to go from Dialog class to QWidget class, matching the expected class name as per when we promoted the widget (MyCustomWidget), and deleting the Dialog-specific code which we don't need:

    from PySide6.QtCore import ...
    
    class MyCustomWidget(QWidget):
        def __init__(self, parent: QWidget | None=None):
            super().__init__(parent)
    
            # [...code of your promoted-demoted widget...]
    
    

    Et voilĂ ! Designed and exported the code of the widget directly from PySide6-designer.

    This widget code will be correctly referenced by the main form dialog code.

    I couldn't find any simpler way, which seems weird, but at least this works. Happy to get comments if there's any simpler solution.