项目创建
注意: 项目创建,名称和路径不能包含中文路径。
创建窗口三大基类
- QWidhet
- QMainWindow
- QDialog
1.1 项目文件介绍
mian.cpp 介绍
#include "mywidget.h"
#include <QApplication> // QApplication 应用程序类
// 程序入口 命令行变量数量 命令行变量数组
int main(int argc, char *argv[])
{
// a 应用程序对象 在Qt中,应用程序对象有且仅有一个
QApplication a(argc, argv);
// 窗口类
myWidget w;
// 窗口默认不弹出, 使用show类方法显示窗口
w.show();
// a.exec(); 进入消息循环机制 阻塞
return a.exec();
}
.pro文件 介绍
注意:如果不知道你在干什么,不要改动.pro文件内容!!!,项目会自动追加,不需要手动填写
#-------------------------------------------------
#
# Project created by QtCreator 2022-07-27T16:11:54
#
#-------------------------------------------------
QT += core gui // Qt包含的模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets // Qt版本大于4,加入widgets模块
TARGET = mywidget // 生成的.exe程序名称
TEMPLATE = app // 默认应用程序模板
SOURCES += \
main.cpp \ // 源文件
mywidget.cpp
HEADERS += \
mywidget.h // 头文件
mywifget.h文件
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
class myWidget : public QWidget // 共有继承 从父类Qwidget中
{
Q_OBJECT // Q_OBJECT 宏 支持信号和槽
public:
myWidget(QWidget *parent = 0); // 构造函数 parent默认值0
~myWidget();
};
#endif // MYWIDGET_H
mywidget.cpp文件
#include "mywidget.h"
myWidget::myWidget(QWidget *parent) // 基类
: QWidget(parent)
{
}
myWidget::~myWidget()
{
}
1.2 命名规范
类名 首字母大写,单词之间首字母大写
**变量名、函数名 **首字母小写
1.3 快捷键
运行 CTRL + R
编译 CTRL + B
查找 CTRL + F
注释 CTRL + /
帮助文档 F1 、左侧列表中的帮助、C:\Qt\Qt5.9.0\5.9\mingw53_32\bin\assistant.exe
字体缩放 CTRL + 鼠标滚轮
整行代码移动 CTRL + shift + ↑ ↓
自动对齐 CTRL + i
同名之间.h 和.cpp 之间切换 F4
1.4 对象树
QObject是Qt里边绝大部分类的根类
-
QObject对象之间是以对象树的形式组织起来的
- 当两个QObject(或子类)的对象建立了父子关系的时候。子对象就会加入到父对象的一个成员变量叫children(孩子)的list(列表)中。
- 当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里是说父对象和子对象,不要理解成父类和子类)
-
QWidget是能够在屏幕上显示的一切组件的父类
- QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。我们向某个窗口中添加了一个按钮或者其他控件(建立父子关系),当用户关闭这个窗口的时候,该窗口就会被析构,之前添加到他上边的按钮和其他控件也会被一同析构。这个结果也是我们开发人员所期望的。
- 当然,我们也可以手动删除子对象。当子对象析构的时候会发出一个信号destroyed,父对象收到这个信号之后就会从children列表中将它剔除。比如,当我们删除了一个按钮时,其所在的主窗口会自动将该按钮从其子对象列表(children)中删除,并且自动调整屏幕显示,按钮在屏幕上消失。当这个窗口析构的时候,children列表里边已经没有这个按钮子对象,所以我们手动删除也不会引起程序错误。
Qt 引入对象树的概念,在一定程度上简化了内存回收机制,当创建的对象指定的基类时由QObject或者Object派生类的时候,这个对象被加载到对象树上,当窗口关闭时,树上的对象也被释放掉。
对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。
// 设置窗口
#include "mywidget.h"
#include <QPushButton>
myWidget::myWidget(QWidget *parent)
: QWidget(parent)
{
// 按钮
QPushButton * btn = new QPushButton;
// btn->show(); // show() 用顶层的方式弹出
//如果想显示到当前窗口中,需要做依赖,加入对象树中
btn->setParent(this); // this指针就是指向当前类的指针
// 显示文本
btn->setText("德玛西亚");
// 按钮2 声明同时进行初始化
QPushButton * btn2 = new QPushButton("Second", this);
// 移动btn2,使用重载版本
btn2->move(100, 100);
// 配置窗口大小
resize(600, 400);
//设置窗口固定大小,设置后不可以通过拖拽调整窗口大小
setFixedSize(600,600);
// 按钮配置大小
btn->resize(200, 100);
// 指定窗口标题
setWindowTitle("第一个窗口");
// 创建自定义按钮
MyPushButton *myBtn = new MyPushButton;
myBtn->setParent(this);
myBtn->setText("我的按钮");
myBtn->move(200,200);
}
myWidget::~myWidget()
{
// 先调用子对象的析构函数,再调用父对象的析构函数,
// 但是打印时可能会出现先打印父对象析构执行提示,但是父对象还未释放完毕。再打印子对象析构执行提示
qDebug() << "MyWidget 析构调用。";
}
1.5 坐标系
坐标系左上角为(0,0),向右x为正,向下y为正。
1.6 信号槽
信号:各种事件
槽:响应信号的动作
当某个事件发生后,如某个按钮被点击了一下,就会发出一个被点击的信号(signal)。
某个对象接受到这个信号之后,就会做出一些相关的处理动作(称为槽slot)。
但是Qt对象不会无缘无故的收到某个信号,要想让一个对象收到另一个对象发出的信号,这时候需要建立连接(connect)。
1.6.1 系统自带的信号和槽
connect 函数是建立信号发送者、信号、信号接收者、槽 四者关系的函数:
connect (sender, signal, receiver, slot);
- 参数:
- sender : 信号发送者
- signal :信号
- receiver :信号接收者
- slot :接受对象在收到信号之后所需要调用的函数(槽函数)
注意:四个参数均为指针类型,信号和槽是函数指针。
// 创建一个关闭按钮
QPushButton *quitBtn = new QPushButton("关闭窗口", this);
// 信号槽的使用
connect(quitBtn, &QPushButton::clicked, this, &MyWidget::close);
利用帮助文档,输入QPushButton,在Content中寻找signal。如果没有就去父类中寻找该关键字。
在QAbstractButton中可以找到信号关键字,系统自带的信号有如下几个:
1.6.2 自定义信号和槽
自定义信号使用条件
- 自定义信号声明在类的signals域下
- 没有返回值,为void类型的函数
- 只有函数声明,没有定义
- 可以有参数,可以重载
- 通过emit关键字来触发信号,形式:
emit object->sig(参数);
自定义槽函数使用条件
-
qt4 必须声明在 private/public/protected slots域下面,qt5之后可以声明public下,同时还可以是静态的成员函数,全局函数,lambda表达式
-
没有返回值,void类型的函数
-
不仅有声明,还得要有实现
-
可以有参数,可以重载
案例:
定义场景:下课了,老师跟同学说肚子饿了(信号),学生请老师吃饭(槽)
首先定义一个学生类和老师类;
老师类中声明信号 饿了 hungry
// teacher.h
signal:
void hungry();
学生类中声明槽函数 请客 treat 并实现
// student.h
public slot:
void treat();
// student.cpp 槽函数实现
void Student::treat(){
qDebug() << "请老师吃饭";
}
在窗口中声明一个公共方法下课,这个方法的调用会触发老师饿了这个信号,而响应槽函数学生请客
// widget.h 声明
public:
void ClassIsOver();
// widget.cpp 实现
void MyWidget::ClassIsOver()
{
// 触发自定义信号
emit teacher->hungry();
}
学生响应了槽函数,并且打印信息
// student.cpp 自定义槽函数
void Student::treat()
{
qDebug() << "Student treat teacher";
}
在窗口连接信号槽
// widget.h
zt = new Teacher(this);
st = new Student(this);
// 连接
connect(zt, &Teacher::hungry, st, &Student::treat);
// 触发下课函数
ClassIsOver();
但是如果有两个重名的信号和自定义的信号的槽,直接连接会报错,所以需要利用函数指针来指向函数地址再连接
// 函数指针
void (Teacher:: * teacherSingal)(QString) = &Teacher:: hangry;
void (Student:: * studentSlot)(QString) = &Student::treat;
// 连接
connect(teacher,teacherSingal,student,studentSlot);
QString 转 char*
// QString 转 char*
// 先转QByteArray 再转到char*
foodName.toUTF8().data();
信号和槽的拓展
-
一个信号可以和多个槽相连
-
多个信号可以连接到一个槽
-
一个信号可以连接到另外的一个信号
新建一个btn,实现点击发送下课信号到Teacher类,再将老师饿了信号发送到Student类。
connect(btn,&QPushButton::clicked,teacher,&Teacher::hungry);
-
信号和槽可以断开连接
disconnect (sender, signal, receiver, slot);
这种情况并不经常出现,因为当一个对象****delete之后,Qt自动取消所有连接到这个对象上面的槽。
- 信号和槽函数参数类型和个数必须同时满足两个条件
- 信号函数的参数个数必须大于等于槽函数的参数个数
- 信号函数的参数类型和槽函数的参数类型必须一一对应