利用Qt Creator组件创建一个可视化界面,仅需要几步就可以完成。
新建一个名字为helloworld的Qt的项目,利用Qcreator打开如下所示:
利用树形图直观的展示每个项目文件夹和文件的功能如下:
后缀为“.pro”的文件是项目的管理文件,文件名就是项目的名称,分析其文件源码:
#-------------------------------------------------
#
# Project created by QtCreator 2023-07-18T14:59:01 // 创作时间
#
#-------------------------------------------------
QT += core gui // 表示项目中加入core gui模块,core gui是Qt用于GUI设计的类库模块,除此之丸Qt还有很多的模块,例如Qt GUI、Qt SQL等等
//Qt类库以模块的形式组织各种功能的类,根据项目涉及的功能需求,在项目中添加适当的类库模块支持。例如,如果项目中使用到了涉及数据库操作的类就需要用到sql 模块,在pro 文件中需要增加如下一行:Qt += sql
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets // 这是个条件执行语句,表示当Qt主版本大于4 时,才加入widgets模块。 因为后面使用的类都包含在widgets模块中
TARGET = helloworld // 表示生成的目标可执行文件的名称,即编译后生成的可执行文件是helloworld.exe。
TEMPLATE = app // 表示项目使用的模板是app,是一般的应用程序。 除了app还有lib建立一个故的makefile等等
DEFINES += QT_DEPRECATED_WARNINGS // 用于定义预处理器宏。预处理器宏是一种编译时进行文本替换的机制,可以在代码中使用宏来定义条件编译、功能开关等。
/* 在C++中,可以使用#ifdef和#ifndef指令来检查宏是否已经定义。
ifdef:如果宏已经定义,则执行后面的代码块。
例如: #ifdef QT_DEPRECATED_WARNINGS
// 执行代码块,如果QT_DEPRECATED_WARNINGS已经定义
#else
// 执行代码块,如果QT_DEPRECATED_WARNINGS没有定义
#endif
ifndef:如果宏没有定义,则执行后面的代码块。
#ifndef QT_DEPRECATED_WARNINGS
例如: // 执行代码块,如果QT_DEPRECATED_WARNINGS没有定义
#else
// 执行代码块,如果QT_DEPRECATED_WARNINGS已经定义
#endif
*/
// 这些文件列表是Qt Creator自动添加到项目管理文件里面的,用户不需手动修改。当添加一个文件到项目,或从项目里删除一个文件时,项目管理文件里的条目会自动修改。
SOURCES += \ // 记录了项目中包含的源程序文件的名称
main.cpp
FORMS += \ // 记录了项目中包含的头文件的名称
hellodialog.ui
HEADERS += \ // 记录了项目中包含的窗体文件(.ui文件)的名称
hellodialog.h
/********************************************************************************
** Form generated from reading UI file 'hellodialog.ui'
**
** Created by: Qt User Interface Compiler version 5.9.0
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
// 前面两句和最后一句是要添加UI_HELLODIALOG_H这个头文件之前要检查头文件是否已经存在,防止重复包含
#ifndef UI_HELLODIALOG_H
#define UI_HELLODIALOG_H
// 包含了及各类的头文件
#include
#include
#include
#include
#include
#include
#include
// 与后面的QT_END_NAMESPACE一起是Qt的命名空间开始宏和结束宏
QT_BEGIN_NAMESPACE
class Ui_HelloDialog //定义了一个Ui_HelloDialog类,在前面更改的对话框类HelloDialog之前加上一个Ui_(默认的名字)
{
public:
QLabel *label; //定义了一个QLabel类对象的指针,这个就是对话框窗口添加的Label部件
void setupUi(QDialog *HelloDialog) // 用来生成界面,因为选择模板时选择的是对话框,所以函数的参数是QDialog类型
{
// 设置对话框的对象名称
if (HelloDialog->objectName().isEmpty())
HelloDialog->setObjectName(QStringLiteral("HelloDialog"));
// 设置了窗口的大小
HelloDialog->resize(400, 300);
// 在对话框上创建了标签对象
label = new QLabel(HelloDialog);
// 设置标签对象的名称(objectname)
label->setObjectName(QStringLiteral("label"));
// 设置标签对象的位置(默认左上角为(0, 0)) 和 大小(长71 宽31)
label->setGeometry(QRect(120, 120, 71, 31));
// 该函数实现对窗口里面的字符串进行编码转换的功能
retranslateUi(HelloDialog);
//调用了QMetaObject这个类里面的connectSlotsByName()函数,使得窗口中的部件可以实现按对象名进行信号和槽的关联
QMetaObject::connectSlotsByName(HelloDialog);
} // setupUi
// 该函数实现对窗口里面的字符串进行编码转换的功能
void retranslateUi(QDialog *HelloDialog)
{
HelloDialog->setWindowTitle(QApplication::translate("HelloDialog", "Dialog", Q_NULLPTR));
label->setText(QApplication::translate("HelloDialog", "Hello World", Q_NULLPTR));
} // retranslateUi
};
// 定义了命名空间Ui
namespace Ui {
class HelloDialog: public Ui_HelloDialog {}; // Ui里面定义了一个HelloDialog继承自Ui_HelloDialog类
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_HELLODIALOG_H
// 前面两句和最后一句是要添加HELLODIALOG_H这个头文件之前要检查头文件是否已经存在,防止重复包含,
#ifndef HELLODIALOG_H // 这是一个条件编译指令,用于检查是否已经包含了名为"HELLODIALOG_H"的头文件。如果已经包含了该头文件,则下面的代码将被忽略,否则会继续编译下去。
#define HELLODIALOG_H // 这是一个预处理指令,用于定义一个名为"HELLODIALOG_H"的宏。
#include // 基类 Qt的三个大基类之一 另外两个为:QWidget、QMainWindow
/*
这是声明了一个名称为Ui 的命名空间(namespace),包含一个类HelloDialog。但是这个类HelloDialog并不是本文件里定义的类HelloDialog,而是ui_hellodialog.h文件里定义的类,用于描述界面组件的。这个声明相当于一个外部类型声明(具体要看完ui_hellodialog.h文件内的解释之后才能搞明白)。
*/
namespace Ui { // 前置声明(前置声明只能作为指针或引用,不能定义类的对象,自然也就不能调用对象中的方法了。)加快编译速度
//使用命名空间:避免在一个头文件中随意包含其他文件而产生错误,这里只使用了该类对象的指针,并不需要该类的完整定义
class HelloDialog; // 命名空间的解析Ui::HelloDialog
}
class HelloDialog : public QDialog // 主体部分是一个继承于QDialog的新定义的类HelloDialog。
{
Q_OBJECT // 扩展功能,在HelloDialog类中使用了宏Q_OBJECT,这是提供了Qt 的信号与槽(signal 和slot)机制
public:
// 定义了HelloDialog类的构造函数和析构函数,两个函数都是只定义,但没有实现
// 用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显示的, 跟它相对应的另一个关键字是implicit隐藏的,类构造函数默认情况下即声明为implicit(隐式).
explicit HelloDialog(QWidget *parent = 0); // 默认参数为0
~HelloDialog();
private:
Ui::HelloDialog *ui; // Ui::HelloDialog 表示是属于前面Ui命名空间里面的类HelloDialog的指针,不加的话会被识别成本文件HelloDialog的指针
};
#endif // HELLODIALOG_H
#include "ui_hellodialog.h" // ui_hellodialog.h文件中已经包含了上面的三个类,故上面的三个类可以注释掉
// main函数程序入口
// argc 命令行便令的数量
// argv 命令行变量的数组
int main(int argc, char *argv[])
{
QApplication a(argc, argv); //实例化一个应用程序的对象a,Qt中有且仅有一个应用程序对象
QDialog w; // 实例化QDialog对象
Ui::HelloDialog ui; // 使用命名空间Ui中断HelloDialog类定义一个ui对象
ui.setupUi(&w); // 将对话框类对象作为参数,这样可以将设计好的界面应用到对象w所表示的对话框上了。
w.show(); // 让对话框显示出来
return a.exec(); // a.exec() 进入一个消息循环机制 main函数是在栈上面的,代码一行一行执行完后就停止,但是调用消息循环机制后,窗口不消失,一直在展示 阻塞代码:即执行到此的时候,代码不往下执行了
}
#include "hellodialog.h" // 由于在hellodialog.h中只是前置声明了Ui,而前置声明只能作为指针或引用,不能定义类的对象,自然也就不能调用对象中的方法了。
#include "ui_hellodialog.h" // 故这里需要导入以可以实例化Ui
/*
实现HelloDialog的构造函数和析构函数
*/
/*
c++构造函数后面跟“:”也表示赋值
1)对含有对象成员的对象进行初始化
类line有两个私有对象成员startpoint、endpoint,line的构造函数写成:
line(int sx,int sy,int ex,int ey):startpoint(sx,sy),endpoint(ex,ey){……}
2)对于不含对象成员的对象,初始化时也可以套用上面的格式,例如,
类rectangle有两个数据成员length、width,其构造函数写成:
rectangle():length(1),width(2){}
rectangle(int x,int y):length(x),width(y){}
3)对父类进行初始化,例如,
CDlgCalcDlg的父类是MFC类CDialog,其构造函数写为:
CDlgCalcDlg(CWnd* pParent ): CDialog(CDlgCalcDlg::IDD, pParent)
*/
// HelloDialog对其父类QDialog进行初始化,如果parent指定了则传给父类,若没有指定则默认用0
HelloDialog::HelloDialog(QWidget *parent) :QDialog(parent), ui(new Ui::HelloDialog)
{
// ui(new Ui::HelloDialog)放在上面的作用与 ui= new Ui::HelloDialog 放在这里是一样的 创建Ui::HelloDialog对象
ui->setupUi(this); // this表示为现在这个类所代表的对话框创建界面。
}
// 类内定义函数,类外实现函数的形式
HelloDialog::~HelloDialog() // HelloDialog:: 表示~HelloDialog()是属于HelloDialog这个类的函数,不加的话会被识别成普通的函数
{
delete ui;
}
<ui version="4.0">
<class>HelloDialogclass>
<widget class="QDialog" name="HelloDialog">
<property name="geometry">
<rect>
<x>0x>
<y>0y>
<width>400width>
<height>300height>
rect>
property>
<property name="windowTitle">
<string>Dialogstring>
property>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>120x>
<y>120y>
<width>71width>
<height>31height>
rect>
property>
<property name="text">
<string>Hello Worldstring>
property>
widget>
widget>
<resources/>
<connections/>
ui>
在解析上面源码的时候发现有两个本不同的类却被取相同的名字 – HelloDialog,将Ui命名空间内的类名改为HelloDialog_Ui,将涉及到HelloDialog_Ui的地方去替换,再观察代码便于去区分理解:
#ifndef UI_HELLODIALOG_H
#define UI_HELLODIALOG_H
// 包含了及各类的头文件
#include
#include
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
class Ui_HelloDialog
{
public:
QLabel *label;
void setupUi(QDialog *HelloDialog)
{
''' '''
} // setupUi
// 该函数实现对窗口里面的字符串进行编码转换的功能
void retranslateUi(QDialog *HelloDialog)
{
''' '''
} // retranslateUi
};
// 定义了命名空间Ui
namespace Ui {
class HelloDialog_Ui: public Ui_HelloDialog {}; // Ui里面定义了一个HelloDialog_Ui继承自Ui_HelloDialog类
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_HELLODIALOG_H
#ifndef HELLODIALOG_H
#define HELLODIALOG_H
#include
namespace Ui {
class HelloDialog_Ui; // 命名空间的解析Ui::HelloDialog_Ui
}
class HelloDialog : public QDialog
{
Q_OBJECT
public:
explicit HelloDialog(QWidget *parent = 0); // 默认参数为0
~HelloDialog();
private:
Ui::HelloDialog_Ui *ui; // Ui::HelloDialog_Ui 表示是属于前面Ui命名空间里面的类HelloDialog_Ui的指针
};
#endif // HELLODIALOG_H
#include "hellodialog.h"
#include "ui_hellodialog.h"
HelloDialog::HelloDialog(QWidget *parent) :QDialog(parent), ui(new Ui::HelloDialog_Ui)
{
ui->setupUi(this);
}
// 类内定义函数,类外实现函数的形式
HelloDialog::~HelloDialog() // HelloDialog:: 表示~HelloDialog()是属于HelloDialog这个类的函数,不加的话会被识别成普通的函数
{
delete ui;
}