Qt可视化界面编译与调用

        上一章我们实现了简易加法器,通过Qt Designer设计界面,然后通过信号与槽完成了“计算”功能。这一章我们来研究下使用Qt Designer设计的界面是如何被Qt Creator编译,又如何被主程序所调用的。

        我们看下上一章使用Qt Designer设计的界面是什么样的:

4211c916e05e4666876c414e44d7f02f.png

        从这个布局图可以看出,这个界面里由7个标签、2个数字输入框、1个按钮、1个水平占位符、4个垂直布局器、2个水平布局器和中心控件组成,下面列出界面中所有的控件及其名称:

        7个标签为:“简易加法器”label_6、“加数1”label_1、“加数2”label_2、“结果”label_3、“+”label_4、“=”label_5、存放结果的标签(带边框)resultLabel;

        2个数字输入框:加数1数字输入框add1SpinBox、加数2数字输入框add2SpinBox;

        1个按钮:“计算”按钮calculatePushButton;

        1个水平占位符:图中在计算按钮左侧的蓝色像弹簧的长条horizontalSpacer;

        4个垂直布局器:加数1垂直布局器verticalLayout_1、加数2垂直布局器verticalLayout_2、结果垂直布局器verticalLayout_3,还有个别忘了,用于整个界面垂直布局的布局器verticalLayout_4;

        2个水平布局器:用于输入数字和结果的那一行的水平布局器horizontalLayout_1、用于水平占位符和计算按钮那一行的水平布局器horizontalLayout_2;

        中心控件:界面中用于盛放所有控件的中心控件centralwidget。

        我们想研究Qt Creator是如何将这些控件编译成代码的,在这之前,我们先看看Qt C++的应用是如何启动的,窗口是如何运行起来的。我们先看下Qt C++应用的入口函数main()函数所在的main.cpp中的代码:

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

         main.cpp中的代码很简单,只有短短几行,先是包含了2个头文件,一个是主窗口MainWindow类的头文件"mainwindow.h",一个是Qt的应用头文件<QApplication>。

        接下来就是Qt C++程序的入口函数main()函数,在main()函数中,只有4行代码,第一行定义了一个QApplication类的对象a,并在构造函数中传入了main()函数的实参;第二行定义了一个主窗口类MainWindow的对象w;第三行执行了w的show()函数,作用是将主窗口显示在屏幕上;第四行main()函数返回了a.exec(),这句代码的作用是执行QApplication应用程序,将程序的控制权交给了Qt。

        QApplication是Qt核的类,我们不必深究,我们只需要看下MainWindow这个类,以下是mainwindow.cpp中的代码:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_calculatePushButton_clicked()
{
    // 获取加数1的值
    int add1 = ui->add1SpinBox->value();
    // 获取加数2的值
    int add2 = ui->add2SpinBox->value();
    // 计算和
    int result = add1 + add2;
    // 将计算和显示出来
    ui->resultLabel->setText(QString::number(result));
}

        在mainwindow.cpp代码中,包含了两个头文件,一个是mainwindow.h,是MainWindow类的头文件;一个是ui_mainwindow.h,这个就是界面编译的文件,Qt Creator将界面翻译成代码存放在ui_mainwindow.h头文件中。下面我们看下ui_mainwindow.h中的内容:

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

#ifndef UI_MAINWINDOW_H
#define UI_MAINWINDOW_H

#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QSpacerItem>
#include <QtWidgets/QSpinBox>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_MainWindow
{
public:
    QWidget *centralwidget;
    QVBoxLayout *verticalLayout_4;
    QLabel *label_6;
    QHBoxLayout *horizontalLayout_1;
    QVBoxLayout *verticalLayout_1;
    QLabel *label_1;
    QSpinBox *add1SpinBox;
    QLabel *label_4;
    QVBoxLayout *verticalLayout_2;
    QLabel *label_2;
    QSpinBox *add2SpinBox;
    QLabel *label_5;
    QVBoxLayout *verticalLayout_3;
    QLabel *label_3;
    QLabel *resultLabel;
    QHBoxLayout *horizontalLayout_2;
    QSpacerItem *horizontalSpacer;
    QPushButton *calculatePushButton;

    void setupUi(QMainWindow *MainWindow)
    {
        if (MainWindow->objectName().isEmpty())
            MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
        MainWindow->resize(291, 187);
        centralwidget = new QWidget(MainWindow);
        centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
        verticalLayout_4 = new QVBoxLayout(centralwidget);
        verticalLayout_4->setObjectName(QString::fromUtf8("verticalLayout_4"));
        label_6 = new QLabel(centralwidget);
        label_6->setObjectName(QString::fromUtf8("label_6"));
        QFont font;
        font.setPointSize(12);
        font.setBold(true);
        font.setWeight(75);
        label_6->setFont(font);
        label_6->setAlignment(Qt::AlignCenter);

        verticalLayout_4->addWidget(label_6);

        horizontalLayout_1 = new QHBoxLayout();
        horizontalLayout_1->setObjectName(QString::fromUtf8("horizontalLayout_1"));
        verticalLayout_1 = new QVBoxLayout();
        verticalLayout_1->setObjectName(QString::fromUtf8("verticalLayout_1"));
        label_1 = new QLabel(centralwidget);
        label_1->setObjectName(QString::fromUtf8("label_1"));
        label_1->setAlignment(Qt::AlignCenter);

        verticalLayout_1->addWidget(label_1);

        add1SpinBox = new QSpinBox(centralwidget);
        add1SpinBox->setObjectName(QString::fromUtf8("add1SpinBox"));

        verticalLayout_1->addWidget(add1SpinBox);


        horizontalLayout_1->addLayout(verticalLayout_1);

        label_4 = new QLabel(centralwidget);
        label_4->setObjectName(QString::fromUtf8("label_4"));
        QFont font1;
        font1.setPointSize(16);
        label_4->setFont(font1);
        label_4->setAlignment(Qt::AlignCenter);

        horizontalLayout_1->addWidget(label_4);

        verticalLayout_2 = new QVBoxLayout();
        verticalLayout_2->setObjectName(QString::fromUtf8("verticalLayout_2"));
        label_2 = new QLabel(centralwidget);
        label_2->setObjectName(QString::fromUtf8("label_2"));
        label_2->setAlignment(Qt::AlignCenter);

        verticalLayout_2->addWidget(label_2);

        add2SpinBox = new QSpinBox(centralwidget);
        add2SpinBox->setObjectName(QString::fromUtf8("add2SpinBox"));

        verticalLayout_2->addWidget(add2SpinBox);


        horizontalLayout_1->addLayout(verticalLayout_2);

        label_5 = new QLabel(centralwidget);
        label_5->setObjectName(QString::fromUtf8("label_5"));
        label_5->setFont(font1);
        label_5->setAlignment(Qt::AlignCenter);

        horizontalLayout_1->addWidget(label_5);

        verticalLayout_3 = new QVBoxLayout();
        verticalLayout_3->setObjectName(QString::fromUtf8("verticalLayout_3"));
        label_3 = new QLabel(centralwidget);
        label_3->setObjectName(QString::fromUtf8("label_3"));
        label_3->setAlignment(Qt::AlignCenter);

        verticalLayout_3->addWidget(label_3);

        resultLabel = new QLabel(centralwidget);
        resultLabel->setObjectName(QString::fromUtf8("resultLabel"));
        resultLabel->setFrameShape(QFrame::Box);
        resultLabel->setFrameShadow(QFrame::Sunken);
        resultLabel->setLineWidth(1);
        resultLabel->setMidLineWidth(0);
        resultLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
        resultLabel->setMargin(0);
        resultLabel->setOpenExternalLinks(false);

        verticalLayout_3->addWidget(resultLabel);


        horizontalLayout_1->addLayout(verticalLayout_3);

        horizontalLayout_1->setStretch(0, 10);
        horizontalLayout_1->setStretch(2, 10);
        horizontalLayout_1->setStretch(4, 10);

        verticalLayout_4->addLayout(horizontalLayout_1);

        horizontalLayout_2 = new QHBoxLayout();
        horizontalLayout_2->setObjectName(QString::fromUtf8("horizontalLayout_2"));
        horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);

        horizontalLayout_2->addItem(horizontalSpacer);

        calculatePushButton = new QPushButton(centralwidget);
        calculatePushButton->setObjectName(QString::fromUtf8("calculatePushButton"));

        horizontalLayout_2->addWidget(calculatePushButton);


        verticalLayout_4->addLayout(horizontalLayout_2);

        MainWindow->setCentralWidget(centralwidget);

        retranslateUi(MainWindow);

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

    void retranslateUi(QMainWindow *MainWindow)
    {
        MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "MainWindow", nullptr));
        label_6->setText(QCoreApplication::translate("MainWindow", "\347\256\200\346\230\223\345\212\240\346\263\225\345\231\250", nullptr));
        label_1->setText(QCoreApplication::translate("MainWindow", "\345\212\240\346\225\2601", nullptr));
        label_4->setText(QCoreApplication::translate("MainWindow", "+", nullptr));
        label_2->setText(QCoreApplication::translate("MainWindow", "\345\212\240\346\225\2602", nullptr));
        label_5->setText(QCoreApplication::translate("MainWindow", "=", nullptr));
        label_3->setText(QCoreApplication::translate("MainWindow", "\347\273\223\346\236\234", nullptr));
        resultLabel->setText(QCoreApplication::translate("MainWindow", "0", nullptr));
        calculatePushButton->setText(QCoreApplication::translate("MainWindow", "\350\256\241\347\256\227", nullptr));
    } // retranslateUi

};

namespace Ui {
    class MainWindow: public Ui_MainWindow {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_MAINWINDOW_H

        我们看到这个文件的顶部注释,意思是:这个文件由界面文件'mainwindow.ui'生成,被Qt用户接口编译版本5.14.2创建。警告!对这个文件的所有改动都将在编译界面文件时丢失。

        也就是说,这个文件是Qt根据界面自动生成的,用户不需要更改。我们看下这个文件有哪些代码:首先,文件头部包含了所用到的头文件,后定义了一个类Ui_MainWindow,在这个类里,定义了一些公有指针变量,这些变量就是我们上面列举的界面上的控件,名称就是设置的objectName,之后定义了一个公有函数void  setupUi(QMainWindow *MainWindow),在这个函数中实例了各个控件对象,设置了各个控件的属性以及界面的布局,是不是很像我们手动撸的代码,但因为是机器自动生成的,显得有些机械化而已,但作用是一样的。其中有条语句关注一下,是QMetaObject::connectSlotsByName(MainWindow);这条语句的作用是搜索MainWindow界面上的所有控件,将信号与槽函数匹配的信号和槽关联起来,用作名为 void 类名::on_控件名_信号();这种槽函数的声明,使得控件的信号可以直接关联到这样的槽函数中。

        除了setupUi函数外,这个类里还定义了一个void retranslateUi(QMainWindow *MainWindow)函数,这个函数用来设置界面各组件的文字内容属性,如窗口的标题、标签的文字、按钮的文字,将界面上的文字设置的内容独立出来作为一个函数,在设计多语言界面时会被用到。

        在函数的最后,有以下语句:

namespace Ui {
    class MainWindow: public Ui_MainWindow {};

这几条语句定义了命名空间Ui,并定义了一个继承Ui_MainWindow的类MainWindow。这个操作的意义我们等会会讲到。

        接下来我们看看mainwindow.h中的代码:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

private slots:
    void on_calculatePushButton_clicked();
};
#endif // MAINWINDOW_H

        mainwindow.h文件中,先是包含了<QMainWindow>头文件,后声明了命名空间namespace Ui { class MainWindow; },注意,这里的class MainWindow是命名空间Ui中的类,Ui命名空间和其下的MainWindow在ui_mainwindow.h文件中定义,这里只是拿过来显性地声明下,在mainwindow.h文件中定义的MainWindow类中用到。

        我们看下mainwindow.h文件中定义的MainWindow类包含哪些东西,首先,这个类继承自QMainWindow,类的内部有个Q_OBJECT宏定义,在声明信号与槽之前,必须使用宏定义Q_OBJECT。类中声明了构造函数和析构函数,是公有函数。然后定义了一个私有指针变量Ui::MainWindow *ui;我们仔细分析下这个指针变量,ui这个指针变量的类型是Ui::MainWindow,就是ui_mainwindow.h文件中定义的MainWindow,所以这个ui指针变量可以访问到ui_mainwindow.h文件中定义的MainWindow内部的所有的公有变量和函数,即可以通过ui这个指针变量访问到界面上的所有控件。

        ui_mainwindow.h文件中实现界面功能的类是Ui_MainWindow,再定义一个类MainWindow继承Ui_MainWindow,并定义在namespace Ui里,这样Ui::MainWindow与mainwindow.h文件中的类MainWindow同名,但是用namespace区分开来。所以,界面的Ui::MainWindow类与文件mainwindow.h中定义的MainWindow类实际上是两个类,但是Qt的处理让用户感觉不到Ui::MainWindow类的存在,只需要知道在MainWindow类里用ui指针可以访问界面的控件就行了。是不是感觉Qt这样的设计非常的巧妙,行云流水!

        mainwindow.h中MainWindow类的最后部分声明的公有槽函数void on_calculatePushButton_clicked();这个是“计算”按钮calculatePushButton的点击信号clicked链接的槽函数,之所以这个槽函数能够被“计算”按钮的点击信号触发,完全是因为在ui_mainwindow.h文件中Ui_MainWindow类里的一条语句QMetaObject::connectSlotsByName(MainWindow)的作用。

        讲完了mainwindow.h,我们再回过头来看看mainwindow.cpp中的内容。在MainWindow的构造函数中,使用new Ui::MainWindow实例化了ui指针变量,开辟了空间,ui也不再是空指针了。在构造函数中,使用ui->setupUi(this);初始化了界面。在MainWindow的析构函数中,将ui指针释放掉。在mainwindow.cpp中定义了槽函数void on_calculatePushButton_clicked(),实现简易加法器计算功能。

        到此,简易加法器的代码框架我们就讲完了,小伙伴们清晰了吗,如果还不清晰,对着代码看几遍就懂了。

猜你喜欢

转载自blog.csdn.net/weixin_47488212/article/details/130210198
今日推荐