斗破QT编程入门系列之四:GUI应用程序设计基础:UI文件设计原理与运行机制(五星斗师)
斗破Qt目录:
斗破Qt编程入门系列之前言:认识Qt:Qt的获取与安装(四星斗师)
斗破QT编程入门系列之一:认识Qt:初步使用(四星斗师)
斗破QT编程入门系列之二:认识Qt:编写一个HelloWorld程序(四星斗师)
斗破QT编程入门系列之三:GUI应用程序设计基础:UI文件(四星斗师)
斗破QT编程入门系列之四:GUI应用程序设计基础:UI文件设计原理与运行机制(五星斗师)
参考书籍
《Qt5.9 C++开发指南》
斗破观看顺序:
https://v.haohuitao.cc/yhplay/336-1-2.html
第一季☞第二季前2集☞特别篇1☞第二季3~12集☞特别篇2沙之澜歌☞第三季☞第四季☞三年之约☞缘起☞年番
斗气大陆等级:
斗气有十一个阶别,斗之气,斗者,斗师,大斗师,斗灵,斗王,斗皇,斗宗,斗尊,斗圣,斗帝。
斗气大陆上很久没有出现过斗帝了。
一、参加炼药师竞赛
萧炎控制异火进入纳兰桀体内,为其祛除烙毒,而毒素却意外凶猛与顽固,并不能一次将其彻底清除。诡异的毒素也悄悄跟随着异火侵入了萧炎的体内,修炼时萧炎意外的发现,烙毒中蕴含着大量精纯的能量,一举使自己能力大增。
萧炎来到炼药师集会,想在这里看看能不能找到自己需要的药材,并在这里遇到了黑岩城的琳菲,并同时解救被商家欺负的雪魅。并在会长弗兰克和奥托大师的邀请下参加了加玛帝国的炼药师比赛,当然萧炎也是为了能获得恢复灵魂力量的药方–融灵丹,而美杜莎女王也急需这种物品来恢复自己的蛇人之身,命令萧炎一定要拿到此物。
二、UI文件设计原理与运行机制
为了搞清楚窗体类的定义,以及界面功能的实现原理,这里将项目进行编译。编译后在项目目录下会自动生成一个文件ui_widget.h,这样对于一个窗体,就有4个文件了,各个文件的功能说明如下:
下面分别分析各个文件的内容及其功能,以及它们是如何联系在一起工作的,实现界面的创建与显示的。
- widget.h文件
widget.h 文件是窗体类的头文件。在创建项目时,选择窗体基类是QWidget,在widget.h中定义了一个继承自QWidget的类Widget,下面是widget.h文件的内容。
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:Ui::Widget *ui;
};
#endif // WIDGET_H
widget.h文件有几个重要的部分。
(1)namespace声明
代码中有如下的一个namespace声明:
namespace Ui { class Widget; }
这是声明了一个名称为Ui的命名空间(namespace),包含一个类Widget。但是这个类Widget并不是本文件里定义的类Widget,而是ui_widget.h文件里定义的类,用于描述界面组件的。这个声明相当于一个外部类型声明(具体要看完ui_widget.h文件内的解释之后才能搞明白)。
(2)Widget类的定义。widget.h文件的主体部分是一个继承于QWidget的类Widget的定义,也就是本实例的窗体类。
在Widget类中使用了宏Q_OBJECT,这是使用Qt的信号与槽(signal和slot)机制的类都必须加入的一个宏(信号与槽在后面详细介绍)。
在public部分定义了Widget类的构造函数和析构函数。
在private部分又定义了一个指针。
Ui::Widget *ui;
这个指针是用前面声明的namespace Ui里的Widget类定义的,所以指针ui是指向可视化设计的界面,后面会看到要访问界面上的组件,都需要通过这个指针ui。
2.widget.cpp文件
widget.cpp文件是类Widget的实现代码,下面是widget.cpp文件的内容。
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}
注意到,在这个文件的包含文件部分自动加入了如下一行内容:
#include "ui_widget.h"
这个就是Qt编译生成的与UI文件widget.ui对应的类定义文件。
3.widget.ui文件
widget.ui是窗体界面定义文件,是一个XML文件,定义了窗口上的所有组件的属性设置、布局,及其信号与槽函数的关联等。用UI设计器可视化设计的界面都由Qt自动解析,并以XML文件的形式保存下来。在设计界面时,只需要再UI设计器里进行可视化设计即可,而不用管widget.ui文件是怎么生成的。
下面是widget.ui文件的内容。
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"><class>Widget</class><widget class="QWidget" name="Widget"><property name="geometry"><rect><x>0</x><y>0</y><width>800</width><height>600</height></rect></property><property name="windowTitle"><string>Widget</string></property></widget><resources/><connections/>
</ui>
4.ui_widget.h文件
ui_widget.h是在对widget.ui文件编译后生成的一个文件,ui_widget.h会出现在编译后的目录下,或与widget.ui同目录(与项目的shadow build编译设置有关)。
注意: ui_widget.h是对widget.ui文件编译后自动生成的,widget.ui又是通过UI设计器可视化设计生成的。所以,对ui_widget.h手工进行修改没有什么意义,所有涉及界面的修改都应该直接在UI设计器里进行,所以,ui_widget.h也没有必要添加到项目里。
下面是ui_widget.h文件的内容
/********************************************************************************
** Form generated from reading UI file 'widget.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_WIDGET_H
#define UI_WIDGET_H#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QWidget>QT_BEGIN_NAMESPACEclass Ui_Widget
{
public:QPushButton *pushButton;QLabel *label;void setupUi(QWidget *Widget){if (Widget->objectName().isEmpty())Widget->setObjectName(QString::fromUtf8("Widget"));Widget->resize(800, 600);pushButton = new QPushButton(Widget);pushButton->setObjectName(QString::fromUtf8("pushButton"));pushButton->setGeometry(QRect(190, 200, 93, 28));label = new QLabel(Widget);label->setObjectName(QString::fromUtf8("label"));label->setGeometry(QRect(170, 150, 151, 16));retranslateUi(Widget);QObject::connect(pushButton, SIGNAL(clicked()), Widget, SLOT(close()));QMetaObject::connectSlotsByName(Widget);} // setupUivoid retranslateUi(QWidget *Widget){Widget->setWindowTitle(QCoreApplication::translate("Widget", "Widget", nullptr));pushButton->setText(QCoreApplication::translate("Widget", "PushButton", nullptr));label->setText(QCoreApplication::translate("Widget", "Hello world", nullptr));} // retranslateUi};namespace Ui {class Widget: public Ui_Widget {};
} // namespace UiQT_END_NAMESPACE#endif // UI_WIDGET_H
查看ui_widget.h文件的内容,发现它主要做了以下的一些工作。
(1)定义了一个类Ui_Widget,用于封装可视化设计的界面。
(2)自动生成了界面各个组件的类成员变量定义。在public部分为界面上每个组件定义了一个指针变量,变量的名称就是设置的objectName。比如,在窗体上放置了一个QLabel和一个QPushButton并命名后,自动生成的定义是:
QPushButton *pushButton;QLabel *label;
(3)定义了setupUi()函数,这个函数用于创建各个界面组件,并设置其位置、大小、文字内容、字体等属性,设置信号与槽的关联。
setupUi()函数体的第一部分是根据可视化设计的界面内容,用C++代码创建界面上各组件,并设置其属性。
接下来,setupUi()调用了函数retranslateUi(Widget),用来设置界面各组件的文字内容属性,如标签的文字、按键的文字、窗体的标题等。将界面上的文字设置的内容独立出来作为一个函数retranslateUi(),在设计多语言界面时会用到这个函数。
setupUi()函数的第三部分是设置信号与槽的关联,本文件中有以下两行:
QObject::connect(pushButton, SIGNAL(clicked()), Widget, SLOT(close()));QMetaObject::connectSlotsByName(Widget);
第1行时调用connect()函数,将在UI设计器里设置的信号与槽的关联转换为语句。这里是将pushButton按键的clicked()信号与窗体Widget的close()槽函数关联起来,当单击pushButton按钮时,就会执行Widget的close()槽函数,而close()槽函数的功能是关闭窗口。
第2行事设置槽函数的关联方式,用于将UI设计器自动生成的组件信号的槽函数与组件信号相关联。
所以,在Widget的构造函数里调用ui->setupUI(this),就是实现了窗体上组件的创建、属性设置、信号与槽的关联。
(4)定义namespace Ui,并定义一个从Ui_Widget继承的类Widget.
namespace Ui {class Widget: public Ui_Widget {};
} // namespace Ui
本节课最精华的部分来了,这也是Qt界面编程的底层设计:
ui_widget.h文件里实现界面功能的类是Ui_Widget。再定义一个类Widget从Ui_Widget继承而来,并定义在namespace Ui里,这样Ui::Widget与widget.h里的类Widget同名,但是用namespace区分开来。所以,界面的Ui::Widget类与文件widget.h里定义的Widget类实际上是两个类,但是Qt的处理让用户感觉不到Ui::Widget类的存在,只需要知道在Widget类里用ui指针可以访问可视化设计的界面按钮就可以了。
三、英语
先看几个单词:
- file 文件
- theory 原理
- work 工作
- mechanism 机制
再看几个句子:
- Burning poison(烙毒) contains(包含) a large amount of pure(纯的) energy, which greatly(极大地) enhances(增强) one’s abilities in one fell swoop(一下子)
- 烙毒中蕴含着大量精纯的能量,一举使自己能力大增
包含最容易想到的是has,比较地道的用contains。
a large amount of 表示大量
增加比较容易想到的是increase,但是能力的增强用enhances比较地道。
最后一个短语in one fell swoop 表示一蹴而就、一下子,原意是“一下子向下俯冲”。