成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

詳解 QT 源碼之QT元對象系統和信號槽機制

移動開發
本文介紹的是QT 源碼之QT元對象系統和信號槽機制,QT的信號和槽機制是用來在對象間通訊的方法,當一個特定事件發生的時候,signal會被 emit 出來,slot 調用是用來響應相應的 signal 的.

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

下面的代碼是我寫的一個繼承QLabel的類,是QLabel可以響應鼠標單擊的消息。

  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的源碼內部,來看一看QT是如何實現singal和slots的。

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

現在我們要看一下Q_OBJECT宏到底是什么?他與main.moc有什么關聯呢?相信我介紹完了Q_OBJECT宏之后,再看main.moc就能明白其所有函數的含義了。我們先到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首先調用了 Q_OBJECT_CHECK (插入了一個 qt_check_for_QOBJECT_macro 的 template function)

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

  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相關的,我們暫時不用去管它。  
  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         最后是接口函數qt_metacall,他的作用是查表,調用函數  
  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. 我們來仔細看看 QMetaObject,這就是meta-object的數據結構定義   
  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是元數據的簽名

qt_meta_data_ClickedLabel,是元數據的索引數組指針。

qt_meta_data_ClickedLabel中這些莫名其妙的數字是如何變成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構建起來。這中間的轉換是通過

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

這個函數來完成的。

#p#

下面我們著重看看幾個與 signal/slot 相關的代碼

qobject.cpp 文件中關于 QObject::connect() 函數的代碼,

  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;  

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

最后得到method的元數據QMetaMethod,然后調用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是為了防止多線程操作引起問題。

一旦我們發送了信號,就應該調用相關中的方法了,這個過程其實就是查找全局的connect列表的過程。真正發出信號是在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的數據,首先判斷是否為阻塞設置  
  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.   // 打上使用標記,因為可能是放在隊列中  
  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設置當前發送者  
  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.    // 調用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.     }  

響應信號也是在main.moc中實現的。

 

  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;  

小結: QT 源碼之QT元對象系統和信號槽機制的內容介紹完了,希望本文對你有所幫助!

責任編輯:zhaolei 來源: CSDN博客
相關推薦

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 事件機制

2011-07-05 18:32:52

QT 信號 機制

2011-07-05 18:40:19

QT 信號 機制

2011-06-20 15:40:19

QT 信號

2011-06-13 10:21:25

QT 信號 槽機制

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 對象機制

2011-06-30 17:51:17

QT 元類型 線程

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-23 15:10:39

Qt 窗體

2011-06-24 17:38:09

Qt 坐標 事件

2021-12-23 15:07:40

QtC++編譯程序
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧洲一区二区视频 | 日本黄视频在线观看 | 国产成人精品一区二区三区四区 | 午夜精品在线观看 | 亚洲国产成人精品在线 | 美女天堂 | 羞羞的视频免费在线观看 | 中文字幕亚洲精品 | 久干网| 狠狠亚洲 | 天天干精品 | 亚洲午夜网 | 久久成人av电影 | 四虎影院新地址 | 国产精品视频导航 | 国产第1页 | 欧美13videosex性极品 | 99精品在线观看 | 91精品欧美久久久久久久 | 欧美aⅴ在线观看 | 91精品国产综合久久久久 | 99精品一区二区三区 | 欧美黄色片 | 欧美大片在线观看 | 伊人电影院av | 黄色大片在线播放 | 亚洲精品一区二区三区中文字幕 | 亚洲一区二区精品视频 | 久久精点视频 | 毛片一级片| 欧美日韩中文字幕在线播放 | 91久久精品国产91久久 | 自拍 亚洲 欧美 老师 丝袜 | 国产精品久久久久久238 | 伊人免费在线 | 影视一区| 一区二区三区在线播放 | 久久久久久国产精品免费免费狐狸 | 国产96色在线 | 欧美电影在线 | 精产国产伦理一二三区 |