希望本是无所谓有,无所谓无的,这正如脚下的路,其实地上本没有路,走的人多了,也便成了路....原创不易,文章会持续更新,感谢您的关注
1.信号和槽函数的定义和添加
Qt本身提供了很多控件,这个控件也定义了很多信号和槽函数。
(1)方式一:直接通过界面设计器来添加,这种方式最常用。
(2)方式二:直接通过信号与槽编辑器来添加。
2.什么时候需要定义信号
当我们需要对界面的内容做改变的时候就需要定义自己的信号。
子线程想更新界面的内容,如果此时子线程直接调用控件的槽函数,更改控件的内容,系统会宕掉。每个控件都有对应的槽函数来修改界面,譬如Label控件,setText( )函数就是Label控件用来修改控件文字的槽函数,如果将Label定义为一个全局变量,在子线程中使用该全局变量修改Label的内容,软件可能会宕掉。
因为QT的主线程就是用来处理信号槽的,该主线程也可能会调用到该Label控件的setText( )槽函数,子线程和主线程同时调用,系统就会宕掉。
场景:譬如用子线程上传文件,同时需要更新UI进度条,如果此时子线程直接调用进度条线程的槽函数,软件会宕掉。
我们可以在主线程中自定义一个槽函数,在槽函数中调用进度条控件的槽函数,然后在子线程中自定义一个信号,最后只有在主线程的初始化函数中使用connect将子线程的信号与主线程的槽函数进行绑定,每当子线程需要更新UI时,只需要调用一次自定义信号即可。
信号是子线程的,槽函数是主线程的,这样两者直接互不干扰,我们绑定好之后,QT会负责将两者进行关联。
3.Q_OBJECT说明
如果你定义的类需要添加信号槽,就需要在头文件的第一行添加这个宏。这个宏对于我们没有任何意义,它主要是给MOC程序用的。
我们调用connect绑定好信号与槽之后,当我们发送信号时,QT为什么就知道要调用哪个函数呢?信号会被发送到队列中,主线程的循环会读取队列,然后调用该消息对应的处理函数。
一个信号可以绑定多个槽函数来接收自己,一个槽函数也可以绑定多个信号。这种复杂的关联,仅仅靠一个connect函数的绑定还不够,还需要MOC程序自动帮我们生成一部分代码,MOC程序只会处理头文件中带Q_OBJECT宏的类。
Q_OBJECT其实就是一个字符串,MOC会查询所有头文件,当匹配到Q_OBJECT字符串时,就会帮我们生成信号槽的部分代码。
如果自己的类需要使用信号槽,但又没有加这个宏,会导致槽收不到信号等问题。QT本身也有一些BUG,如果明明加了这个宏,但在编译信息中仍然没有看到MOC程序被调用,生成对应的文件。原因:Q_OBJECT前后可能存在某些其他字符,将Q_OBJECT删除,重新输入一遍即可。
总结:
QT会查询头文件中是否包含Q_OBJECT字符串,如果包含,会进一步查询关键字signals,函数定义前面有singnals的,就说明是一个信号,MOC会实现信号函数的定义(注意,我们定义的信号函数只有声明,没有定义,其定义/函数的具体的实现代码由MOC自动完成),同时也会查询函数声明的前面是否包含public slots,QT会生成“槽函数和信号的关联”这部分代码。
当我们使用界面编辑器给一个button的点击信号绑定widget的close槽函数时,观察编译信息:
(1)系统自动在头文件中添加了Q_OBJECT宏。
(2)编译信息。 moc_widget.cpp就是moc程序自动生成的,该文件在debug或release目录下,可以打开研究一下。
(3)将头文件中的Q_OBJECT删除,再观察编译信息(编译报错)。 如果信号与槽没有起作用,可以观察编译过程,看看moc程序的执行过程是否正确。
![02b8e5d6e12693a8de8177a5245a93c7.png](https://i-blog.csdnimg.cn/blog_migrate/80682f7c78c3730d7e00a88d9dab2636.png)
专注于 嵌入式 和 qt知识分享
欢迎扫码关注
“嵌入式工程师成长之路”
![73927c224a84715d746e9eb58d2e7491.gif](https://i-blog.csdnimg.cn/blog_migrate/9b10877d0060aca29177bbbc100306b3.gif)
![73927c224a84715d746e9eb58d2e7491.gif](https://i-blog.csdnimg.cn/blog_migrate/9b10877d0060aca29177bbbc100306b3.gif)