简体   繁体   English

将 .ui 文件中的小部件加载到由单独的.ui 文件定义的 window 中

[英]Loading a widget from a .ui file into a window defined by a separate .ui file

My overall goal here is to define have a main app with multiple important widgets, where each widget is defined with a separate python class and loaded from a separate.ui file.我在这里的总体目标是定义一个包含多个重要小部件的主应用程序,其中每个小部件都使用单独的 python class 定义并从单独的.ui 文件加载。

I can't seem to get widget UI to show up when I start the main app when the widget module loads its.ui file directly.当小部件模块直接加载 its.ui 文件时,当我启动主应用程序时,我似乎无法显示小部件 UI。

What I am hoping to see is this (Figure 1)我希望看到的是这个(图1)

在此处输入图像描述

What I seeing is this (Figure 2)我看到的是这个(图2)

在此处输入图像描述

Here is the code that generates Figure 2. It is excerpted from HETP_main.py, which is at the end.下面是生成图 2 的代码。它摘自 HETP_main.py,位于末尾。

QtWidgets.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.topPanelWDG = TopPanel()   # set up behaviors for the top panel
self.setupUi(self)

The following code will generate Figure 1 (what I want).以下代码将生成图 1(我想要的)。 However it constructs the main window in python code rather than loading it from a.ui file.然而,它在 python 代码中构造主要的 window ,而不是从 a.ui 文件中加载它。 This code was generated using pyuic5 but I had to remove the commented-out lines to get it to work.此代码是使用 pyuic5 生成的,但我必须删除注释掉的行才能使其正常工作。

MainWindow.setObjectName("MainWindow")
MainWindow.resize(649, 130)
MainWindow.setStyleSheet("")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setStyleSheet("QPushButton {background-color: rgb(239, 239, 239)};")
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName("verticalLayout")
# self.topPanelWDG = QtWidgets.QWidget(self.centralwidget)     # removed
# self.topPanelWDG.setMinimumSize(QtCore.QSize(0, 75))         # removed
# self.topPanelWDG.setMaximumSize(QtCore.QSize(16777215, 75))  # removed
# self.topPanelWDG.setStyleSheet("background-color: yellow")   # removed
# self.topPanelWDG.setObjectName("topPanelWDG")                # removed
self.verticalLayout.addWidget(self.topPanelWDG)
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)

# self.retranslateUi(MainWindow)                               # removed
# QtCore.QMetaObject.connectSlotsByName(MainWindow)            # removed

It's pretty clear that I am somehow doubly defining topPanelWDG, but I don't see how or how to fix it.很明显,我以某种方式双重定义了 topPanelWDG,但我不知道如何或如何修复它。 So my question is how do I get Figure 1 while loading from a.ui file.所以我的问题是如何在从 a.ui 文件加载时获得图 1。

The full.py and.ui files used here are attached.此处使用的 full.py 和 .ui 文件附后。

The following is the full contents of the.py and the two.ui files.以下是.py和两个.ui文件的全部内容。

HETP_main.py HETP_main.py

```from PyQt5 import QtCore, QtGui, QtWidgets, uic
import sys

Ui_MainWindow, QtBaseClass = uic.loadUiType("main.ui")


class TopPanel(QtWidgets.QWidget):
    def __init__(self):
        super(TopPanel, self).__init__()
        uic.loadUi("toppanel.ui", self)


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        Ui_MainWindow.__init__(self)
        construct_method = 'load'
        construct_method = 'build2'
        if construct_method == 'load':
            self.topPanelWDG = TopPanel()   # set up behaviors for the top panel
            self.setupUi(self)
        else:
            self.topPanelWDG = TopPanel()   # set up behaviors for the top panel
            self.construct2(self)

        self.setWindowTitle(construct_method)

    def construct2(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(649, 130)
        MainWindow.setStyleSheet("")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setStyleSheet("QPushButton {background-color: rgb(239, 239, 239)};")
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        # self.topPanelWDG = QtWidgets.QWidget(self.centralwidget)     # removed
        # self.topPanelWDG.setMinimumSize(QtCore.QSize(0, 75))         # removed
        # self.topPanelWDG.setMaximumSize(QtCore.QSize(16777215, 75))  # removed
        # self.topPanelWDG.setStyleSheet("background-color: yellow")   # removed
        # self.topPanelWDG.setObjectName("topPanelWDG")                # removed
        self.verticalLayout.addWidget(self.topPanelWDG)
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        # self.retranslateUi(MainWindow)                               # removed
        # QtCore.QMetaObject.connectSlotsByName(MainWindow)            # removed


    # def retranslateUi(self, MainWindow):                                               # removed
    #     _translate = QtCore.QCoreApplication.translate                                 # removed
    #     MainWindow.setWindowTitle(_translate("MainWindow", "HETP Scanning System"))    # removed

app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()  # should pass command line kwargs?```

toppanel.ui顶部面板.ui

```<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>654</width>
    <height>72</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QHBoxLayout" name="horizontalLayout_2">
   <item>
    <widget class="QFrame" name="vanFrame">
     <property name="sizePolicy">
      <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
       <horstretch>0</horstretch>
       <verstretch>0</verstretch>
      </sizepolicy>
     </property>
     <property name="minimumSize">
      <size>
       <width>0</width>
       <height>48</height>
      </size>
     </property>
     <property name="maximumSize">
      <size>
       <width>16777215</width>
       <height>64</height>
      </size>
     </property>
     <property name="styleSheet">
      <string notr="true">
QWidget {background-color: rgb(167, 255, 195)}
.QPushButton {
    background-color: LightGray
    opacity 0.2;
    font: bold;
};</string>
     </property>
     <property name="frameShape">
      <enum>QFrame::Panel</enum>
     </property>
     <property name="lineWidth">
      <number>0</number>
     </property>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <widget class="QLabel" name="label">
        <property name="text">
         <string>Top Panel</string>
        </property>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>```

main.ui主界面

I had difficulty including this in the question so it is available here.我很难在问题中包含这个,所以它可以在这里找到。 main.ui主界面

There are two possible solutions to your issue.您的问题有两种可能的解决方案。

The easiest way is to remove the topPanelWDG from the main.ui , and manually add it from code.最简单的方法是从main.ui中删除topPanelWDG ,然后从代码中手动添加它。

from PyQt5 import QtWidgets, uic

# the following line is not necessary, as we can use loadUi in the same way for
# both the widget *and* the main window
# Ui_MainWindow, QtBaseClass = uic.loadUiType("main.ui")


class TopPanel(QtWidgets.QWidget):
    def __init__(self):
        super(TopPanel, self).__init__()
        uic.loadUi("toppanel.ui", self)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        uic.loadUi("main.ui", self)
        self.topPanelWDG = TopPanel()
        self.verticalLayout.addWidget(self.topPanelWDG)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    # there's no need to pass args here, usually they're important only to the app
    sys.exit(app.exec_())

Sometimes, though, you can't easily (or don't want to) add a widget to a layout of an UI created from designer from your code.但是,有时您不能轻松(或不想)将小部件添加到由设计器从您的代码创建的 UI 布局中。
An alternative approach is to use what Qt calls "promoted widgets".另一种方法是使用 Qt 所谓的“升级小部件”。

This works by adding a widget to the ui that will be used as an advanced "placeholder" for the actual widget class you're implementing.这通过向 ui 中添加一个小部件来工作,该小部件将用作您正在实现的实际小部件 class 的高级“占位符”。 In this specific situation, it will be a standard QWidget, but if you're subclassing other widgets (buttons, tables, etc) you'll use them, since it will allows you to set their base properties (a label for the button, the headers of the table, etc) directly from Designer, leaving the class code to do only do what you need to implement.在这种特定情况下,它将是标准 QWidget,但如果您将其他小部件(按钮、表格等)子类化,您将使用它们,因为它允许您设置它们的基本属性(按钮的 label,表的标题等)直接来自 Designer,让 class 代码只做您需要实现的事情。

In your case, leave the topPanelWDG in the main ui file, right click it and select "Promote to".在您的情况下,将topPanelWDG保留在主 ui 文件中,右键单击它并 select “提升到”。 In the "Promoted class name" insert the class name that will be used ( TopPanel ), and in the "Header file" field write the file name (including the relative path, if it is in a subdirectory) of the python file that contains the TopPanel class definition, without the py extension.在“Promoted class 名称”中插入将要使用的class 名称TopPanel ),并在“头文件”字段中写入包含 Z23EEEB4347BDD26BDFC6B75 文件的 Z23EEEB4347BDD26BDFC6B75 文件的文件名(如果它位于子目录中,则包括相对路径) TopPanel class 定义,没有py扩展名。

推广小部件

Finally, click "Add" and then "Promote", and save the file.最后,单击“添加”,然后单击“升级”,然后保存文件。

At this point you only need to add the *args and **kwargs arguments to the custom widget initialization.此时您只需将 *args 和 **kwargs arguments 添加到自定义小部件初始化中。 These are necessary as now it will be Qt's responsibility to create the widget and, since all widgets accept at least an argument (the parent) those arguments will be added when Qt creates them: if the __init__ function does not accept those arguments, python will raise a TypeError exception because it's receiving unexpected arguments. These are necessary as now it will be Qt's responsibility to create the widget and, since all widgets accept at least an argument (the parent) those arguments will be added when Qt creates them: if the __init__ function does not accept those arguments, python will引发TypeError异常,因为它收到意外的 arguments。

from PyQt5 import QtWidgets, uic
import sys

class TopPanel(QtWidgets.QWidget):
    def __init__(self, *args, **kwargs):
        super(TopPanel, self).__init__(*args, **kwargs)
        uic.loadUi("toppanel.ui", self)

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        uic.loadUi("main.ui", self)

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

It's also usually a good habit to use separated files for custom widgets, as their code will be loaded every time a promoted widget is added.为自定义小部件使用单独的文件通常也是一个好习惯,因为每次添加提升的小部件时都会加载它们的代码。
This also means that the portion of code that will actually run your program has to be enclosed in the if __name__ == "__main__": statement, if the custom widget class is in that file;这也意味着,如果自定义小部件 class 在该文件中,则必须将实际运行程序的代码部分包含在if __name__ == "__main__":语句中; this is a good habit in any case, as whenever a file is imported it's "main indentation code" is always run.无论如何,这是一个好习惯,因为每当导入文件时,它的“主缩进代码”总是运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM