Qt核心特色--信号与槽
(2011-05-09 09:34:05)
信号-槽机制可以携带任意类型、任意数量的参数,而且完全是安全的,不会引起系统的崩溃。
信号:当一个事件发生时,触发一个信号。
槽:响应一个特别的信号。
对象中的信号不是真正的函数,编译时由moc自动产生,返回类型void,槽是函数
Qt的窗口部件有很多预定的信号与槽,我们可以继承这些部件,实现我们自己感兴趣的信号与槽。
信号的参数要和槽的参数类型兼容,信号的参数个数可以大于等于槽的参数个数,多余的参数就被槽忽略。
所有直接或者间接继承QObject的类都可以包含信号与槽。
信号不必关心与它相连的槽。
槽可以用来接收信号,但它是一般的成员函数。它也不用关心与它相连的信号。
信号与槽的连接:1. 信号<--->1个或者多个槽;2. 1个或者多个信号<--->槽;3. 信号<--->槽
DEMO:
语句,所有的槽将延后执行。如果多个槽与一个信号相连,则槽的执行顺序是任意的!
如果想知道发送信号的对象的情况,使用QObject::sender(),它返回发送信号的对象的指针。
如果有多个信号同时连接同一个槽,而且这个槽需要对不同的信号做不同的处理,则要使用QSignalMapper。
假设有3个按钮分别打开不同的文件:
信号:当一个事件发生时,触发一个信号。
槽:响应一个特别的信号。
信号槽的处理借助于moc(Meta Object Compiler)工具,Q_OBJECT宏通知compiler使用moc工具进行moc扩展。
信号的参数要和槽的参数类型兼容,信号的参数个数可以大于等于槽的参数个数,多余的参数就被槽忽略。
所有直接或者间接继承QObject的类都可以包含信号与槽。
信号不必关心与它相连的槽。
槽可以用来接收信号,但它是一般的成员函数。它也不用关心与它相连的信号。
信号与槽的连接:1. 信号<--->1个或者多个槽;2. 1个或者多个信号<--->槽;3. 信号<--->槽
DEMO:
#include <QObject>
class Counter : public QObject
{
Q_OBJECT
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
private:
int m_value;
}
所有包含信号与槽的类,必须在类的声明的头部添加宏Q_OBJECT,而且必须直接或者间接继承QObject。
void Counter::setValue(int value)
{
if (value != m_value) {
m_value = value;
emit valueChanged(value);
}
}
Counter a, b;
QObject::connect(&a, SIGNAL(valueChanged(int)),
&b, SLOT(setValue(int)));
a.setValue(12); // a.value() == 12, b.value() == 12
b.setValue(48); // a.value() == 12, b.value() == 48
通常情况下,当一个信号触发时,与之相连的槽会马上执行,它独立于Qt的GUI事件循环。当所有的槽都返回时,才执行emit语句后面的语句。它不同于排队连接,排队连接在执行emit语句后,马上执行其后面的语句,所有的槽将延后执行。如果多个槽与一个信号相连,则槽的执行顺序是任意的!
如果想知道发送信号的对象的情况,使用QObject::sender(),它返回发送信号的对象的指针。
如果有多个信号同时连接同一个槽,而且这个槽需要对不同的信号做不同的处理,则要使用QSignalMapper。
假设有3个按钮分别打开不同的文件:
signalMapper = new QSignalMapper(this);
signalMapper->setMapping(taxFileButton, QString("taxfile.txt"));
signalMapper->setMapping(accountFileButton, QString("accountsfile.txt"));
signalMapper->setMapping(reportFileButton, QString("reportfile.txt"));
connect(taxFileButton, SIGNAL(clicked()),
signalMapper, SLOT (map()));
connect(accountFileButton, SIGNAL(clicked()),
signalMapper, SLOT (map()));
connect(reportFileButton, SIGNAL(clicked()),
signalMapper, SLOT (map()));
connect(signalMapper, SIGNAL(mapped(const QString &)),
this, SLOT(readFile(const QString &)));
如果不想使用Qt的信号与槽机制,在project文件中加入:
CONFIG += no_keywords
信号与槽机制是比较灵活的,但有些局限性我们必须了解,这样在实际的使用过程中做到有的放矢,避免产生一些错误。下面就介绍一下这方面的情况。
1 .信号与槽的效率是非常高的,但是同真正的回调函数比较起来,由于增加了灵活性,因此在速度上还是有所损失,当然这种损失相对来说是比较小的,通过在一台 i586-133 的机器上测试是 10 微秒(运行 Linux),可见这种机制所提供的简洁性、灵活性还是值得的。但如果我们要追求高效率的话,比如在实时系统中就要尽可能的少用这种机制。
2 .信号与槽机制与普通函数的调用一样,如果使用不当的话,在程序执行时也有可能产生死循环。因此,在定义槽函数时一定要注意避免间接形成无限循环,即在槽中再次发射所接收到的同样信号。例如 , 在前面给出的例子中如果在 mySlot() 槽函数中加上语句 emit mySignal() 即可形成死循环。
3 .如果一个信号与多个槽相联系的话,那么,当这个信号被发射时,与之相关的槽被激活的顺序将是随机的。
4. 宏定义不能用在 signal 和 slot 的参数中。
既然 moc 工具不扩展 #define,因此,在 signals 和 slots 中携带参数的宏就不能正确地工作,如果不带参数是可以的。
5. 构造函数不能用在 signals 或者 slots 声明区域内。
6. 函数指针不能作为信号或槽的参数。
7. 信号与槽不能有缺省参数。
8. 信号与槽也不能携带模板类参数。
9. 嵌套的类不能位于信号或槽区域内,也不能有信号或者槽。
10. 友元声明不能位于信号或者槽声明区内。