Qt项目源码分析

利用Qt Creator组件创建一个可视化界面,仅需要几步就可以完成。

  • 但是生成的项目目录中的各个文件是什么?
  • 它们有什么作用?
  • 相互之间的联系是什么?
  • Qt 程序是如何编译运行的?

Qt项目介绍

​ 新建一个名字为helloworld的Qt的项目,利用Qcreator打开如下所示:

Qt打开

利用树形图直观的展示每个项目文件夹和文件的功能如下:

Qt 项目 - demo

helloworld.pro

​ 后缀为“.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    

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 <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QDialog>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>

// 与后面的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

// 前面两句和最后一句是要添加HELLODIALOG_H这个头文件之前要检查头文件是否已经存在,防止重复包含,
#ifndef HELLODIALOG_H // 这是一个条件编译指令,用于检查是否已经包含了名为"HELLODIALOG_H"的头文件。如果已经包含了该头文件,则下面的代码将被忽略,否则会继续编译下去。
#define HELLODIALOG_H // 这是一个预处理指令,用于定义一个名为"HELLODIALOG_H"的宏。

#include <QDialog> // 基类 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

main.cpp

#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函数是在栈上面的,代码一行一行执行完后就停止,但是调用消息循环机制后,窗口不消失,一直在展示 阻塞代码:即执行到此的时候,代码不往下执行了
}

hellodialog.cpp

#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;
}

hellodialog.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> 
 <class>HelloDialog</class>
 <widget class="QDialog" name="HelloDialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <widget class="QLabel" name="label">
   <property name="geometry">
    <rect>
     <x>120</x>
     <y>120</y>
     <width>71</width>
     <height>31</height>
    </rect>
   </property>
   <property name="text">
    <string>Hello World</string>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

在解析上面源码的时候发现有两个本不同的类却被取相同的名字 – HelloDialog,将Ui命名空间内的类名改为HelloDialog_Ui,将涉及到HelloDialog_Ui的地方去替换,再观察代码便于去区分理解:

改写ui_hellodialog.h

#ifndef UI_HELLODIALOG_H
#define UI_HELLODIALOG_H

// 包含了及各类的头文件
#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QDialog>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>

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

改写hellodialog.h

#ifndef HELLODIALOG_H 
#define HELLODIALOG_H

#include <QDialog>

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

改写hellodialog.cpp

#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;
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值