目录
窗口坐标系
Qt窗口坐标体系:窗口左上角为原点(0,0) 向右x增加 向下y增加
信号(Signal)和槽(Slot)的机制
信号:代表特定情况下被发射的事件 比如鼠标点击的clicked()信号 可在帮助中查找相关的信号 若在该类中未发现Signals 可以向上寻找其父类的信号
槽:对信号响应的函数,当对应信号发射时,槽函数会自动执行
特点:松散耦合 通过信号与槽机制实现的对象间通信方式具有低耦合度 信号的发送方和接收方彼此并无关联 仅通过connect函数来连接实现信号响应
信号与槽函数的连接方式
引入connect函数,其参数依次为信号的发送者 发送的信号 信号的接收者 处理的槽函数
样例:实现点击按钮关闭窗口
//mybtn代表信号发送方 发送的信号要用函数地址 格式为& + 信号发送方的类 + :: + 对应的信号
//this代表myWidget 信号的接收方 处理的槽函数同理也要用函数地址 格式与信号一致
connect(mybtn, &mypushbutton::clicked, this, &myWidget::close);
自定义的信号和槽
需求:创造老师类和学生类 下课后 老师饿 学生请客吃饭
1.添加新文件 老师类和学生类 由于这两个类并不属于窗口类 所以base class 中选择QObject 方便释放 原因可参考对象树机制
2.声明信号
//自定义信号写到signals下面
//返回值必须为void型 信号只需在.h文件中声明 无需实现
//可以有参数,可以重载
signals:
void hungry();
3.创建槽函数
//.h文件
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
//槽函数的声明 早期Qt版本声明要写到public slots: 高版本(5.4以后)可以写到public 或者 全局
//返回值也必须为void 需要声明与实现
//可以有参数 可以进行重载
void treat();
signals:
};
//.cpp文件 注意作用域一定要标明
void Student::treat()
{
qDebug() << "请客吃饭";
}
4.创建对象 构造链接
创建对象也需要先在.h文件中声明
//QWidget.h 添加对应头文件
#include <QWidget>
#include "teacher.h"
#include "student.h"
//在class下声明对象 设为私有
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
Teacher * zhy;
Student * st;
};
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//zhy 和 st 属于Widget下的对象 可以用. 或 ->调用
this->zhy = new Teacher(this);
this->st = new Student(this);
connect(zhy, &Teacher::hungry, st, &Student::treat);
}
5.创造信号触发函数
//Widget.h
private:
Ui::Widget *ui;
Teacher * zhy;
Student * st;
//代表下课后老师开始饿 信号触发函数
void ClassIsOver();
};
//Widget.cpp
void Widget::ClassIsOver()
{
//emit 专门用来发射信号
emit zhy->hungry();
}
//构造函数的涉及代码 注意顺序 要先用connect建立连接 再发送信号
connect(zhy, &Teacher::hungry, st, &Student::treat);
ClassIsOver();
信号和槽函数的重载问题
以信号hungry和槽函数treat的重载为例
信号hungry重载
//teacher.h
signals:
void hungry();
void hungry(QString foodname);
槽函数treat重载
//student.h
public:
explicit Student(QObject *parent = nullptr);
void treat();
void treat(QString foodname);
//student.cpp
void Student::treat(QString foodname)
{
qDebug() << "请客吃饭,吃" << foodname;
}
connect连接
//Widget.cpp
//当信号和槽函数出现重载时 不能单纯地直接调用函数地址&Teacher::hungry, &Student::treat
//会出现二义性 编译过程不知道要调用无参函数还是带参函数 需要引入函数指针指向具体的函数
//函数指针的格式 该函数返回值类型(函数作用域:: * 指针名称)(该函数形参类型, , , )
//若要指向的函数非全局函数 而是成员函数 则需要在指针名称前加上函数作用域 否则不需要
//等号右边代表要指向的函数 该成员函数由于存在多个 需要等式左边第二个(形参类型)来指向特定的一个
void (Teacher:: * teachersignal)(QString) = &Teacher::hungry;
void (Student:: * studentslot)(QString) = &Student::treat;
connect(zhy, teachersignal, st, studentslot);
ClassIsOver();
void Widget::ClassIsOver()
{
emit zhy->hungry("北京烤鸭");
}
输出结果
可以看到输出的是 请客吃饭,吃“北京烤鸭”
这里介绍下QString 它是Qt编程中常用的类 QString存储字符串采用Unicode码 编码方式采用UTF-16进行编码(一个汉字占两个字节)
qDebug() 函数输出 QString 时,根据需要会自动添加引号来区分字符串的起始和结束位置。
qDebug() << "请客吃饭,吃" << foodname;
在这个代码中,"请客吃饭,吃"
是一个常量字符串,而 foodname
是一个 QString
对象。当它们一起被输出时,qDebug()
函数会将 foodname
的内容用双引号括起来,以标识字符串的边界
若要去掉这个双引号 可以采用以下方法
//将QString型转化为char*
//char* 字符型指针 用来指向一个字符数组 被视为以空字符'\0'结尾
//qDebug在输出char*时仍然将其视为C风格字符串,并且不会添加双引号来表示字符串的边界
//qDebug在输出char*类型的指针时会进行特殊处理,不会直接输出指针的地址,而是根据该指针指向的内存地址来输出字符串的内容
qDebug() << "请客吃饭,吃" << foodname.toUtf8().data();
信号连接信号
需求:在窗口中提供一个按钮 点击后下课 老师饿 学生请吃饭
方法一:采用信号与槽函数的连接
QPushButton * btn = new QPushButton("下课", this);
connect(btn, &QPushButton::clicked, this, &Widget::ClassIsOver);
方法二:采用信号与信号的连接
//将按钮的点击信号与老师的饿了信号做连接 当下课按钮被点击时 饿了信号就会被发送
//同时饿了信号还与学生的请客槽函数连接 从而触发请客
//注意 这里指向的老师饿了信号和学生的请客槽函数都是无参的 详情见下面拓展
QPushButton * btn = new QPushButton("下课", this);
void (Teacher:: * Teachersignal)() = &Teacher::hungry;
void (Student:: * Studentslot)() = &Student::treat;
connect(zhy, Teachersignal, st, Studentslot);
connect(btn, &QPushButton::clicked, zhy, Teachersignal);
断开信号
//将要断开的连接()内的各个参数直接复制到disconnect()内即可
disconnect(btn, &QPushButton::clicked, zhy, Teachersignal);
拓展
1.信号可以和信号连接
2.一个信号可以连接多个槽函数
3.多个信号可以连接同一个槽函数
4.信号和槽函数的参数类型必须一一对应
5.信号的参数个数可以多于槽函数的参数个数 槽函数可以选择不接受多余的参数 但槽函数所需要的参数类型信号参数中必须存在且顺序一致
举例:信号参数 bool 槽函数 void 槽函数不会接受信号的参数 bool型相当于无效传递
信号参数 bool 槽函数 int 会报错 因为最基本的槽函数所需要的参数类型没有被满足
信号参数 bool int 槽函数 bool 可行
信号参数 int bool 槽函数 bool int 不可行 顺序必须一致
6.Qt4版本及以前的信号和槽的连接方式
优点:简洁直观
缺点:由于该连接方式的底层实现是将信号和槽函数转变为字符串去查找 并没有进行信号与槽函数参数之间的比较 因此可能会出现5中参数不符的情况 但它依然会通过编译 当你进行相关操作时会提示报错信息
//底层实现 "hungry()" "treat()"
connect(zhy, SIGNAL(hungry()), st, SLOT(treat()));
特别注意:在本机QT版本中信号和槽函数均要明确声明,否则会判定为信号或槽函数不存在
signals:
void hungry();
void hungry(QString foodname);
public slots:
void treat();
void treat(QString);