首页 > 解决方案 > 在 Qt Designer 中使用自定义 PySide2 小部件

问题描述

我正在寻找一种在 Qt Designer 中有效地使用 Qt for Python (PySide2) 编写的自定义小部件的方法。

我发现,可以使用基本小部件设计 GUI,然后只需将类换成 UI 文件中的自定义小部件并通知QUiLoader子类loader.registerCustomWidget(MyMainWindow),然后在 Qt Designer 中再次打开它不起作用那好吧。

我在PyQt 的类似问题中读到,您必须为自定义小部件编写插件。PySide2 是否也存在这种可能性?

一些示例代码:

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

使用此版本,应用程序将毫无疑问地关闭。

<?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

在此版本中,系统会询问您是否真的要退出。

<?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>

<customwidgets>段在再次打开后由 Qt 设计器添加。

修改后 Qt Designer 没有正确放置三个标签。

标签: pythonqt-designerpyside2

解决方案


在 Qt Designer 中使用自定义小部件有两种主要方法:

1. 推广小部件:

这是最简单、最省力的方法,如果它是一个内部小部件,您只需右键单击该小部件并选择Promote To ...,然后在:

  • 基类名称选择您继承的类
  • 提升类名称放置类的名称
  • 头文件放置将扩展名 .py 更改为 .h 的文件的路径

在此处输入图像描述

以上所做的是生成以下内容:

...
    <widget class="RadialBar" name="widget" native="true"/>
...
 <customwidgets>
  <customwidget>
   <class>RadialBar</class>
   <extends>QWidget</extends>
   <header>radialbar.h</header>
   <container>1</container>
  </customwidget>
 </customwidgets>
...

即更改类,在customwidget中添加一个字段指向类的名称:<class> NAME_OF_CLASS </class>,它们继承的类的名称<extends>CLASS_EXTENDS</extends>和文件路径将文件扩展名从.py更改为.h <header>PATH_OF_FILE.h</header>,即与您输入的数据相同表格。

在根小部件的情况下,它不能通过 Qt Designer 完成,但我看到你已经理解了逻辑,但你没有正确修改所有内容,你的主要错误是指出它们继承的类

 <customwidgets>
  <customwidget>
   <class>MyMainWindow</class>
   <extends>QMainWindow</extends> <----
   <header>mymainwindow.h</header>
   <container>1</container>
  </customwidget>
 </customwidgets>

所以正确的文件是:

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>

我的主窗口.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_())

2. 推广小部件:


推荐阅读