【Qt】信号、槽
目录
一、信号和槽的基本概念
二、connect函数:关联信号和槽
例子:
三、自定义信号和槽
1.自定义槽函数
2.自定义信号函数
例子:
四、带参的信号和槽
例子:
五、Q_OBJECT宏
六、断开信号和槽的连接
例子:
七、lambda作为槽函数
例子:
qt中关于lambda引用传递捕捉
一、信号和槽的基本概念
1.信号
信号(signal)是由于用户对窗口或控件进行某些操作,导致窗口或控件产生某种特定事件,这时窗口类/控件类会发出信号给接收信号的控件对象。
2.槽
槽(slot)是对信号响应的函数。槽就是一个函数,与一般C++函数一样,可以定义在类任何访问权限下(public、protected、private),可以带参,可以重载,但是不能有默认参数。槽函数与普通函数不同的点是:槽函数可以关联某个信号,当该信号发出时关联的槽函数会自动执行。
信号和槽要解决的问题就是响应用户的操作
下面有一个例子:我们在窗口中创建一个按钮,点击该按钮控件该按钮上的内容会在"hello world
"和"hello qt"中间切换。
使用拖拽控件的方式构建图形界面:
当使用拖拽控件的方式构建图形界面时,每个控件对象都有下图中右下角中红圈圈的部分:objectName这个里面的内容就是通过拖拽创建的控件对象的名字,可以对该名字进行修改。
在Qt中,QObject类提供了一个静态成员函数connect,该函数用于关联信号函数和槽函数。
值得注意的是:如果使用这种拖拽方式构建图形界面,此时创建的控件对象都会在Widget类中的成员变量ui所指向的类中,因此在Widget类中访问以拖拽方式创建的控件对象时,要使用ui指针访问。
使用纯代码方式构建图形界面:
值得注意的是:如果按照纯代码方式进行构建,那么我们应该将按钮控件对象声明为Widget类的成员变量,然后再Widget的构造函数中定义,这样就可以在槽函数中对该按钮对象进行操作。
qt中类的关系:
QObject是Qt内置的父类,Qt中大多数类都是间接或直接继承自QObject。比如QWidget就是继承自QObject因此继承自QObject的类都可以使用QObject中的静态成员函数connect以及QObject的其他成员函数。
二、connect函数:关联信号和槽
例子:
现在写一个qt项目:通过点击窗体控件中的一个按钮控件使窗体控件关闭。
如下:发送信号的控件对象是button按钮,信号函数是QPushButton::clicked,接收信号的控件是窗口Widget,槽函数是Widget::close,close函数其实是QWidget类的成员函数,由于类Widget继承自QWidget因此Widget可以使用QWidget中的所有方法。
需要注意的是:使用connect函数关联信号和槽时,信号函数必须是发送信号的控件对象的成员函数;槽函数必须是接收信号的控件对象的成员函数。
通过图标判断信号函数和槽函数:
像信号发射的图标是信号函数,比如这个clicked函数;像锯齿形的是槽函数,比如这个click。
三、自定义信号和槽
qt内部已经有大量现成的信号函数和槽函数,但是有些时候需要我们自己定义信号函数和槽函数。
1.自定义槽函数
1.自定义槽函数的返回值是void,需要声明和实现
2.可以有参数,可以被重载。
3.可以在public slots、protected slots、priavte slots下,也允许写在public下声明。
C++纯代码(即使用connect函数)方式连接信号和槽。
上面已有这种方式实现的代码。
UI设计窗口的方式自动连接信号和槽。
1.选中拖拽的控件点击右键,点击转到槽
2.选择要连接的信号
3.qt会自动生成一个函数,通过该函数的名字自动连接信号和槽
qt自动生成的这个函数名是具有格式规则的。对于函数名on_pushButton_clicked,on_后面就是发出信号的控件对象名字,clicked就是该控件发送的信号函数(一旦用户触发pushButton对象中的QPushButton::clicked,pushButton就会发送信号)。处理信号的动作就是在这个on_pushButton_clicked函数里实现。
2.自定义信号函数
1.自定义信号函数的返回值是void,只需声明,不需要定义,qt会自动帮我们完成定义。
2.可以有参数,可以被重载。
3.必须在signals后声明。
例子:
我们通过自定义信号完成一个简单的需求:但我们点击按钮控件后,窗口标题需要发生改变。
这有个emit其实是可以不写的。
四、带参的信号和槽
对于不带参的信号和槽前面已经有了。
1.信号函数和槽函数的参数类型必须一致,个数可以不一致(信号函数的参数个数多于槽函数)。
2.发射信号的时候,就可以给信号函数传递参数,该信号函数被执行完成后,会将这个参数传递给对应的槽函数。
例子:
实现一个功能:通过UI设计窗口创建两个按钮控件pushButton_1和pushButton_2。点击pushButton_1后窗口标题变为"标题一",点击pushButton_2窗口标题变为"标题二"。如下:
如果信号和槽连接,允许两者的参数个数可以不一致(信号函数的参数个数多于槽函数),但参数类型必须一致,这样的设计相较于参数数量必须一致的好处是什么?
情况:一个槽可能连接多个信号,如果要求参数必须一致才可连接,信号绑定槽的要求就会变高,如果一个槽的参数个数是1,那么参数个数是2的信号就无法绑定槽,尽管此时信号的第一个参数类型和这个槽的参数类型相同,也无法进行连接,这就导致了信号和槽之间的连接不够灵活。
五、Q_OBJECT宏
Qt中如果要让某个类可以使用信号和槽(可以在这个类中定义信号函数和槽函数),则必须要在这个类的最开始加上Q_OBJECT。
qt创建项目后会自动qt会自动帮我们加上了这个宏,如果没加这个宏使用了信号槽则会编译出错。
六、断开信号和槽的连接
使用disconnect断开信号和槽的连接,disconnect和connect在接口上的用法基本一致。断开信号和槽的连接比较少见,调用disconnect一般是为了更换一个信号连接的槽函数,disconnect后这个信号就可以连接其他新的槽函数了。
注意:
qt中的信号和槽中支持一个信号绑定多个槽,一个槽被多个信号绑定。
例子:
现在要设计这样一个功能:在UI窗口设计中创建按钮pushButton和pushButton_1,点击pushButton后将窗口标题改为"这是旧窗口标题",点击pushButton_1后,然后每次点击pushButton窗口标题变为”这是新窗口标题“。如下:
七、lambda作为槽函数
例子:
这个例子中lambda通过值传递捕捉了外层作用与的所有变量。
qt中关于lambda引用传递捕捉
在使用lambda表达式时要注意捕捉到的变量的生命周期,如果捕获了一个指针,该指针指向的空间在lambda访问的时候已经被释放,那么就会出现访问错误。
如果上面的例子中的lambda采用引用传递捕捉会出现什么情况?
当按钮button被点击,发送信号,则槽函数即lambda表达式会被执行,此时无法执行button->move(100,100)及以下代码。
原因:由于button(是一个指针变量,存的是地址)是一个栈区的变量,Widget的构造函数调用完后,button变量会被销毁,因此如果槽函数被触发执行,捕捉的是button的引用,想一下button变量都销毁了,那这个捕捉的引用也就指向垃圾空间了,但是如果button是Widget类的成员变量那么就不会出现错误。
Qt 5及更高版本的,都会默认在qt项目文件中.pro文件中加上CONFIG += c++11,但是Qt 5以下的版本则不会默认加,加上后就可以按照C++11来编译了。