I'm trying to build an app with extensive use of QDockwidgets. However, I've found that I cannot use QDockwidgets made in Qt Designer.
Here's my customized dockwidget.ui as displayed in Designer:
Here's the QMainWindow form main2.ui with promoted widget (called DockWidget with instance name dw)
Here's what the finished app looks like. The dockwidget is completely empty. Are there any tricks to getting a QDockWidget form (ui file) from Designer to display properly in PyQt5?
main2.py
import sys
from PyQt5 import uic, QtWidgets
from PyQt5.QtWidgets import QApplication
class Main(QtWidgets.QMainWindow, uic.loadUiType('main2.ui')[0]):
def __init__(self, parent=None):
super().__init__()
self.setupUi(self)
app = QApplication(sys.argv)
main = Main(None)
main.show()
sys.exit(app.exec_())
dockwidget.py
from PyQt5 import uic
from PyQt5.QtWidgets import QDockWidget
# ~ form_class = uic.loadUiType('dockwidget.ui')[0]
class DockWidget(QDockWidget):
def __init__(self, parent=None):
super().__init__()
uic.loadUi('dockwidget.ui', self)
main2.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>390</width>
<height>233</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<property name="maximumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout"/>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>390</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="DockWidget" name="dw">
<attribute name="dockWidgetArea">
<number>4</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents"/>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>DockWidget</class>
<extends>QDockWidget</extends>
<header>dockwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
dockwidget.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DockWidget</class>
<widget class="QDockWidget" name="DockWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>328</width>
<height>303</height>
</rect>
</property>
<property name="features">
<set>QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable</set>
</property>
<property name="allowedAreas">
<set>Qt::BottomDockWidgetArea|Qt::TopDockWidgetArea</set>
</property>
<property name="windowTitle">
<string>My Custom Dock Widget</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QGridLayout" name="gridLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="2" column="0">
<widget class="QRadioButton" name="radioButton_2">
<property name="text">
<string>RadioButton</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="radioButton">
<property name="text">
<string>RadioButton</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QRadioButton" name="radioButton_3">
<property name="text">
<string>RadioButton</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
Similarly to scroll areas, Designer creates a "contents" widget for new QDockWidgets. That widget is actually set using setWidget()
when the main window UI is loaded, and it cannot be removed from Designer.
You already have a contents widget in your custom QDockWidget, but when it's added by the main ui, it's "overwritten" by it.
Consider that promoted classes that act as containers should only have their contents set in the class as long as the promoted widget does not change/override them, otherwise the previously set contents will be removed. For instance, if you create a custom widget with its layout and buttons, and use it as a promoted class in Designer, you cannot set another layout and add other children to that widget from there.
The only way to avoid the issue and actually create a customizable QDockWidget that already has basic contents set and can be added to a QMainWindow in Designer would be to create a Designer plugin. That's not easy, the documentation is complex and, especially for python, extremely poor (with support even missing for some classes).
There are various possibilities here, though:
addDockWidget()
in your code;dockWidgetContents
of the dock widget that is added to the main window ui;setWidget()
on the dock widget, and you really need the dock widget in the main window of Designer, override setWidget()
in a similar way:class DockWidget(QDockWidget):
def __init__(self, parent=None):
super().__init__()
uic.loadUi('dockwidget.ui', self)
def setWidget(self, widget):
if self.widget() is None:
super().setWidget(widget)
with the above code, the setWidget()
call made by uic/setupUi of the main window will be ignored, as at that point the dock widget already has a widget set by its own loadUi
.