Qt的信号本质是函数,且默认也只能为公有函数(这样可以在任何地方进行信号发送)
前置条件
Qt的信号支持需要Qt的元对象系统的支持,元对象系统需要一下条件
- 类必须继承与QObject
- 类内必须私有声明Q_OBJECT宏
- 需有元对象编译器(moc)
声明和实现
在我们定义的元对象类中可以进行信号的声明,默认形式为 :
void mySignal(paramterLists);//返回值只能是void
在我们的代码中,我们只能声明信号,而不能为信号编写实现代码。实际的信号实现是由元对象系统自动生成的。
当我们的.cpp文件被编译时,如果在类的声明中包含了Q_OBJECT宏,编译器会使用元对象编译器(MOC)来创建一个新的.cpp源文件,该源文件以moc_开头。这个新生成的源文件将参与编译和链接过程,并包含信号的实现代码。
元对象编译器会根据类的声明中的信号定义信息,自动生成信号的实现代码。这些实现代码包括信号的触发、连接和处理逻辑等。生成的源文件会在编译过程中被编译为目标代码,并与其他源文件一起链接到最终的可执行文件中。
通过这种方式,我们可以在代码中声明信号,而不需要手动编写信号的实现代码。元对象系统会负责自动生成并处理信号的实现,使我们能够方便地使用信号和槽机制来实现事件驱动的功能。
例如下面的函数,就是自动生成的文件moc_myTest.cpp中的信号定义。
void PlotsPanel::show_a_text_message(QString _t1)
{
void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 6, _a);
}
调用
在实际的代码中,我们可以使用emit宏进行调用,即使实际的代码中emit宏定义是一个空的,我们还是要写上,这样别人看到就可以立即知道这事一个信号发送的调用。
下面是Qt中emit宏的定义
#ifndef QT_NO_EMIT
# define emit
#endif
我们可以使用下面两种形式发送信号:
emit(mySignal(paramterLists));
//或者
emit mySignal(paramterLists);
上面两种形式最终经过预编译,都会变成一样的
mySignal(paramterLists)
运行
QT程序只有一个应用程序类QApplication,QApplication也只有一个事件处理函数exex(),我们整个程序的运行期间都是在不断的QApplication的exex()函数。
从名字就可以知道exex是进行事件处理的函数,但它同样可以处理信号,信号被发送后会被放入到事件队列中,但它不是一个事件。
exex()函数内部从事件队列中取出信号,会根据信号和槽的绑定形式来调用对应的槽函数,如果是直接绑定,这槽函会直接在exec()函数内部执行,如果不是且为多线程,这槽函数会进入另一个线程中运行。
此时注意我们需要避免任何操作界面的操作放在非主线程中运行,这样会大概率导致两个线程同时访问UI控件资源而崩溃