1、Qt 概述:
Qt是一个用C++编写的、跨平台的GUI工具包
(1)windoes 平台 Qt 的下载与安装:
Qt 5.15之前的版本可以离线安装,直接下载离线安装包即可:
下载地址:https://download.qt.io/archive/qt/
Qt从5.15开始,不再支持离线安装,那我们就需要下载 Qt 在线安装器,安装Qt:
Qt 在线安装器下载地址:https://download.qt.io/official_releases/online_installers/
2、Qt 的项目结构
untitled
|____untitled.pro ---- qmake工程文件(如果选择cmake则工程文件为CMakeLists.txt)
|____头文件
| |____mywindow.h
|____源文件
| |____main.cpp ---- 程序的入口
| |____mywindow.cpp
|____界面文件
|____mywindow.ui ---- ui类
(1)qt 程序的入口 main.cpp
#include "mywindow.h"
#include <QApplication>
// argc 是命令行参数个数,argv是命令行参数
int main(int argc, char *argv[])
{
// QApplication app(argc, argv) 管理Qt程序的运行,设置Qt应用程序,针对QWidget应用程序;
// QGuilication app(argc, argv) 管理Qt程序的运行,设置Qt应用程序,针对非QWidget应用程序,比如QQuick;
// QCorelication app(argc, argv) 管理Qt程序的运行,设置Qt应用程序,针对无界面的应用程序;
QApplication app(argc, argv);
// MyWindow是我们自定义的类,w是创建的对象;
MyWindow w;
// w对象调用了show() 方法;
w.show();
// 事件循环,QEventLoop::exec(),等待鼠标或键盘等其他的输入;
return app.exec();
}
(2)mywindow.h 头文件
#ifndef MYWINDOW_H
#define MYWINDOW_H
#include <QMainWindow>
// 命名空间
QT_BEGIN_NAMESPACE
namespace Ui { class MyWindow; }
QT_END_NAMESPACE
// 自定义类MyWindow 公有继承 QMainWindow类
class MyWindow : public QMainWindow
{
// 宏定义,Qt信号槽
Q_OBJECT
public:
// 构造函数,在创建对象时为对象的成员属性赋值,完成对象的初始化
MyWindow(QWidget *parent = nullptr);
// 析构函数,在对象销毁前系统自动调用,执行一些清理工作
~MyWindow();
// 添加一个自己的变量,声明一个变量
int i;
private:
Ui::MyWindow *ui;
};
#endif // MYWINDOW_H
(3)mywindow.cpp 构造函数 MyWindow() 和 析构函数 ~MyWindow() 所在的文件
#include "mywindow.h"
#include "ui_mywindow.h"
#include <QDebug> // QDebug 可以打印输出调试信息
// 定义了一个对象 QWidget *parent
// 把定义的对象parent,交给QMainWindow(),初始化出一个 继承了QWidget 的 QMainWindow对象
// 在构造函数里初始化对象要写在冒号 : 之后
MyWindow::MyWindow(QWidget *parent)
: QMainWindow(parent) // 初始化 QMainWindow 对象
, ui(new Ui::MyWindow) // 初始化 ui 对象,写法等同于,ui = new Ui::MyWindow
, i(4) // 初始化自定义变量i,写法等同于,i=4
{
// 初始化自定义变量i,也可以写在这里,在上面已经写过了,这里就先注释掉
// i=4
// 把MyWindow类,传递给 mywindow.ui
ui->setupUi(this);
// 打印输出调试信息
qDebug() << "构造函数执行了!" << endl;
}
MyWindow::~MyWindow()
{
// 打印输出调试信息
qDebug() << "析构函数执行了!" << endl;
// 销毁
delete ui;
}
(3)qt creator 快捷键
Ctrl + R 运行
Ctrl + Shift + Enter 上一行
Ctrl + Enter 下一行
选中类名 + F1 打开帮助文档
3、Qt 的信号 与 槽
信号与槽(Signal & Slot)是 Qt 编程的基础,也是Qt的一大创新。因为有了信号与槽的编程机制,在Qt中处理界面各个组件的交互操作时变得更加直观和简单;
GUI 程序设计的主要内容就是对界面上各组件的信号的响应,只需要知道什么情况下发射哪些信号,合理的去响应和处理这些信号就可以了。
(1)Qt 中的信号与槽连接模型
发送者 -> 信号 -> 接收者-> 槽
对象 -> 函数 -> 对象 -> 函数
信号与槽的连接:
举个例子:我们要实现点击按钮关闭窗口,按钮(对象) -> 点击函数(信号) -> 主窗口(对象) -> 关闭函数(槽)
- 信号(Signal):在特定情况下被发射的事件,例如 PushButton 最常见的信号就是鼠标单击时发射的 clicked() 信号,一个 ComboBox 最常见的信号是选择的列表项变化时发射的CurrentIndexChanged() 信号;
- 槽(Slot):就是对信号响应的函数。槽就是一个函数,与一般的C++ 函数是一样的,可以定义在类的任何部分(public,private,protected),可以具有任何参数,也可以被直接调用。槽函数与一般的函数不同的是,槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。
- 信号与槽关联:用 QObject::connect() 函数实现的,其基本格式为:
QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
connect() 是 QObject 类的一个静态函数,而 QObject 是所有 Qt 类的基类,在实际调用时可以忽略前面的限定符,所以可以直接写为:
connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
- sender 是发射信号的对象的名称;
- signal() 是信号名称;信号可以看做是特殊的函数,需要带括号,有参数时还需要指明参数;
- receiver 是接收信号的对象名称;
- slot() 是槽函数的名称,需要带括号,有参数时还需要指明参数;
- SIGNAL 和 SLOT 是Qt的宏,用于指明信号和槽,并将他们的参数转换为相应的字符串。
一个信号可以连接多个槽(当一个对象pushButton被点击时,有两个槽响应,hide()隐藏窗体,close()关闭窗体)
- connect(pushButton, SIGNAL(clicked()), this, SLOT(hide()));
- connect(pushButton, SIGNAL(clicked()), this, SLOT(close()));
多个信号可以连接同一个槽
- connect(pushButton1, SIGNAL(clicked()), this, SLOT(close()));
- connect(pushButton2, SIGNAL(clicked()), this, SLOT(close()));
一个信号可以连接到另外一个信号
- 信号与槽断开连接:disconnect()
bool QObject::disconnect(const QObject *sebder, const char *singnal, const QObject *receiver, const char *method)
- 断开所有与 myObject 连接的信号或槽:disconnect(myObject, 0, 0, 0) 等价于 myObject -> disconnect();
- 断开所有连接到特定信号的连接:disconnect(myObject, SIGNAL(mySignal()), 0, 0) 等价于 myObject->disconnect(SIGNAL(mySignal());
- 与指定的接收者断开连接:disconnect(myObject, 0, SLOT(slot()), 0) 等价于 myObject -> disconnect(SLOT(slot()));
(2)创建信号与槽
方法一:通过GUI方式:
双击 mainwindow.ui 文件 -> 拖拽 PushButton -> 点击信号与槽 -> 添加(发送者,信号,接收者,槽) -> 运行查看效果
方法二:通过代码方式添加:
打开 mainwindow.cpp 文件,通过代码添加信号与槽:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 在这里添加信号与槽 pushButton按钮对象,clicked()点击信号,this接收对象,主窗口MainWindow,close()槽函数
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(close()));
}
MainWindow::~MainWindow()
{
delete ui;
}
(3)自定义信号与槽
例子:学校 -> 通知 -> 学生 -> 上课
我们需要建一个学校类(发送者),建一个学生类(接收者),建一个通知函数(信号),建一个上课函数(槽)
新建一个学校类
#ifndef SCHOOL_H
#define SCHOOL_H
#include <QObject>
class School : public QObject
{
Q_OBJECT
public:
explicit School(QObject *parent = nullptr);
signals: // Qt信号的关键字
// 1、信号函数声明
void sendMessage();
};
#endif // SCHOOL_H
新建一个学生类
#ifndef STUDENT_H
#define STUDENT_H
#include <QObject>
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
signals:
// 2、槽函数声明
public slots:
void comBackStudy();
};
#endif // STUDENT_H
在界面类的头文件中引入学校类和学生类
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
// 3、在界面类的头文件中引入学校类和学生类
#include "school.h"
#include "student.h"
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow; }
QT_END_NAMESPACE
// 4、声明School类和Student类
class School;
class Student;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
// 5、创建School类的对象,创建Student类的对象,写在这里是公有的,写在下面是私有的
School *school;
Student *student;
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
在界面类的构造函数里,实例化School类和Student类
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//6、在界面类的构造函数里,实例化School类和Student类
school = new School(this);
student = new Student(this);
// 7、添加信号与槽
connect(school, SIGNAL(sendMessage()), student, SLOT(comBackStudy()));
// 9、当MainWindow类执行完,发送一个sendMessage()的信号
emit school->sendMessage();
}
MainWindow::~MainWindow()
{
delete ui;
}
槽函数定义
#include "student.h"
#include <QDebug>
Student::Student(QObject *parent)
: QObject{
parent}
{
}
// 8、槽函数定义
void Student::comBackStudy()
{
qDebug() << "学生上课!" << endl;
}
完成
4、不使用ui文件编程,以按钮为例(设置按钮的文本,按钮的位置,按钮的大小,主窗口的大小,来完成界面的设计)
主窗口的头文件引入QPushButton
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
// 1、主窗口的头文件引入QPushButton
#include <QPushButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
// 3、声明一个按钮对象pushButton
QPushButton *pushButton;
};
#endif // MAINWINDOW_H
项目文件这里加 widgets
QT += core gui
# 2、项目文件这里加 widgets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
# Default rules for deployment.
qnx: target.path = /tmp/$${
TARGET}/bin
else: unix:!android: target.path = /opt/$${
TARGET}/bin
!isEmpty(target.path): INSTALLS += target
实例化pushButton按钮
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// 4、实例化pushButton按钮
pushButton = new QPushButton(this);
// 设置按钮的文本
pushButton->setText("我是个按钮");
// 按钮的位置,按钮的大小
pushButton->setGeometry(0, 0, 100, 40);
// 主窗口的大小
this->resize(800, 480);
// 搞一个信号槽试试
connect(pushButton, SIGNAL(clicked()), this, SLOT(close()));
}
MainWindow::~MainWindow()
{
}
5、Qt 的 Object Tree (对象树)
(1)Qt 为什么需要设置父对象:设置父对象的主要目的是管理子对象的生命周期和管理子对象的内存;
- 父对象在析构的时候,会连带子对象全部释放,避免内存泄漏;
- 父对象显示的时候,子对象也会自动显示,不需要单独调用 show 方法,方便管理界面元素;
- 父对象可以通过子对象列表访问和操作子对象,实现对象间的通信和协作;
(2)如何设置父对象
- 通过构造函数传参
- 通过 setParent() 方法
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
// 通过构造函数传参
// pushButton = new QPushButton(this);
// 通过 setParent() 方法
pushButton = new QPushButton;
pushButton->setParent(this);
}
Widget::~Widget()
{
}
(3)Qt 的对象树机制
在 Qt 中,使用对象树(object tree)来组织和管理所有的 QObject 类及其子类的对象。当创建一个 QObject 时,如果使用了其他的对象作为其父对象(parent),那么这个 QObject 就会被添加到父对象的 children() 列表中,这样当父对象被销毁时,这个 QObject 也会被销毁。