'Promoted Widget in Qt Designer does not instantiate children

Problem

I promoted a widget in Qt Designer and it worked, the problem is that when I run the program, it doesn't show the children of this widget that I created in Qt Designer.

Example

So in order to show a code that can produce the problem I created a test project where I can demonstrate the problem

Consider the following UI created in Qt Designer:

Consider the following UI created in Qt Designer

As you can see, it contains a centralWidget, with a BoxLayout, and two children, a label and a QTabWidget.

When I run the program now, everything is fine:

When I run the program now, everything is fine

But when I promote the CentralWidget to a custom Widget:

But when I promote the CentralWidget to a custom Widget

The layout is now broken:

The layout is now broken

Code

form.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="CentralWidgetPromotion" name="centralwidget">
   <widget class="QWidget" name="verticalLayoutWidget">
    <property name="geometry">
     <rect>
      <x>9</x>
      <y>19</y>
      <width>771</width>
      <height>531</height>
     </rect>
    </property>
    <layout class="QVBoxLayout" name="verticalLayout">
     <item>
      <widget class="QLabel" name="label">
       <property name="styleSheet">
        <string notr="true">background-color: rgb(220, 138, 221);</string>
       </property>
       <property name="text">
        <string>This is a test</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QTabWidget" name="tabWidget">
       <property name="sizePolicy">
        <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
         <horstretch>0</horstretch>
         <verstretch>0</verstretch>
        </sizepolicy>
       </property>
       <property name="currentIndex">
        <number>0</number>
       </property>
       <widget class="QWidget" name="tab">
        <attribute name="title">
         <string>This is tab 2</string>
        </attribute>
       </widget>
       <widget class="QWidget" name="tab_2">
        <attribute name="title">
         <string>This is tab1</string>
        </attribute>
       </widget>
      </widget>
     </item>
    </layout>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>23</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <customwidgets>
  <customwidget>
   <class>CentralWidgetPromotion</class>
   <extends>QWidget</extends>
   <header>central_widget_promotion.h</header>
   <container>1</container>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

mainwindow.py (QMainWindow)

# This Python file uses the following encoding: utf-8
import os
from pathlib import Path
import sys

from PyQt6.QtWidgets import QApplication, QMainWindow
from PyQt6 import uic

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.loadUi()

    def loadUi(self):
        path = os.fspath(Path(__file__).resolve().parent / "form.ui")
        uic.loadUi(path,self)

if __name__ == "__main__":

    qt_app = QApplication(sys.argv)
    widget = MainWindow()
    widget.show()
    sys.exit(qt_app.exec())

CentralWidgetPromotion.py the widget in which I promoted the centralWidget

from PyQt6.QtWidgets import QWidget

class CentralWidgetPromotion(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)

        print("Promotion working")

I hope I made my problem clear, if there is still any problem, please ask.



Solution 1:[1]

From what I can see, it could be a "bug" in uic; by looking at uiparser.py, I found this line:

        elif isinstance(topwidget, QtWidgets.QMainWindow):
            if type(widget) == QtWidgets.QWidget:
                topwidget.setCentralWidget(widget)

Since type() == <class> only matches the exact class, and not the inheritance, the result of that comparison will be False: while your promoted class does inherit from QWidget, it is not a QWidget type, but a CentralWidgetPromotion one.

Supposing you know for sure the object name of the central widget, you can work around it with this:

    def loadUi(self):
        path = os.fspath(Path(__file__).resolve().parent / "form.ui")
        uic.loadUi(path, self)

        if not self.centralWidget() and hasattr(self, 'centralwidget'):
            self.setCentralWidget(self.centralwidget)

Consider that there's usually very little point in promoting the central widget: promotion is normally intended to extend specific "final" widgets, and since the central widget is mainly a basic container, most of what you would probably do can be easily implemented in the QMainWindow subclass you're already using.

Note that you are not supposed to "add" a layout to the central widget by dragging it from the widget box, but set one: right now, that layout is just "floating" in the window, and if you resize it, its contents won't be resized along with it (always check for that small red ? symbol in the object tree, which means that the relative widget has no layout set). Remove that vertical layout (right click on any of its widget, and select "Break layout" from the "Lay out" sub menu), then right click on an empty area of the window and select the layout from the relative menu. Read how to properly use layout managers in Designer

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 musicamante