Qt rcc 与 moc

Qt4 Gossip: Qt资源系统

Qt 資源系統(Resource System)可以提供與平台無關的機制,讓您把應用程式的圖檔、語系檔、資料等儲存於可執行檔之中,避免相關的資源檔案遺失的問題,Qt資源系統是基於 qmake、rcc(resource compiler),並搭配QFile來使用,您必須在產生的.pro檔之中,告知資源群集檔(Resource Collection File)的位置與名稱。 Qt资源系统(Resource System)可以提供与平台无关的机制,让您把应用程式的图档、语系档、资料等储存于可执行档之中,避免相关的资源档案遗失的问题,Qt资源系统是基于 qmake、rcc(resource compiler),并搭配QFile来使用,您必须在产生的.pro档之中,告知资源群集档(Resource Collection File)的位置与名称。

資源群集檔的副檔名為.qrc,實際的內容為XML格式的檔案,當中告知了這個應用程式所要使用到的資源檔案,例如您想要將QListWidget 與QListWidgetItem中所使用到的圖檔儲存在可執行檔案之中,則可以撰寫一個resourcefile.qrc: 资源群集档的副档名为.qrc,实际的内容为XML格式的档案,当中告知了这个应用程式所要使用到的资源档案,例如您想要将QListWidget与QListWidgetItem中所使用到的图档储存在可执行档案之中,则可以撰写一个resourcefile.qrc:
  • resourcefile.qrc resourcefile.qrc
 <!DOCTYPE RCC><RCC version="1.0"> <!DOCTYPE RCC><RCC version="1.0"> 
<qresource> <qresource>
<file>images/bee_head.jpg</file> <file>images/bee_head.jpg</file>
<file>images/bush_head.jpg</file> <file>images/bush_head.jpg</file>
<file>images/cat_head.jpg</file> <file>images/cat_head.jpg</file>
<file>images/caterpillar_head.jpg</file> <file>images/caterpillar_head.jpg</file>
<file>images/momor_head.jpg</file> <file>images/momor_head.jpg</file>
</qresource> </qresource>
</RCC> </RCC>

檔案的路徑是相對於.qrc檔案的位置。 档案的路径是相对于.qrc档案的位置。 接著您要在產生的.pro檔案中,增加一行,告知.qrc檔案的位置,例如: 接着您要在产生的.pro档案中,增加一行,告知.qrc档案的位置,例如:
RESOURCES = resourcefile.qrc RESOURCES = resourcefile.qrc

qmake會產生出製造qrc _ resourcefile.cpp的規則,之後使用rcc產生.cpp檔案,當中會將想要嵌入的相關檔案,壓縮並轉換為代表二進位資料的C++靜態無號字元陣列,如果您的.qrc檔案內容有變動,在編譯時.cpp檔案也會重新產生。 qmake会产生出制造qrc _ resourcefile.cpp的规则,之后使用rcc产生.cpp档案,当中会将想要嵌入的相关档案,压缩并转换为代表二进位资料的C++静态无号字元阵列,如果您的.qrc档案内容有变动,在编译时.cpp档案也会重新产生。

如果要使用嵌入的資源,則要在路徑的前端放置:/ ,例如: 如果要使用嵌入的资源,则要在路径的前端放置:/ ,例如:
QListWidget *listWidget = new QListWidget; QListWidget *listWidget = new QListWidget;
listWidget->insertItem(0, new QListWidgetItem( listWidget->insertItem(0, new QListWidgetItem(
QIcon(":/images/caterpillar_head.jpg"), "caterpillar")); QIcon(":/images/caterpillar_head.jpg"), "caterpillar"));
listWidget->insertItem(1, new QListWidgetItem( listWidget->insertItem(1, new QListWidgetItem(
QIcon(" :/images/ momor_head.jpg"), "momor")); QIcon(" :/images/ momor_head.jpg"), "momor"));
listWidget->insertItem(2, new QListWidgetItem( listWidget->insertItem(2, new QListWidgetItem(
QIcon(" :/images/ bush_head.jpg"), "bush")); QIcon(" :/images/ bush_head.jpg"), "bush"));
listWidget->insertItem(3, new QListWidgetItem( listWidget->insertItem(3, new QListWidgetItem(
QIcon(" :/images/ bee_head.jpg"), "bee")); QIcon(" :/images/ bee_head.jpg"), "bee"));
listWidget->insertItem(4, new QListWidgetItem( listWidget->insertItem(4, new QListWidgetItem(
QIcon(" :/images/ cat_head.jpg"), "cat")); QIcon(" :/images/ cat_head.jpg"), "cat"));

您也可以為資源檔案的路徑設置別名(Alias),例如: 您也可以为资源档案的路径设置别名(Alias),例如:
<file alias="caterpillar_head.jpg">images/caterpillar_head.jpg</file> <file alias="caterpillar_head.jpg">images/caterpillar_head.jpg</file>

之後在程式中指定路徑時,就可以直接使用別名,例如: 之后在程式中指定路径时,就可以直接使用别名,例如:
listWidget->insertItem(0, new QListWidgetItem( listWidget->insertItem(0, new QListWidgetItem(
QIcon(":/caterpillar_head.jpg"), "caterpillar")); QIcon(":/caterpillar_head.jpg"), "caterpillar"));

您也可以為別名設置前置(Prefix),例如: 您也可以为别名设置前置(Prefix),例如:
<qresource prefix="/resources"> <qresource prefix="/resources">
<file alias="caterpillar_head.jpg">images/caterpillar_head.jpg</file> <file alias="caterpillar_head.jpg">images/caterpillar_head.jpg</file>
</qresource> </qresource>

之後每個別名都會自動加上前置,使用時如下: 之后每个别名都会自动加上前置,使用时如下:
listWidget->insertItem(0, new QListWidgetItem( listWidget->insertItem(0, new QListWidgetItem(
QIcon(":/resources/caterpillar_head.jpg"), "caterpillar")); QIcon(":/resources/caterpillar_head.jpg"), "caterpillar"));

您也可以搭配語系來使用嵌入的資源檔,例如若這麼設定: 您也可以搭配语系来使用嵌入的资源档,例如若这么设定:
<qresource> <qresource>
<file>caterpillar_head.jpg</file> <file>caterpillar_head.jpg</file>
</qresource> </qresource>
<qresource lang="zh_TW"> <qresource lang="zh_TW">
<file alias="caterpillar_head.jpg">caterpillar_head_zh_TW.jpg</file> <file alias="caterpillar_head.jpg">caterpillar_head_zh_TW.jpg</file>
</qresource> </qresource>

當路徑指定為:/caterpillar_head.jpg,如果使用者是使用zh_TW語系,則會自動對應使用caterpillar_head_zh_TW.jpg,此一方法也可以用來載入.qm檔案,以實現多國語系支援,可參考 翻譯應用程式與多國語系選擇與切換 。 当路径指定为:/caterpillar_head.jpg,如果使用者是使用zh_TW语系,则会自动对应使用caterpillar_head_zh_TW.jpg,此一方法也可以用来载入.qm档案,以实现多国语系支援,可参考 翻译应用程式与多国语系选择与切换 。

The rccResource Compiler (rcc)tool is used to embed resources into a Qt application during the build process. It works by generating a C++ source file containing data specified in a Qt resource (.qrc) file.


The Meta-Object Compiler, moc, is the program that handles Qt's C++ extensions.

The moc tool reads a C++ header file. If it finds one or more class declarations that contain the Q_OBJECT macro, it produces a C++ source file containing the meta-object code for those classes. Among other things, meta-object code is required for the signals and slots mechanism, the run-time type information, and the dynamic property system.

The C++ source file generated by moc must be compiled and linked with the implementation of the class.


Qt 不是使用的“标准的” C++ 语言,而是对其进行了一定程度的“扩展”。这里我们从Qt新增加的关键字就可以看出来:signals、slots 或者 emit。所以有人会觉得 Qt 的程序编译速度慢,这主要是因为在 Qt 将源代码交给标准 C++ 编译器,如 gcc 之前,需要事先将这些扩展的语法去除掉。完成这一操作的就是 moc。

moc 全称是 Meta-Object Compiler,也就是“元对象编译器”。Qt 程序在交由标准编译器编译之前,先要使用 moc 分析 C++ 源文件。如果它发现在一个头文件中包含了宏 Q_OBJECT,则会生成另外一个 C++ 源文件。这个源文件中包含了 Q_OBJECT 宏的实现代码。这个新的文件名字将会是原文件名前面加上 moc_ 构成。这个新的文件同样将进入编译系统,最终被链接到二进制代码中去。因此我们可以知道,这个新的文件不是“替换”掉旧的文件,而是与原文件一起参与编 译。另外,我们还可以看出一点,moc 的执行是在预处理器之前。因为预处理器执行之后,Q_OBJECT 宏就不存在了。

既然每个源文件都需要 moc 去处理,那么我们在什么时候调用了它呢?实际上,如果你使用 qmake 的话,这一步调用会在生成的 makefile 中展现出来。从本质上来说,qmake 不过是一个 makefile 生成器,因此,最终执行还是通过 make 完成的。

为了查看 moc 生成的文件,我们使用一个很简单的 cpp 来测试:

test.cpp

  1. class Test : public QObject 
  2.  Q_OBJECT 
  3. public
  4.  explicit Test(QObject *parent = 0); 
  5. signals: 
  6. public slots: 
  7. }; 

这是一个空白的类,什么都没有实现。在经过编译之后,我们会在输出文件夹中找到 moc_test.cpp:

moc_test.cpp

  1. /**************************************************************************** 
  2. ** Meta object code from reading C++ file 'test.h' 
  3. ** 
  4. ** Created: Thu Jul 22 13:06:45 2010 
  5. **      by: The Qt Meta Object Compiler version 62 (Qt 4.6.3) 
  6. ** 
  7. ** WARNING! All changes made in this file will be lost! 
  8. *****************************************************************************/ 
  9.  
  10. #include "../test.h" 
  11. #if !defined(Q_MOC_OUTPUT_REVISION) 
  12. #error "The header file 'test.h' doesn't include <QObject>." 
  13. #elif Q_MOC_OUTPUT_REVISION != 62 
  14. #error "This file was generated using the moc from 4.6.3. It" 
  15. #error "cannot be used with the include files from this version of Qt." 
  16. #error "(The moc has changed too much.)" 
  17. #endif 
  18.  
  19. QT_BEGIN_MOC_NAMESPACE 
  20. static const uint qt_meta_data_Test[] = { 
  21.  
  22.  // content: 
  23.        4,       // revision 
  24.        0,       // classname 
  25.        0,    0, // classinfo 
  26.        0,    0, // methods 
  27.        0,    0, // properties 
  28.        0,    0, // enums/sets 
  29.        0,    0, // constructors 
  30.        0,       // flags 
  31.        0,       // signalCount 
  32.  
  33.        0        // eod 
  34. }; 
  35.  
  36. static const char qt_meta_stringdata_Test[] = { 
  37.     "Test\0" 
  38. }; 
  39.  
  40. const QMetaObject Test::staticMetaObject = { 
  41.     { &QObject::staticMetaObject, qt_meta_stringdata_Test, 
  42.       qt_meta_data_Test, 0 } 
  43. }; 
  44.  
  45. #ifdef Q_NO_DATA_RELOCATION 
  46. const QMetaObject &Test::getStaticMetaObject() { return staticMetaObject; } 
  47. #endif //Q_NO_DATA_RELOCATION 
  48.  
  49. const QMetaObject *Test::metaObject() const 
  50.     return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; 
  51.  
  52. void *Test::qt_metacast(const char *_clname) 
  53.     if (!_clname) return 0; 
  54.     if (!strcmp(_clname, qt_meta_stringdata_Test)) 
  55.         return static_cast<void*>(const_cast< Test*>(this)); 
  56.     return QObject::qt_metacast(_clname); 
  57.  
  58. int Test::qt_metacall(QMetaObject::Call _c, int _id, void **_a) 
  59.     _id = QObject::qt_metacall(_c, _id, _a); 
  60.     if (_id < 0) 
  61.         return _id; 
  62.     return _id; 
  63. QT_END_MOC_NAMESPACE 

可以看到,moc_test.cpp 里面为 Test 类增加了很多函数。然而,我们并没有实际写出这些函数,它是怎么加入类的呢?别忘了,我们还有 Q_OBJECT 这个宏呢!在 qobjectdefs.h 里面,找到 Q_OBJECT 宏的定义:

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

这下了解了:正是对 Q_OBJECT 宏的展开,使我们的 Test 类拥有了这些多出来的属性和函数。注意,QT_TR_FUNCTIONS 这个宏也是在这里定义的。也就是说,如果你要使用 tr() 国际化,就必须使用 Q_OBJECT 宏,否则是没有 tr() 函数的。这期间最重要的就是 virtual const QMetaObject *metaObject() const; 函数。这个函数返回 QMetaObject 元对象类的实例,通过它,你就获得了 Qt 类的反射的能力:获取本对象的类型之类,而这一切,都不需要 C++ 编译器的 RTTI 支持。Qt 也提供了一个类似 C++ 的 dynamic_cast() 的函数 qobject_case(),而这一函数的实现也不需要 RTTI。另外,一个没有定义 Q_OBJECT 宏的类与它最接近的父类是同一类型的。也就是说,如果 A 继承了 QObject 并且定义了 Q_OBJECT,B 继承了 A 但没有定义 Q_OBJECT,C 继承了 B,则 C 的 QMetaObject::className() 函数将返回 A,而不是本身的名字。因此,为了避免这一问题,所有继承了 QObject 的类都应该定义 Q_OBJECT 宏,不管你是不是使用信号槽。

本文出自 “豆子空间” 博客,请务必保留此出处http://devbean.blog.51cto.com/448512/355100


作者: zimang   发布时间: 2010-11-05