QT core模块是QT GUI的核心模块,主要涵盖的核心功能:
Qt Data Types:数据类型
Qt Object Model:对象模型(包括元对象模型()、属性系统、信号与槽机制、对象树,智能指针等)
Input/Output Classes:I/O读写
Threading and Concurrent Programming:多线程和并发编程
QCoreApplication:应用程序类
Event System :事件系统
Container Classes:容器类
Animation Framework:动画框架
Qt Plugins:插件类
State Machine Framework:状态机
JSON Support:JSON读写
Qt Resource System: 资源系统
Implicit Sharing:隐性共享
其他各个模块的使用也大多都要依赖QT的core模块。
Qt的基本对象模型:
标准 C++,其设计的对象模型虽然已经提供了非常高效的 RTTI 支持,其生命周期也由对象本身管理,而Qt的对象模型综合C++对象模型,扩展了其他特性:
- 方便两个对象间交互的“信号-槽”机制
- 非常灵活好用的“对象属性系统”
- 可以使用强大的“事件及事件过滤”
- 可以国际化翻译字符串
- 可以在 GUI 编程中用 Qt 提供的“计时器”完成许多任务
以一种自然的方式组织对象父子关系的“对象树”,使得实现删除某一对象时,先删除自己的所有子对象,同时从从父对象中将自己删除
当引用对象被销毁时会自动设为0的指针(主要是 QPointer),而 C++ 指针销毁时将成为悬空指针
Qt 对象之间的动态转换支持
可以自定义创建一个 Qt 类型,从而享受 Qt 的其他特性
QT的对象模型的核心类QObject
QObject类方法属性.
QObject 对象是具有线程亲和性(Thread Affinity)
CPU的亲和性, 就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,也称为CPU关联性;再简单的点的描述就将制定的进程或线程绑定到相应的cpu上;在多核运行的机器上,每个CPU本身自己会有缓存,缓存着进程使用的信息,而进程可能会被OS调度到其他CPU上,如此,CPU cache命中率就低了,当绑定CPU后,程序就会一直在指定的cpu跑,不会由操作系统调度到其他CPU上,性能有一定的提高。
QObject 类创建的对象收到信号或者事件的时候,处理信号的槽函数以及处理事件的某某函数也是和这个对象在一个线程中运行,查询 QObject 对象的亲和性用 thread() 函数。
QObject 对象必须和父对象在同一个线程中
如果两个 QObject 位于不同线程中,他们不能互相指定为父子关系,即调用 setParent() 将失败;
移动 QObject 到另一个线程中,其所有子对象也都自动移过去;
如果某个 QObject 对象指定了父对象,则调用 moveToThread() 函数将会失败;
在 QThread::run() 中创建的 QObject 对象不能成为 QThread 对象的子对象,因为QThread::run() 中的线程不存在QThread对象。
QObject 的成员变量默认不是其子对象( 如QLabel类作为其成员)
需要调用 setParent() 函数来手动指定,或者成员变量定义的时候构造函数中指明。否则的话,当 QObject 对象移动到其他线程后,成员变量是不动的保留在旧的线程中。
uic工具
利用uic工具将所有的.ui文件生成ui_文件名.h(或者执行:uic mainwindow.ui -o ui_mainwindow.h)
rcc工具
利用rcc工具将 myresource.qrc 里面描述的图片、文档等编译成对应的独立源代码文件 qrc_myresource.cpp或独立的二进制资源文件*.rcc。
- 生成独立的二进制资源文件 myresource.rcc,作为资源外挂。
对于太大的图片、音频、视频等文件,不适合集成到目标程序内部,可以放到操作系统文件路径,或者单独编译成外挂资源 *.rcc 。举例来说,myresource.qrc 包含了很多图片和视频,没法编译成目标程序内嵌资源时,可以通过执行:
cpp rcc -binary myresource.qrc -o myresource.rcc
rcc 会把 *.qrc 里面描述的所有图片、视频等文件,都压缩打包,生成一个二进制的 *.rcc 文件,这个 *.rcc 包含了所有的资源,这样程序发布只需要myresource.rcc 搞定。
之后在main函数入口开始的位置注册这个独立的二进制资源文件:
QResource::registerResource("/path/to/myresource.rcc");
- 直接将资源内嵌到应用程序
在项目管理文件(.pro)加入:
cpp RESOURCES += application.qrc
qmake会自动根据这句话,为 application.qrc 添加编译脚本,其编译过程如下图所示
rcc 工具会解析 application.qrc 内的 XML 文本,找到需要添加的各种文件,默认情况下,rcc 工具会对这些文件做 ZIP 压缩,然后将压缩后的 ZIP 数据的每个字节转换成比如 0x6f, 数值形式,所有文件压缩后的数据对应一个 C++ 静态数组 qt_resource_data[] ,并添加注册、取消注册、初始化、清除等函数和资源描述结构体,最终形成一个 qrc_application.cpp 文件。然后用编译器编译 qrc_application.cpp文件,得到 qrc_applicaotion.o ,链接到目标程序内部,就可以用 “:/images/copy.png” 等形式访问程序内嵌资源了。
元对象预编译器(moc工具)
利用qmake生成makefile后,对QT程序执行make时会调用moc工具,会对每一个c++头文件进行分析预处理,并生成一个能被标准C++编译器编译的moc_*.cpp文件,moc工具会替换Qt定义的宏Q_OBJECT,slots,signals,emit,SIGNAL,SLOT,Q_PROPERTY等,这些宏定义都在Qtcore目录中qobjectdefs.h文件中定义,通过moc工具处理之后,可以被标准C++编译器进行编译。
对Qt的完整编译过程为:moc预处理->普通编译流程(预处理->编译->汇编->链接)。生成最终二进制文件(库或可执行程序)。
经过moc预处理后,所有的QObject相关宏将会发生宏替换,并在moc_**.cpp生成函数实现,其中主要宏替换为:
声明(5个类型):
#define Q_OBJECT \
public: \
static const QMetaObject staticMetaObject; \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
QT_TR_FUNCTIONS \ //Qt的翻译宏
private: \
static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
struct QPrivateSignal {}; \
对应实现:
public:
const QMetaObject 类名::staticMetaObject = { {
QMetaObject::SuperData::link<基类名::::staticMetaObject>(),
qt_meta_stringdata_ 类名.data,
qt_meta_data_ 类名,
qt_static_metacall,
nullptr,
nullptr
} };
const QMetaObject * 类名::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void * 类名::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_MyWindow.stringdata0))
return static_cast<void*>(this);
return QMainWindow::qt_metacast(_clname);
}
int 类名::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QMainWindow::qt_metacall(_c, _id, _a);
return _id;
}
private:
void MyWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
Q_UNUSED(_o);
Q_UNUSED(_id);
Q_UNUSED(_c);
Q_UNUSED(_a);
}
#define signals Q_SIGNALS
# define Q_SIGNALS public QT_ANNOTATE_ACCESS_SPECIFIER(qt_signal)
# define emit 空 //说明emit可写可不写
# define SLOT(a) qFlagLocation("1"#a)
# define SIGNAL(a) qFlagLocation("2"#a)
SIGNAL与SLOT宏会利用预编译器将一些参数转化成字符串,并且在前面添加上编码,在调试模式中,如果signal的连接出现问题,提示警告信息的时候还会注明对应的文件位置。qFlagLocation 用于定位代码对应的行信息,会将对应代码的地址信息注册到一个有两个入口的表里。