QC

以一个长方形计算器为案例。

Ui_Widget (容器/蓝图)
    ├── 控件指针 (QPushButton*, QLabel*, ...)
    ├── setupUi() - 创建控件、设置参数、布局
    └── retranslateUi() - 设置文本翻译

::Widget (窗口)
    ├── 继承 QWidget (真正的窗口)
    ├── ui 指针 (指向 Ui::Widget)
    ├── 构造函数中调用 ui->setupUi(this) 来构建界面
    └── 实现信号槽,处理业务逻辑
Widget (this) ← QWidget 对象
    ├── ui (Ui::Widget) ← 普通 C++ 对象,不是 QObject
    │    ├── commitPushButton (QPushButton*) ← 指向下面按钮的指针
    │    ├── label (QLabel*) ← 指向下面标签的指针
    │    └── lineEdit (QLineEdit*) ← 指向下面输入框的指针
    │
    ├── commitPushButton (QPushButton) ← 真实的按钮,父对象是 Widget
    ├── label (QLabel) ← 真实的标签,父对象是 Widget
    └── lineEdit (QLineEdit) ← 真实的输入框,父对象是 Widget
// main.cpp
#include "widget.h"     // widget.h
#include <QApplication> // Qt应用程序类

int main(int argc, char *argv[])
{
    QApplication a(argc, argv); // 创建Qt应用程序对象
    Widget w;           // 创建主窗口,来自::Widget类
    w.show();           // 显示窗口
    return a.exec();    // 进入事件循环
}
// main.cpp 负责创建 a对象来循环运行窗口,监听事件等等
// 同时创建w对象,即程序的窗口,并将其显示出来。
// widget.h
#ifndef WIDGET_H    // 头文件保护符,避免被编辑器多次包含
#define WIDGET_H    // 宏规范要求把头文件字母转换成大写,'.'号转换成'_'

#include <QWidget>  // Qt窗口类,提供了窗口的基本功能:显示、隐藏、大小、位置、事件处理等


QT_BEGIN_NAMESPACE  // QT_BEGIN_NAMESPACE 是Qt的宏,用于处理不同编译器对命名空间的支持
namespace Ui {      // Qt自动生成UI命名空间
class Widget;       // 前置声明UI类 Ui::Widget
}                   // 此类的正式声明位置在ui_widget.h中
QT_END_NAMESPACE


class Widget : public QWidget   // 创建一个继承自QWidget的::Widget类,窗口类
{
    Q_OBJECT        // Qt元对象系统宏,启用信号槽等功能


public:
    Widget(QWidget *parent = nullptr);  // 构造函数,parent:父窗口指针,默认nullptr
    ~Widget();                          // 析构函数


private slots:      // 私有槽函数部分,自动连接命名规则:on_对象名_信号名()
    void on_commitPushButton_clicked(); // 提交按钮点击事件处理


private:
    Ui::Widget *ui; // 私有成员变量,指向UI类的指针
};


#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include "./ui_widget.h"


Widget::Widget(QWidget *parent) //parent 参数传递给基类,建立对象树关系
                                //如果 parent 为 nullptr,则窗口为顶层窗口
    : QWidget(parent)       // :后跟初始化列表,各项用,分开。此项为调用基类构造函数
    , ui(new Ui::Widget)    // 此项为将ui指针初始化为新创建的UI对象
{
    ui->setupUi(this);      // 初始化UI界面:把::Widget类的对象的地址,即this指针,
                            // 放入ui指向的Ui::Widget类的对象的setupUi()方法中

    // 设置样式属性,允许背景样式
    this->setAttribute(Qt::WA_StyledBackground, true);
    //创建所有真实控件
    //设置控件的属性、布局
    //填充 ui 对象中的指针,指向这些控件
    //将 this(你的窗口)作为控件的父对象

    // 设置样式表
    this->setStyleSheet(
        "Widget {"
        "background-image: url(:/1.jpg);"   // 设置背景图片
        "background-repeat: no-repeat;"     // 不平铺
        "background-position: center;"      // 居中显示
        "}"
    );
}

Widget::~Widget()   //析构函数
{
    delete ui;      // 释放UI对象内存
}

// 手动连接(需要在构造函数中写代码)
//connect(ui->commitPushButton, &QPushButton::clicked,
//      this, &Widget::onCommitButtonClicked);

void Widget::on_commitPushButton_clicked()      // ::Widget的函数,写在类外面,更清晰
{
    // 读取输入内容(QString)
    QString lenStr = ui->lengthLineEdit->text();
    QString widStr = ui->widthLineEdit->text();

    // 转成 double
    double length = lenStr.toDouble();
    double width  = widStr.toDouble();

    // 计算面积与周长
    double area = length * width;
    double perimeter = 2 * (length + width);

    // 拼接显示字符串
    QString result = QString("周长:%1    面积:%2")
                         .arg(perimeter)
                         .arg(area);

    // 显示
    ui->resultLineEdit->setText(result);
}

//ui_widget.h
/********************************************************************************
** Form generated from reading UI file 'widget.ui'
**
** Created by: Qt User Interface Compiler version 6.10.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_WIDGET_H
#define UI_WIDGET_H

#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_Widget     // 全局作用域 ::Ui_Widget
{                   // 只是一个指针容器,存储指向这些控件的指针
public:
    QLineEdit *lengthLineEdit;
    QLineEdit *widthLineEdit;
    QLineEdit *resultLineEdit;
    QPushButton *commitPushButton;
    QLabel *label;

    void setupUi(QWidget *Widget)
    {
        if (Widget->objectName().isEmpty())
            Widget->setObjectName("Widget");
        Widget->resize(264, 364);
        lengthLineEdit = new QLineEdit(Widget);
        lengthLineEdit->setObjectName("lengthLineEdit");
        lengthLineEdit->setEnabled(true);
        lengthLineEdit->setGeometry(QRect(30, 90, 200, 30));
        lengthLineEdit->setMinimumSize(QSize(200, 30));
        lengthLineEdit->setMaximumSize(QSize(200, 30));
        lengthLineEdit->setCursor(QCursor(Qt::CursorShape::IBeamCursor));
        lengthLineEdit->setCursorPosition(3);
        widthLineEdit = new QLineEdit(Widget);
        widthLineEdit->setObjectName("widthLineEdit");
        widthLineEdit->setGeometry(QRect(30, 150, 200, 30));
        widthLineEdit->setMinimumSize(QSize(200, 30));
        widthLineEdit->setMaximumSize(QSize(200, 30));
        resultLineEdit = new QLineEdit(Widget);
        resultLineEdit->setObjectName("resultLineEdit");
        resultLineEdit->setGeometry(QRect(30, 210, 200, 30));
        resultLineEdit->setMinimumSize(QSize(200, 30));
        resultLineEdit->setMaximumSize(QSize(200, 30));
        commitPushButton = new QPushButton(Widget);
        commitPushButton->setObjectName("commitPushButton");
        commitPushButton->setGeometry(QRect(150, 270, 80, 30));
        commitPushButton->setMinimumSize(QSize(80, 30));
        commitPushButton->setMaximumSize(QSize(80, 30));
        label = new QLabel(Widget);
        label->setObjectName("label");
        label->setGeometry(QRect(90, 40, 101, 31));

        retranslateUi(Widget);

        QMetaObject::connectSlotsByName(Widget);
    } // setupUi

    void retranslateUi(QWidget *Widget)
    {
        Widget->setWindowTitle(QCoreApplication::translate("Widget", "Widget", nullptr));
        lengthLineEdit->setText(QCoreApplication::translate("Widget", "\350\276\223\345\205\245\351\225\277", nullptr));
        widthLineEdit->setText(QCoreApplication::translate("Widget", "\350\276\223\345\205\245\345\256\275", nullptr));
        resultLineEdit->setText(QCoreApplication::translate("Widget", "\346\230\276\347\244\272\347\273\223\346\236\234", nullptr));
        commitPushButton->setText(QCoreApplication::translate("Widget", "\350\256\241\347\256\227", nullptr));
        label->setText(QCoreApplication::translate("Widget", "<html><head/><body><p align=\"center\"><span style=\" font-size:16pt;\">\347\237\251\345\275\242\350\256\241\347\256\227</span></p></body></html>", nullptr));
    } // retranslateUi

};

namespace Ui {
    class Widget: public Ui_Widget {};  // Ui::Widget 是Ui_Widget子类
} // namespace Ui                       // Ui::Widget 是Ui_Widget的套壳封装

QT_END_NAMESPACE

#endif // UI_WIDGET_H
1. 程序启动,创建 Widget 对象
   ↓
2. Widget 构造函数执行
   ├── 调用 QWidget 构造函数
   ├── new Ui::Widget 创建 ui 容器
   ├── ui->setupUi(this) 创建所有控件
   └── 设置样式表
   ↓
3. 窗口显示,用户输入长和宽
   ↓
4. 用户点击"计算"按钮
   ↓
5. Qt 自动调用 on_commitPushButton_clicked()
   ↓
6. 槽函数执行
   ├── 读取输入值
   ├── 计算面积和周长
   └── 显示结果
   ↓
7. 关闭窗口
   ↓
8. ~Widget() 析构函数执行
   ├── delete ui
   └── 基类析构,自动删除所有控件
滚动至顶部