1 先理解一下信号与槽
我们通常会在改变一个组件的同时,通知另一个组件做出响应。改变一个组件的时候,就会发送一个信号,写好的槽会对这个信号进行匹配,做出相应的相应。
槽和信号都是函数,具有类成员函数相同的属性。唯一不同的就是槽可以与信号进行连接。这种通信方式是安全的,因为信号的函数签名必须与信号的函数签名匹配上。实际中,信号函数的参数可以比槽的参数多,因为槽可以自动忽略无用的参数。
2 connect 函数就是用来将信号与槽之间的联系建立起来的
2.1 格式:
QObject::connect( 发送方, signal(..), 接收方, slot(...))
源码出connect函数是这样定义的connect(sender,SIGNAL(signal()),receiver,SLOT(slot()));
SIGNAL,SLOT宏的作用是将信号和槽函数转化为字符串并添加标识符。
2.2 在使用connect函数需要注意到的问题
(1) 信号的发送者必须是QObject的派生对象。
(2) 槽函数所在类的定义中必须添加宏Q_OBJECT
(3) 如果槽函数是on_控件命_信号名,那么不需要主动调用connect进行连接
2.3 关于connect函数的实现
代码与注释如下
bool QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type) { { const void *cbdata[] = { sender, signal, receiver, method, &type }; if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))//判断连接是否已经建立 return true; } if (sender == 0 || receiver == 0 || signal == 0 || method == 0) { qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", sender ? sender->metaObject()->className() : "(null)", (signal && *signal) ? signal+1 : "(null)", receiver ? receiver->metaObject()->className() : "(null)", (method && *method) ? method+1 : "(null)"); return false; } QByteArray tmp_signal_name; if (!check_signal_macro(sender, signal, "connect", "bind"))//判断信号是否合法 return false; const QMetaObject *smeta = sender->metaObject(); const char *signal_arg = signal; ++signal; //skip code int signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) { // check for normalized signatures tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); signal = tmp_signal_name.constData() + 1; signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) { err_method_notfound(sender, signal_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; } } //获取signal的索引 QByteArray tmp_method_name; int membcode = extract_code(method); if (!check_method_code(membcode, receiver, method, "connect")) return false; const char *method_arg = method; ++method; // skip code const QMetaObject *rmeta = receiver->metaObject(); int method_index = -1; switch (membcode) { case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; } if (method_index < 0) { // check for normalized methods tmp_method_name = QMetaObject::normalizedSignature(method); method = tmp_method_name.constData(); switch (membcode) { case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; } } if (method_index < 0) { err_method_notfound(receiver, method_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; } if (!QMetaObject::checkConnectArgs(signal, method)) { //判断signal和method是否兼容,通过判断其参数是否相同 qWarning("QObject::connect: Incompatible sender/receiver arguments" "\n %s::%s --> %s::%s", sender->metaObject()->className(), signal, receiver->metaObject()->className(), method); return false; } int *types = 0; if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection) && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes()))) return false; QMetaObject::connect(sender, signal_index, receiver, method_index, type, types); const_cast<QObject*>(sender)->connectNotify(signal - 1); //调用虚函数connectNotify表示connect执行完成 return true; }
3 信号与槽通过connect函数连接的一个例子
//MyStr.h # ifndef MYSTR # define MYSTR #include<QObject> #include<QString> class MyStr :public QObject{ Q_OBJECT //必须包含的宏 public: MyStr (){m_value = "zero";} QString value() const {return m_value;} public slots : void setValue(QString value ); signals: //信号 void valueChanged(QString newValue); private: QString m_value; }; #endif
#include"MyStr.h" void MyStr::setValue(QString value) { if(value != m_value)//如果这里不进行判断,那么a,b信号就会重复调用,从而进入死循环 { m_value = value; emit valueChanged(value); }
}
int main(int argc, char *argv[]) { MyStr a; MyStr b; QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SLOT(setValue(QString))); a.setValue("this is A"); return 0; }
当我们调用setValue函数时,将a.my_value设为了"this is A",也把信号valuechanged()发送了出去,被b.setValue接收,将b.my_value设为了"this is A",同时b又会把信号valuechanged发送出去,但此时因为没有建立连接所以没有接收者。
4 细节
一个信号可以连接多个槽
多个信号可以连接同一个槽
一个信号可以和另一个信号连接
QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SIGNAL(valueChanged(QString)));
连接是可以被删除的
QObject::disconnect(&b,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));
当连接的某一个对象被删除时,连接自动也就被删除了。