自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

詳解 QT 源碼之QT元對象系統(tǒng)和信號槽機(jī)制

移動開發(fā)
本文介紹的是QT 源碼之QT元對象系統(tǒng)和信號槽機(jī)制,QT的信號和槽機(jī)制是用來在對象間通訊的方法,當(dāng)一個特定事件發(fā)生的時候,signal會被 emit 出來,slot 調(diào)用是用來響應(yīng)相應(yīng)的 signal 的.

QT 源碼之QT元對象系統(tǒng)和信號槽機(jī)制是本文要介紹的內(nèi)容。QT信號機(jī)制是用來在對象間通訊的方法,當(dāng)一個特定事件發(fā)生的時候,signal會被 emit 出來,slot 調(diào)用是用來響應(yīng)相應(yīng)的 signal 的。簡單點說就是如何在一個類的一個函數(shù)中觸發(fā)另一個類的另一個函數(shù)調(diào)用,而且還要把相關(guān)的參數(shù)傳遞過去.好像這和回調(diào)函數(shù)也有點關(guān)系,但是消息機(jī)制可比回調(diào)函數(shù)有用多了,也復(fù)雜多了。

下面的代碼是我寫的一個繼承QLabel的類,是QLabel可以響應(yīng)鼠標(biāo)單擊的消息。

  1. view plaincopy to clipboardprint?  
  2. #include <QLabel>     
  3. #include <QWidget>     
  4. #include <QMessageBox>     
  5. #include <QApplication>     
  6. class ClickedLabel : public QLabel     
  7. {     
  8.     Q_OBJECT     
  9. signals:     
  10.     void Clicked(ClickedLabel* clicked);     
  11. public:     
  12.     ClickedLabel(const QString &text,QWidget *parent=0): QLabel(text,parent){   };     
  13.     ~ClickedLabel() {};     
  14. protected:     
  15.     void mouseReleaseEvent( QMouseEvent* ){emit Clicked(this);};     
  16. public slots:     
  17.     void OnCLicked( ClickedLabel* ) {QMessageBox::information(topLevelWidget(), "Message from Qt", "Label Clicked!");   };     
  18. };     
  19. #include "main.moc"     
  20. int main(int argc,char* argv[])     
  21. {     
  22.     QApplication app(argc,argv);     
  23.     ClickedLabel label("<h2>test</h2>");     
  24.     QObject::connect( &label, SIGNAL( Clicked(ClickedLabel*) ),&label, SLOT( OnCLicked(ClickedLabel*) ) ) ;     
  25.     label.show();     
  26.     return app.exec();     
  27. }    
  28. #include <QLabel> 
  29. #include <QWidget> 
  30. #include <QMessageBox> 
  31. #include <QApplication> 
  32. class ClickedLabel : public QLabel  
  33. {  
  34.  Q_OBJECT  
  35. signals:  
  36.  void Clicked(ClickedLabel* clicked);  
  37. public:  
  38.  ClickedLabel(const QString &text,QWidget *parent=0): QLabel(text,parent){ };  
  39.  ~ClickedLabel() {};  
  40. protected:  
  41.  void mouseReleaseEvent( QMouseEvent* ){emit Clicked(this);};  
  42. public slots:  
  43.  void OnCLicked( ClickedLabel* ) {QMessageBox::information(topLevelWidget(), "Message from Qt", "Label Clicked!"); };  
  44. };  
  45. #include "main.moc"  
  46. int main(int argc,char* argv[])  
  47. {  
  48.  QApplication app(argc,argv);  
  49.  ClickedLabel label("<h2>test</h2>");  
  50.  QObject::connect( &label, SIGNAL( Clicked(ClickedLabel*) ),&label, SLOT( OnCLicked(ClickedLabel*) ) ) ;  
  51.  label.show();  
  52.  return app.exec();  

這段代碼很簡單,講述了QT的singal和slot的使用。下面我們就深入QT的源碼內(nèi)部,來看一看QT是如何實現(xiàn)singal和slots的。

#include “main.moc” 的意思就是使編譯器找到moc對Q_OBJECT處理后的標(biāo)準(zhǔn)C++文件。編譯的時候我們需要首先在該目錄中使用 qmake -project 生成一個 .pro 文件,該文件含有工程細(xì)節(jié),然后使用 qmake 產(chǎn)生 Makefile,最后 nmake 就可以產(chǎn)生可執(zhí)行文件了。我們看看在nmake之后除了生成目標(biāo)代碼和可執(zhí)行文件之外,還有一個main.moc文件,這個文件是moc產(chǎn)生的一個中間文件。

現(xiàn)在我們要看一下Q_OBJECT宏到底是什么?他與main.moc有什么關(guān)聯(lián)呢?相信我介紹完了Q_OBJECT宏之后,再看main.moc就能明白其所有函數(shù)的含義了。我們先到objectdefs.h 文件中看一下Q_OBJECT宏的定義:

  1. #define Q_OBJECT \  
  2. public: \  
  3.  Q_OBJECT_CHECK \  
  4.  static const QMetaObject staticMetaObject; \  
  5.  virtual const QMetaObject *metaObject() const; \  
  6.  virtual void *qt_metacast(const char *); \  
  7.  QT_TR_FUNCTIONS \  
  8.  virtual int qt_metacall(QMetaObject::Call, int, void **); \  
  9. private: 

1首先調(diào)用了 Q_OBJECT_CHECK (插入了一個 qt_check_for_QOBJECT_macro 的 template function)

2  然后是全局常量 QMetaObject 對象,因此可以用 QClassname::staticMetaObject 直接訪問,另外提供了兩個接口函數(shù) metaObject() 用于不同的 class 返回自己的 staticMetaObject、qt_metacast() 用于轉(zhuǎn)換,我們在 moc 產(chǎn)生的文件里面可以找到這兩個接口的實現(xiàn):

  1. const QMetaObject *ClickedLabel::metaObject() const  
  2. {  
  3.     return &staticMetaObject;  
  4. }  
  5. void *ClickedLabel::qt_metacast(const char *_clname)  
  6. {  
  7.     if (!_clname) return 0;  
  8.     if (!strcmp(_clname, qt_meta_stringdata_ClickedLabel))  
  9.         return static_cast<void*>(const_cast< ClickedLabel*>(this));  
  10.     return QLabel::qt_metacast(_clname);  
  11. }  
  12. 3  宏QT_TR_FUNCTIONS是和i18n相關(guān)的,我們暫時不用去管它。  
  13. #  define QT_TR_FUNCTIONS \  
  14.     static inline QString tr(const char *s, const char *c = 0) \  
  15.         { return staticMetaObject.tr(s, c); }  
  16.  
  17. 4         最后是接口函數(shù)qt_metacall,他的作用是查表,調(diào)用函數(shù)  
  18. int ClickedLabel::qt_metacall(QMetaObject::Call _c, int _id, void **_a)  
  19. {  
  20.     _id = QLabel::qt_metacall(_c, _id, _a);  
  21.     if (_id < 0)  
  22.         return _id;  
  23.     if (_c == QMetaObject::InvokeMetaMethod) {  
  24.         switch (_id) {  
  25.         case 0: Clicked((*reinterpret_cast< ClickedLabel*(*)>(_a[1]))); break;  
  26.         case 1: OnCLicked((*reinterpret_cast< ClickedLabel*(*)>(_a[1]))); break;  
  27.         }  
  28.         _id -2;  
  29.     }  
  30.     return _id;  
  31. }  
  32. 我們來仔細(xì)看看 QMetaObject,這就是meta-object的數(shù)據(jù)結(jié)構(gòu)定義   
  33. struct Q_CORE_EXPORT QMetaObject  
  34. {  
  35.  const char *className() const;  
  36.  const QMetaObject *superClass() const;  
  37.  QObject *cast(QObject *obj) const;  
  38.  // ...  
  39.  struct { // private data  
  40.    const QMetaObject *superdata;  
  41.    const char *stringdata;  
  42.    const uint *data;  
  43.    const void *extradata;  
  44.  } d;  
  45. } ; 

#p#

下面看看我們生成的具體的代碼:

  1. static const uint qt_meta_data_ClickedLabel[] = {  
  2.  // content:  
  3.        1,       // revision  
  4.        0,       // classname  
  5.        0,    0, // classinfo  
  6.        2,   10, // methods  
  7.       0,    0, // properties  
  8.        0,    0, // enums/sets  
  9.  // signals: signature, parameters, type, tag, flags  
  10.       22,   14,   13,   13, 0x05,  
  11.  // slots: signature, parameters, type, tag, flags  
  12.       45,   13,   13,   13, 0x0a,  
  13.        0        // eod  
  14. };  
  15. static const char qt_meta_stringdata_ClickedLabel[] = {  
  16.     "ClickedLabel\0\0clicked\0Clicked(ClickedLabel*)\0"  
  17.     "OnCLicked(ClickedLabel*)\0"  
  18. };  
  19. const QMetaObject ClickedLabel::staticMetaObject = {  
  20.     { &QLabel::staticMetaObject, qt_meta_stringdata_ClickedLabel,  
  21.       qt_meta_data_ClickedLabel, 0 }  
  22. }; 

這就是meta-object的初始化代碼,meta-object包含所有繼承QObject類的元對象信息。包括class name, superclass name, properties, signals and slots等等。

ClickedLabel的staticMetaObject初始化用到了QLabel::staticMetaObject,

qt_meta_stringdata_ClickedLabel是元數(shù)據(jù)的簽名

qt_meta_data_ClickedLabel,是元數(shù)據(jù)的索引數(shù)組指針。

qt_meta_data_ClickedLabel中這些莫名其妙的數(shù)字是如何變成QMetaObject的呢?

在qmetaobject.cpp中我們找到了QMetaObjectPrivate的定義:

  1. struct QMetaObjectPrivate  
  2. {  
  3.     int revision;  
  4.     int className;  
  5.     int classInfoCount, classInfoData;  
  6.     int methodCount, methodData;  
  7.     int propertyCount, propertyData;  
  8.     int enumeratorCount, enumeratorData;  
  9. }; 

很明顯,利用qt_meta_data_ClickedLabel中存儲的索引和qt_meta_stringdata_ClickedLabel中存儲的值,我們很容易將QMetaObject構(gòu)建起來。這中間的轉(zhuǎn)換是通過

  1. static inline const QMetaObjectPrivate *priv(const uint* data)  
  2. { return reinterpret_cast<const QMetaObjectPrivate*>(data); } 

這個函數(shù)來完成的。

#p#

下面我們著重看看幾個與 signal/slot 相關(guān)的代碼

qobject.cpp 文件中關(guān)于 QObject::connect() 函數(shù)的代碼,

  1. bool QObject::connect(const QObject *sender, const char *signal,  
  2.                      const QObject *receiver, const char *method,  
  3.                       Qt::ConnectionType type)  
  4. {  
  5.     {  
  6.         const void *cbdata[] = { sender, signal, receiver, method, &type };  
  7.         if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))  
  8.             return true;  
  9.     }  
  10. #ifndef QT_NO_DEBUG  
  11.     bool warnCompat = true;  
  12. #endif  
  13.     if (type == Qt::AutoCompatConnection) {  
  14.         type = Qt::AutoConnection;  
  15. #ifndef QT_NO_DEBUG  
  16.         warnCompat = false;  
  17. #endif  
  18.     }  
  19.     //判斷是否是NULL  
  20.     if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {  
  21.         qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",  
  22.                  sender ? sender->metaObject()->className() : "(null)",  
  23.                  (signal && *signal) ? signal+1 : "(null)",  
  24.                  receiver ? receiver->metaObject()->className() : "(null)",  
  25.                  (method && *method) ? method+1 : "(null)");  
  26.         return false;  
  27.     }  
  28.     QByteArray tmp_signal_name;  
  29.     if (!check_signal_macro(sender, signal, "connect", "bind"))  
  30.         return false;  
  31.     const QMetaObject *smeta = sender->metaObject();  
  32.     ++signal; //skip code  
  33.     int signal_index = smeta->indexOfSignal(signal);  
  34.     if (signal_index < 0) {  
  35.         // check for normalized signatures  
  36.         tmp_signal_name = QMetaObject::normalizedSignature(signal).prepend(*(signal - 1));  
  37.         signal = tmp_signal_name.constData() + 1;  
  38.         signal_index = smeta->indexOfSignal(signal);  
  39.        if (signal_index < 0) {  
  40.             err_method_notfound(QSIGNAL_CODE, sender, signal, "connect");  
  41.             err_info_about_objects("connect", sender, receiver);  
  42.             return false;  
  43.         }  
  44.     }  
  45.     QByteArray tmp_method_name;  
  46.     int membcode = method[0] - '0';  
  47.     if (!check_method_code(membcode, receiver, method, "connect"))  
  48.         return false;  
  49.     ++method; // skip code  
  50.     const QMetaObject *rmeta = receiver->metaObject();  
  51.     int method_index = -1;  
  52.     switch (membcode) {  
  53.     case QSLOT_CODE:  
  54.         method_index = rmeta->indexOfSlot(method);  
  55.         break;  
  56.     case QSIGNAL_CODE:  
  57.         method_index = rmeta->indexOfSignal(method);  
  58.         break;  
  59.     }  
  60.     if (method_index < 0) {  
  61.         // check for normalized methods  
  62.         tmp_method_name = QMetaObject::normalizedSignature(method);  
  63.         method = tmp_method_name.constData();  
  64.         switch (membcode) {  
  65.         case QSLOT_CODE:  
  66. method_index = rmeta->indexOfSlot(method);  
  67.             break;  
  68.         case QSIGNAL_CODE:  
  69.             method_index = rmeta->indexOfSignal(method);  
  70.            break;  
  71.         }  
  72.     }  
  73.     if (method_index < 0) {  
  74.         err_method_notfound(membcode, receiver, method, "connect");  
  75.         err_info_about_objects("connect", sender, receiver);  
  76.         return false;  
  77.     }  
  78.     if (!QMetaObject::checkConnectArgs(signal, method)) {  
  79.         qWarning("QObject::connect: Incompatible sender/receiver arguments"  
  80.                  "\n\t%s::%s --> %s::%s",  
  81.                  sender->metaObject()->className(), signal,  
  82.                  receiver->metaObject()->className(), method);  
  83.         return false;  
  84.     }  
  85.     int *types = 0;  
  86.     if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)  
  87.             && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes())))  
  88.         return false;  
  89. #ifndef QT_NO_DEBUG  
  90.     {  
  91.         QMetaMethod smethod = smeta->method(signal_index);  
  92.         QMetaMethod rmethod = rmeta->method(method_index);  
  93.         if (warnCompat) {  
  94.             if(smethod.attributes() & QMetaMethod::Compatibility) {  
  95.                 if (!(rmethod.attributes() & QMetaMethod::Compatibility))  
  96.                     qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", smeta->className(), signal);  
  97.             } else if(rmethod.attributes() & QMetaMethod::Compatibility && membcode != QSIGNAL_CODE) {  
  98.                 qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)",  
  99.                          smeta->className(), signal, rmeta->className(), method);  
  100.             }  
  101. }  
  102.     }  
  103. #endif  
  104.     QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);  
  105.     const_cast<QObject*>(sender)->connectNotify(signal - 1);  
  106.     return true;  

上面這段代碼首先調(diào)用了 QInternal 這個 namespace 里面 activateCallbacks 這個函數(shù),然后根據(jù) QMetaObject 信息檢查了 sender、receiver 以及對應(yīng) signal/slots 的匹配性,得到元數(shù)據(jù)類。把 signal/slot 字符串轉(zhuǎn)換成為了對應(yīng)的 index,然后檢查信號的參數(shù)是否一致,函數(shù)的參數(shù)可以小于信號函數(shù)的參數(shù)。

最后得到method的元數(shù)據(jù)QMetaMethod,然后調(diào)用QMetaObject::connect的方法。

  1. bool QMetaObject::connect(const QObject *sender, int signal_index,  
  2.                           const QObject *receiver, int method_index, int type, int *types)  
  3. {  
  4.     QConnectionList *list = ::connectionList();  
  5.     if (!list)  
  6.         return false;  
  7.     QWriteLocker locker(&list->lock);  
  8.     list->addConnection(const_cast<QObject *>(sender), signal_index,  
  9.                         const_cast<QObject *>(receiver), method_index, type, types);  
  10.     return true;  

QMetaObject::connect代碼中QWriteLocker是為了防止多線程操作引起問題。

一旦我們發(fā)送了信號,就應(yīng)該調(diào)用相關(guān)中的方法了,這個過程其實就是查找全局的connect列表的過程。真正發(fā)出信號是在main.moc中。

  1. void ClickedLabel::Clicked(ClickedLabel * _t1)  
  2. {  
  3.     void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };  
  4.     QMetaObject::activate(this, &staticMetaObject, 0, _a);  
  5. }  
  6. void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv)  
  7. {  
  8.  // 這里得到的是QObject的數(shù)據(jù),首先判斷是否為阻塞設(shè)置  
  9.     if (sender->d_func()->blockSig)  
  10.         return;  
  11.  // 得到全局鏈表  
  12.     QConnectionList * const list = ::connectionList();  
  13.     if (!list)  
  14.         return;  
  15.     QReadLocker locker(&list->lock);  
  16.     void *empty_argv[] = { 0 };  
  17.     if (qt_signal_spy_callback_set.signal_begin_callback != 0) {  
  18.         locker.unlock();  
  19.         qt_signal_spy_callback_set.signal_begin_callback(sender, from_signal_index,   argv ? argv : empty_argv);  
  20.         locker.relock();  
  21.     }  
  22.  // 在sender的哈希表中得到sender的連接  
  23.     QConnectionList::Hash::const_iterator it = list->sendersHash.find(sender);  
  24.     const QConnectionList::Hash::const_iterator end = list->sendersHash.constEnd();  
  25.     if (it == end) {  
  26.         if (qt_signal_spy_callback_set.signal_end_callback != 0) {  
  27.             locker.unlock();  
  28.           qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);  
  29.             locker.relock();  
  30.         }  
  31.         return;  
  32.     }  
  33.     QThread * const currentThread = QThread::currentThread();  
  34.     const int currentQThreadId = currentThread ? QThreadData::get(currentThread)->id : -1;  
  35.  // 記錄sender連接的索引  
  36.     QVarLengthArray<int> connections;  
  37.     for (; it != end && it.key() == sender; ++it) {  
  38.         connections.append(it.value());  
  39.   // 打上使用標(biāo)記,因為可能是放在隊列中  
  40.         list->connections[it.value()].inUse = 1;  
  41.     }  
  42.     for (int i = 0; i < connections.size(); ++i) {  
  43.         const int at = connections.constData()[connections.size() - (i + 1)];  
  44.         QConnectionList * const list = ::connectionList();  
  45.   // 得到連接  
  46.         QConnection &c = list->connections[at];  
  47.         c.inUse = 0;  
  48.         if (!c.receiver || (c.signal < from_signal_index || c.signal > to_signal_index))  
  49.             continue;  
  50.   // 判斷是否放到隊列中  
  51.         // determine if this connection should be sent immediately or  
  52.         // put into the event queue  
  53.         if ((c.type == Qt::AutoConnection  
  54.              && (currentQThreadId != sender->d_func()->thread  
  55.                  || c.receiver->d_func()->thread != sender->d_func()->thread))  
  56.            || (c.type == Qt::QueuedConnection)) {  
  57.            ::queued_activate(sender, c, argv);  
  58.             continue;  
  59.         }  
  60.   // 為receiver設(shè)置當(dāng)前發(fā)送者  
  61.         const int method = c.method;  
  62.         QObject * const previousSender = c.receiver->d_func()->currentSender;  
  63.         c.receiver->d_func()->currentSender = sender;  
  64.         list->lock.unlock();  
  65.        if (qt_signal_spy_callback_set.slot_begin_callback != 0)  
  66.          qt_signal_spy_callback_set.slot_begin_callback(c.receiver, method, argv ? argv : empty_argv);  
  67. #if defined(QT_NO_EXCEPTIONS)  
  68.         c.receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);  
  69. #else  
  70.        try {  
  71.    // 調(diào)用receiver的方法  
  72.             c.receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);  
  73.  
  74.         } catch (...) {  
  75.             list->lock.lockForRead();  
  76.             if (c.receiver)  
  77.                 c.receiver->d_func()->currentSender = previousSender;  
  78.             throw;  
  79.         }  
  80. #endif  
  81.         if (qt_signal_spy_callback_set.slot_end_callback != 0)  
  82.             qt_signal_spy_callback_set.slot_end_callback(c.receiver, method);  
  83.         list->lock.lockForRead();  
  84.         if (c.receiver)  
  85.             c.receiver->d_func()->currentSender = previousSender;  
  86.     }  
  87.     if (qt_signal_spy_callback_set.signal_end_callback != 0) {  
  88.         locker.unlock();  
  89.         qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);  
  90.         locker.relock();  
  91.     }  

響應(yīng)信號也是在main.moc中實現(xiàn)的。

 

  1. int ClickedLabel::qt_metacall(QMetaObject::Call _c, int _id, void **_a)  
  2. {  
  3.     _id = QLabel::qt_metacall(_c, _id, _a);  
  4.     if (_id < 0)  
  5.         return _id;  
  6.     if (_c == QMetaObject::InvokeMetaMethod) {  
  7.         switch (_id) {  
  8.         case 0: Clicked((*reinterpret_cast< ClickedLabel*(*)>(_a[1]))); break;  
  9.         case 1: OnCLicked((*reinterpret_cast< ClickedLabel*(*)>(_a[1]))); break;  
  10.         }  
  11.         _id -2;  
  12.     }  
  13.     return _id;  

小結(jié): QT 源碼之QT元對象系統(tǒng)和信號槽機(jī)制的內(nèi)容介紹完了,希望本文對你有所幫助!

責(zé)任編輯:zhaolei 來源: CSDN博客
相關(guān)推薦

2011-06-23 14:40:13

Qt 信號

2011-06-09 09:45:35

Linux QT 信號

2011-06-15 14:38:01

QT 信號

2011-06-23 14:05:32

Qt 事件機(jī)制

2011-07-05 18:32:52

QT 信號 機(jī)制

2011-07-05 18:40:19

QT 信號 機(jī)制

2011-06-20 15:40:19

QT 信號

2011-06-13 10:21:25

QT 信號 槽機(jī)制

2011-06-28 15:47:13

Qt 信號

2011-06-23 13:25:42

QT 源碼 窗口

2011-06-23 11:16:39

Qt Excel

2011-06-24 10:05:51

QT 對象 父對象

2011-06-23 13:10:39

Python 對象機(jī)制

2011-06-23 15:32:05

Qt Windows消息

2023-10-07 08:21:35

PyQtPython

2011-06-28 16:18:24

Qt QObject

2011-06-30 17:51:17

QT 元類型 線程

2011-06-23 15:10:39

Qt 窗體

2011-06-24 17:38:09

Qt 坐標(biāo) 事件

2021-12-23 15:07:40

QtC++編譯程序
點贊
收藏

51CTO技術(shù)棧公眾號