赞
踩
概述:
信号和槽是一种高级接口,应用于对象之间的通信,它是 QT 的核心特性。要正确的处理信号和槽,必须借助一个称为 moc(Meta Object Compiler)的 QT 工具,该工具是一个 C++ 预处理程序,它为高层次的事件处理自动生成所需要的附加代码。 信号和槽能携带任意数量和任意类型的参数。我们可以将很多信号与单个的槽进行连接,也可以将单个的信号与很多的槽进行连接,(当这个信号被发射时,这些槽将会一个接一个地 执行,但是它们执行的顺序将会是随机的、不确定的,我们不能人为地指定哪个先执行、哪个后执行。)甚至于将一个信号与另外一个信号相连接,这时无论第一个信号什么时候发射系统都将立刻发射第二个信号。总之,信号与槽构造了一个强大的部件编程机制。
信号
信号只需要在头文件中做声明,不需要在cpp中实现。放在QT自定义关键字signals下,在此之前一定要加上Q_OBJECT宏!
在编程中,一般使用的是控件内部定义好的信号。也可以自定义信号,并通过emit在代码中发射信号。
当然很多控件都提供了一般的信号,如果使用的时候查看帮助文档即可。
比如QAbstractButton
里面的Signals
槽函数:
和普通的C++成员函数几乎是一样的(可以是虚函数,可以被重载,可以是public slots、protected slots、private slots,可以被其他C++成员函数直接调用;唯一不同的是:槽还可以和信号连接在一起,在这种情况下,信号被发射时,会自动调用这个槽。)槽不需要信号传过来的参数时,可以不要参数;但槽一旦要参数,其参数个数,类型,顺序必须要和对应的信号保持一致。另外,槽的参数不能有缺省值。
同理和信号一样一般常用的槽函数对应的类会提供在帮助手册里面找即可
比如:QAbstractButton
里面提到的Public Slots
自定义槽函数下面会提到
1,信号和槽的连接
所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,用自己的一个函数(成为槽(slot))来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。(这里提一句,
Qt 的信号槽使用了额外的处理来实现,并不是 GoF 经典的观察者模式的实现方式。)
按钮在 Qt 中被称为 QPushButton。对它的创建和显示,这里不做过多的讲解。我们这里要仔细分析 QObject::connect()这个函数。
注:在Qt5
中connect
函数有5个重载。
connect(sender, signal, receiver, slot);
这是我们最常用的形式。connect()一般会使用前面四个参数,第一个是发出信号的对象,第二个是发送对象发出的信号,第三个是接收信号的对象,第四个是接收对象在接收到信号之后所需要调用的函数。也就是说,当 sender 发出了 signal 信号之后,会自动调用 receiver 的 slot 函数。
信号和槽的参数
信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。如果不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少),但是不能说信号根本没有这个数据,你就要在槽函数中使用(就是槽函数的参数比信号的多,这是不允许的)。
注意:槽函数也可以是Lambda
表达式
比如用按钮来关闭窗口,并且用Debug打印关闭成功
button1 = new QPushButton("close",this);//创建按钮,指定父对象
button2 = new QPushButton("print",this);//创建按钮,指定父对象
button1->move(100,100);//移动按钮位置
button2->move(100,300);//移动按钮位置
connect(button1,&QPushButton::clicked,this,&QWidget::close);
connect(button2,&QPushButton::clicked,this,[](){
qDebug() << "关闭成功";//打印关闭成功
});
自定义信号和槽函数:
对于上面的信号和槽函数除了使用一些类本身自带的一些之外还可以自己定义比如
//mysignal头文件 class Mysignal : public QWidget { Q_OBJECT public: explicit Mysignal(QWidget *parent = nullptr); QPushButton *button3; signals: void print(); }; //mysignal源文件 #include "mysignal.h" Mysignal::Mysignal(QWidget *parent) : QWidget(parent) { button3 = new QPushButton("send",this);//创建按钮,指定父对象 connect(button3,&QPushButton::clicked,this,[=](){ emit print();//发射对象定义的信号 }); } //主窗口头文件 class TestWidget : public QWidget { Q_OBJECT public: TestWidget(QWidget *parent = nullptr); ~TestWidget(); private: QPushButton *button1; QPushButton *button2; private: void mySlots() { qDebug() << "发射了信号"; } } //主窗口源文件 TestWidget::TestWidget(QWidget *parent) : QWidget(parent) { Mysignal* send = new Mysignal(this); button1 = new QPushButton("close",this);//创建按钮,指定父对象 button2 = new QPushButton("print",this);//创建按钮,指定父对象 button1->move(100,100);//移动按钮位置 button2->move(100,300);//移动按钮位置 connect(button1,&QPushButton::clicked,this,&QWidget::close); connect(button2,&QPushButton::clicked,this,[](){ qDebug() << "关闭成功";//打印关闭成功 }); connect(send,&Mysignal::print,this,&TestWidget::mySlots); }
执行结果
对于自定义的信号槽注意事项
1,发送者和接收者都需要是 QObject 的子类(当然,槽函数是全局函数、Lambda表达式等无需接收者的时候除外);
2,信号和槽函数返回值是 void
3,信号只需要声明,不需要实现
4,槽函数需要声明也需要实现
5,槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
6,使用 emit 在恰当的位置发送信号;
7,使用 connect()函数连接信号和槽。
8,如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少, 即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。