I'm searching for a way to use custom widgets in Qt Designer written with Qt for Python (PySide2) effectively.
I found out, that it is possible to design the GUI with the base widget, then just swap out the class to my custom widget in the UI File and informing the QUiLoader
about the subclass loader.registerCustomWidget(MyMainWindow)
, but then opening it again in Qt Designer doesn't work that well.
I read in this similar question for PyQt that you have to write a Plugin for the custom widget. Does this possibility also exist for PySide2?
Some example code:
custom_widget.py:
import sys
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QMainWindow, QAction, QMessageBox, QFileDialog, QTextBrowser
from PySide2.QtCore import QFile
class MyMainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle("Demo QtWidget App")
def closeEvent(self, event):
msgBox = QMessageBox()
msgBox.setWindowTitle("Quit?")
msgBox.setText("Exit application?")
msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
msgBox.setDefaultButton(QMessageBox.No)
ret = msgBox.exec_()
if ret == QMessageBox.Yes:
event.accept()
else:
event.ignore()
if __name__ == '__main__':
app = QApplication([])
file = QFile("custom_widget_original.ui")
#file = QFile("custom_widget_modified.ui")
file.open(QFile.ReadOnly)
loader = QUiLoader()
loader.registerCustomWidget(MyMainWindow)
main_window = loader.load(file)
main_window.show()
sys.exit(app.exec_())
custom_widget_original.ui
With this version the application is closed without question.
<?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="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionExit"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
</widget>
<resources/>
<connections>
<connection>
<sender>actionExit</sender>
<signal>triggered()</signal>
<receiver>MainWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>299</y>
</hint>
</hints>
</connection>
</connections>
</ui>
custom_widget_modified.ui
With this version you are asked if you really want to quit.
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="MyMainWindow" 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">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>155</width>
<height>15</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionExit"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusbar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>3</width>
<height>18</height>
</rect>
</property>
</widget>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>MyMainWindow</class>
<extends>QWidget</extends>
<header>mymainwindow.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>actionExit</sender>
<signal>triggered()</signal>
<receiver>MainWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>299</y>
</hint>
</hints>
</connection>
</connections>
</ui>
The <customwidgets>
segment is added by Qt Designer after opening it again.
After modification Qt Designer doesn't place the three labels correctly.
There are 2 main ways to use a custom widget in Qt Designer:
This is the simplest and least laborious way, if it's an internal widget you just have to right click on the widget and select Promote To ..., then in:
What the above does is generate the following:
...
<widget class="RadialBar" name="widget" native="true"/>
...
<customwidgets>
<customwidget>
<class>RadialBar</class>
<extends>QWidget</extends>
<header>radialbar.h</header>
<container>1</container>
</customwidget>
</customwidgets>
...
That is, change class, and add a field in customwidget pointing to the name of the class: <class> NAME_OF_CLASS </class>
, name of the class they inherit <extends>CLASS_EXTENDS</extends>
and file path changing the file extension from .py to .h <header>PATH_OF_FILE.h</header>
, that is, the same data as You entered the form.
In the case of the root widget it can not be done through Qt Designer, but I see that you have understood the logic but you have not modified everything correctly, your main mistake is to point out the class from which they inherit
<customwidgets>
<customwidget>
<class>MyMainWindow</class>
<extends>QMainWindow</extends> <----
<header>mymainwindow.h</header>
<container>1</container>
</customwidget>
</customwidgets>
So the correct file is:
custom_widget_modified.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="MyMainWindow" 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">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>155</width>
<height>15</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionExit"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusbar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>3</width>
<height>18</height>
</rect>
</property>
</widget>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>MyMainWindow</class>
<extends>QMainWindow</extends>
<header>mymainwindow.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>actionExit</sender>
<signal>triggered()</signal>
<receiver>MainWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>299</y>
</hint>
</hints>
</connection>
</connections>
</ui>
mymainwindow.py
import sys
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QMainWindow, QMessageBox
from PySide2.QtCore import QFile
class MyMainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle("Demo QtWidget App")
def closeEvent(self, event):
msgBox = QMessageBox()
msgBox.setWindowTitle("Quit?")
msgBox.setText("Exit application?")
msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
msgBox.setDefaultButton(QMessageBox.No)
ret = msgBox.exec_()
if ret == QMessageBox.Yes:
event.accept()
else:
event.ignore()
if __name__ == '__main__':
app = QApplication([])
file = QFile("custom_widget_modified.ui")
file.open(QFile.ReadOnly)
loader = QUiLoader()
loader.registerCustomWidget(MyMainWindow)
main_window = loader.load(file)
main_window.show()
sys.exit(app.exec_())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With