QT元对象系统:
Qt中的元对象系统提供了对象间通信的信号和槽机制、运行时类型信息和动态属性系统。
使用元对象系统需要满足三个条件:
- 只有QObject派生类才可以使用元对象系统特性。
- 在类声明前使用Q_OBJECT()宏来开启元对象功能。
- 使用Moc工具为每个QObject派生类提供实现代码。
QObject派生类之间的转换:
QObject *obj = new QWidget();
QWidget *widget = qobject_cast<Qwidget *>(obj);
详解可进下面的链接:
Qt中的元对象系统(Meta-Object System) - 知乎 (zhihu.com)
信号与槽:
信号与槽:简单的说,就是发送信号,然后用槽函数接收信号。
信号(Signal)就是在特定情况下被发射的事件。
槽函数(Slot)就是对信号响应的函数。
连接函数为:connect()
QT4的方法:(旧版本的)
QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
QPushButton *p1=new QPushButton(this);
connect(p1,SIGNAL(clicked()),this,SLOT(close()));//点击关闭窗口
//connect(信号发送者, 发送的信号, 信号的接收者 ,处理的槽函数)
优点:参数直观
缺点:编译器不会检测参数类型
QT5的方法为:
QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
connect(p1,&QPushButton::clicked,this,&Widget::close);
//connect(信号发送者, 发送的信号, 信号的接收者 ,处理的槽函数)
connect(p1,&QPushButton::clicked,&Widget::close);//this可以省略
信号的特点:
- 写在 signals:后面
- 返回值为 void
- 不需要实现、可以有参、可以重载
槽函数的特点:
- 写在public 或 全局函数下
- 返回值为void
- 需要实现、可以有参、可以重载
信号与槽的特点:
- 信号可以连接信号
- 一个信号可以连接多个槽函数
- 多个信号可以连接一个槽函数
- 信号和槽的类型必须一致
- 信号参数可以多于槽函数 但参数类型必须一致
自定义信号和槽函数
触发信号的关键词 emit signal();
实现功能:点击按钮,触发信号,执行槽函数
widget.h文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include"person.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void debug();//槽函数
void show_emit();//触发函数
private:
Ui::Widget *ui;
signals:
void show();//信号函数
};
#endif // WIDGET_H
widget.cpp文件
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//p1=new person;
setFixedSize(600,600);
QPushButton *pbt=new QPushButton(this);//创建一个按键
pbt->move(200,200);//移动按键位置
pbt->setText("点击");//设置按键信息
connect(pbt,&QPushButton::clicked,this,&Widget::show_emit);//点击按钮触发信号
connect(this,&Widget::showdata,this,&Widget::debug);//接收信号,执行槽函数
}
void Widget::show_emit()
{
emit showdata();//发射信号
}
void Widget::debug()//槽函数
{
qDebug()<<"1231231230";//输出一串数字
}
Widget::~Widget()
{
delete ui;
}
点击按钮后:显示一串数字
当信号和槽有重载时的处理:
需要把信号或槽转化为函数指针
//函数指针
//返回类型(类名:: *名称)(参数类型)=&类名::函数名
widget.h文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include"person.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void debug();//槽函数
void debug(QString a);//重载
void show_emit();//触发函数
signals:
void showdata();//信号函数
void showdata(QString a);//重载
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp文件
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//p1=new person;
setFixedSize(600,600);
QPushButton *pbt=new QPushButton(this);//创建一个按键
pbt->move(200,200);//移动按键位置
pbt->setText("点击");//设置按键信息
connect(pbt,&QPushButton::clicked,this,&Widget::show_emit);//点击按钮触发信号
void (Widget:: *k1)(QString)=&Widget::debug;//debug的函数指针
void(Widget:: *k2)(QString)=&Widget::showdata;//showdata的函数指针
connect(this,k2,this,k1);//接收信号,执行槽函数
}
void Widget::show_emit()
{
emit showdata("故此");//发射有参信号
}
void Widget::debug()//槽函数
{
qDebug()<<"1231231230";//输出字符串
}
void Widget::debug(QString a)//槽函数
{
qDebug()<<a;//输出字符串
}
Widget::~Widget()
{
delete ui;
}
点击之后
这里需要解决的问题:输出的结果带有 " "
解决方法为:
- 把qDebug()<<a; 改为qDebug<<a.toUtf8().data();
- .toUtf8() :QString转化为QBateArray型
- .data() :QBateArray转化为QChar*型
lambda 表达式
c++11中新加的lambda表达式
格式为:
[ 函数对象参数 ](操作符重载函数参数) mutable ->return-type
{
内容
}
函数对象参数 | 含义 |
空 | 没有函数对象参数 |
= | 可以使用lambda范围内的所有可见的局部变量 ,以值传递的方式 |
& | 与 this 大体相同 以引用的方式传递 一般不使用& 会造成内存泄漏 |
this | 可以使用lambda中的成员变量 |
a | 将a 按值传递 默认不能修改(const) 可以在 操作符重载函数参数后加 mutable |
&a | 以引用的方式 |
- mutable 按值传递对象参数时加上mutable 可以 修改按值传递进来的拷贝(修改拷贝 不是修改值本身)
- 以上的参数可以混合使用
lambda表达式的用法:
1.可以代替槽函数:实现两个页面的跳转
主页面为widget 副页面为:person(继承于QWidget)
大致思路:
- 在widget中,先创建一个person对象,和一个QPushButton控件
- connect()实现widget到person的跳转
- 在person中,添加信号和触发信号的函数,和一个QPushButton控件
- connect() 实现点击,执行触发函数
- 在widget中,再一个connect(),信号函数为person中的信号,槽函数为lambda表达式,实现页面隐藏和页面显示。
person.h
#ifndef PERSON_H
#define PERSON_H
#include <QWidget>
class person : public QWidget
{
Q_OBJECT
public:
explicit person(QWidget *parent = nullptr);
void backemit();//出发信号
signals:
void back();//返回的信号
};
#endif // PERSON_H
person.cpp
#include "person.h"
#include<QPushButton>
person::person(QWidget *parent) : QWidget(parent)
{
setFixedSize(600,600);//重置大小
QPushButton *pbt1=new QPushButton(this);
pbt1->move(200,200);//移动按键位置
pbt1->setText("返回widget");//设置按键信息
connect(pbt1,&QPushButton::clicked,this,&person::backemit);//点击按键,发送信号
}
void person::backemit()
{
emit back();//发射信号
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include"person.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
private:
Ui::Widget *ui;
person *p1=nullptr;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
setFixedSize(600,600);
QPushButton *pbt=new QPushButton(this);//创建一个按键
pbt->move(200,200);//移动按键位置
pbt->setText("点击");//设置按键信息
p1=new person;//初始化对象
connect(pbt,&QPushButton::clicked,[=]()//lambda表达式
{
this->hide();//隐藏窗口
p1->show();
});
connect(p1,&person::back,this,[=]()
{
this->show();//显示自身
p1->hide();//p1隐藏
});
}
Widget::~Widget()
{
delete ui;
}
2.参数列表使用
QPushButton *P=new QPushButton("按钮1",this);
//因为clicked有一个返回值 为bool类型 所以可以接受这个值
connect(P,&QPushButton::clicked,[=](bool clicked)
{
qDebug()<<"clicked="<<clicked;
});
3.mutable的使用
QPushButton *P=new QPushButton("按钮1",this);
int a=10;
//未加mutable时 lambda中的变量只是可读
//添加mutable后 lambda中的变量变为 可读可写
connect(P,&QPushButton::clicked,[=]()mutable
{
a+=10;
qDebug()<<"a在lamba中的值="<<a;
});
//说明修改的仅仅是拷贝
qDebug()<<"a在lambda外的值="<<a;