信号和槽是用于对象之间的通信,signals用来定义信号,emit用来发射信号。slots 用来定义槽函数;
自定义一个类想使用信号和槽,必须满足两点:
槽函数限制:
1.槽函数的参数个数必须小于或等于信号参数,大于则报错;
2.槽函数参数类型必须和信号参数匹配;
Qt5写法:
QPushButton *pBtn1 = new QPushButton("测试按钮",this);
connect(pBtn1,&QPushButton::clicked,this,&MainWindow::testFunc);
//槽函数
void MainWindow::testFunc()
{
QPushButton *btn = qobject_cast<QPushButton*>(sender());//通过sender()获取信号发送者
QString strName = btn->text();
}
Qt4写法
connect(pBtn1,SIGNAL(clicked()),this,SLOT(testFunc()));
Qt4写法简单,Qt5更安全、它会编译时对参数进行检测;
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
void shitHungrySignal();
signals:
void hungry(); //自定义信号,返回值为void,不需要实现,可以有函数,可以重载
void hungry(QString foodName);//重载
};
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
public slots:
void treat();//返回值必须void,需要实现,可以有参数,也可以重载
void treat(QString foodName);//重载
};
调用:
this->st = new Student(this);
this->te = new Teacher(this);
//Qt4写法,可以通过参数来区分
connect(te,SIGNAL(hungry()),st,SLOT(treat()));
connect(te,SIGNAL(hungry(QString)),st,SLOT(treat(QString));
//Qt5写法,因无法区分重载信号和槽,需要使用函数指针方式
void(Teacher:: *theacherSignal1)(QString) = &Teacher::hungry;
void(Student:: *studentSignal1)(QString) = &Student::treat;
void(Teacher:: *theacherSignal2)(void) = &Teacher::hungry;
void(Student:: *studentSignal2)(void) = &Student::treat;
connect(te,theacherSignal1,st,studentSignal1);
connect(te,theacherSignal2,st,studentSignal2);
//Qt4问题,编译通过,但运行时无法触发槽函数,原因参数不匹配
connect(te,SIGNAL(hungry()),st,SLOT(treat(QString)));
//Qt5则会编译时直接报错,提示错误信息
connect(te,theacherSignal2,st,studentSignal1);
static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,
const QObject *receiver, const QMetaMethod &method,
Qt::ConnectionType type = Qt::AutoConnection);
//直接
QPushButton *btn1 = new QPushButton(this);
connect(btn1,&QPushButton::clicked,this,&Widget::btn1Clicked);//第五个参数是Qt::DirectConnection
//队列
MyThread *thread = new MyThread(this);//自定义的线程类
connect(btn1,&QPushButton::clicked,thread,&MyThread::startThread);//Qt::QueuedConnection
thread->start();
//阻塞队列
MyThread *thread = new MyThread(this);
connect(thread,&MyThread::sig_progress,this,&Widget::slot_pp,Qt::BlockingQueuedConnection);
thread->start();
C++11新特性,如果QT版本低于5.4则需要在.pro文件中添加 CONFIG += c++11,但一般会自动生成;
[](){}; //这就是一个表达式
[=] //=号表示获取当前局部中的所有变量,值传递,是一个拷贝,无法修改真实变量
[&] //引用传递方式,操作的都是真实变量
() //参数
[](){}();//最后的()就是直接调用这个表达式
//ep.1-err
QPushButton *but1 = new QPushButton(this);
[](){
but1->setText("aaa");//失败,找不到but1,需要使用=号来获取局部变量
};
//ep.1-ok
[=](){
but1->setText("aaa");
};
//定义+调用
[=](){
but1->setText("aaa");//失败,找不到but1,需要使用=号来获取局部变量
}();
//ep.2
QPushButton *myBtn = new QPushButton(this);
QPushButton *myBtn2 = new QPushButton(this);
myBtn2->move(100,100);
int m = 100;
connect(myBtn,&QPushButton::clicked,this,[m]()mutable {m=100+10;qDebug()<<m;});
connect(myBtn2,&QPushButton::clicked,this,[=](){qDebug()<<m;});
qDebug()<<m;
//注意第一个connect触发后结果永远是110,它的m是外部m的一份拷贝.并不影响外部m的值
//注意第一个connect因为是值传递的方式,如果要修改值则需要添加mutable关键字,否则报错
//ep.3
QPushButton *but3 = new QPushButton(this);
but3->setText("关闭");
but3->move(100,0);
connect(but3,&QPushButton::clicked,this,&Widget::close());//可以
connect(but3,&QPushButton::clicked,this,[=](){
//可以在这里坐一起其他操作再关闭窗口
//...
this->close();
});
当我们使用信号和槽来传递一些自定义类型数据时,通常会报如下错误:
//自定义数据
class MyClass
{
public:
MyClass();
int m_nAge;
QString m_strNmae;
};
//调用
MyThread *thread = new MyThread(this);
connect(thread,SIGNAL(sig_message(MyClass)),this,SLOT(slot_message(MyClass)));
thread->start();
//报错
QObject::connect: Cannot queue arguments of type 'MyClass'
以上错误表示:Qt对该类型未知,需要提前注册到元对象系统中来告诉Qt;
添加如下代码即可:
#include
class MyClass
{
public:
MyClass();
int m_nAge;
QString m_strNmae;
};
Q_DECLARE_METATYPE(MyClass)
上面所说是Qt::DirectConnection的单线程连接模式时的使用方法,但如果是多线程Qt::QueuedConnection等建立的连接时,还需添加一步:
3.通过qRegisterMetaType<>()来注册类型,来确保在第一次建立connect之前类型被注册; ( 注意:在哪绑定信号槽就在哪注册;比如我要在主界面中绑定与子线程中的自定义类型,可以在主界面的构造函数中提前注册好MyClass类型; )
mainWidget::mainWidget(QWidget *parent) :QWidget(parent),ui(new Ui::mainWidget)
{
ui->setupUi(this);
//注册
qRegisterMetaType<MyClass>("MyClass");
}