Qt在C++标准的基础上添加了一些特性,也即属于Qt自己的核心技术。这些核心技术在Qt Core模块中实现。这些特性主要包括:

* 非常强大的无缝连接通信机制,称为信号和槽;
* 可查询和可设计的对象属性;
* 层次化并可查询的对象树;
* 用保护指针(QPointer)通过很自然的方式实现对象的所有权管控;
* 跨库工作的动态投射;
 

一、元对象系统(The Meta-Object System)

Qt的元对象系统提供了 对象之间通信的信号和槽机制、运行时的类型信息和动态属性系统。元对象系统基本构成为:

1.QObject类。该类是所有元对象系统类的基类。

2.Q_OBJECT宏。在类的成员变量开始的地方声明该宏,就可以使这个类具有元对象的特性,如动态属性和信号与槽。

3.元对象编译器(MOC)。MOC工具在编译时,将含有Q_OBJECT宏的类解释为标准C++源文件,使得通用编译器可以编译Qt项目。

元对象系统是Qt
的核心,我们在开发过程中必然会用到QObject类,Q_OBJECT宏,在编译时也会在VS的输出框看到MOC过程。但是,元对象系统的功能不仅仅如此,它与后面的属性系统,对象模型以及信号与槽有这密切联系,或者说,是它们的基础。

标准C++对象模型为对象范例提供了非常有效的运行效率支持。但GUI编程是一个既需要运行效率,又需要高度灵活性的领域。Qt则通过对象模型则非常好的结合了C+++的速度和灵活性。许多的Qt特性是通过继承QObject类,用标准C++技术实现的。其他的如对象通信机制和动态属性系统,需要Qt自己的MOC来实现。总之,元对象系统时一个C++扩展,使得Qt语言更适合于GUI编程开发。

下面列举一些元对象模型中的设计的基础类:



说明

QMetaClassInfo

Additional information about a class

QMetaEnum

Meta-data about an enumerator

QMetaMethod

Meta-data about a member function

QMetaProperty

Meta-data about a property

QMetaType

Manages named types in the meta-object system

QObject

The base class of all Qt objects

QSignalBlocker

Exception-safe wrapper around QObject::blockSignals()

QObjectCleanupHandler

Watches the lifetime of multiple QObjects

QMetaObject

Contains meta-information about Qt objects

QPointer

Template class that provides guarded pointers to QObject

QSignalMapper

Bundles signals from identifiable senders

QVariant

Acts like a union for the most common Qt data types

 

 

二、属性系统(The Property System)

Qt提供了一个复杂的属性系统,就像一些编译器开发商提供的属性系统一样。但是,作为一个独立于编译器和开发平台的类库,Qt不依赖非标准的编译指令比如__property
or [property].Qt的工程可以在任何Qt支持的平台上用标准C++编译器编译。这个技术是通过上述的元对象系统实现的。

在QObject的派生类中,用宏Q_PROPERTY()定义属性,其格式如下:
Q_PROPERTY(type name              (READ getFunction [WRITE setFunction] |
              MEMBER memberName [(READ getFunction | WRITE setFunction)])
             [RESET resetFunction]              [NOTIFY notifySignal]
             [REVISION int]              [DESIGNABLE bool]             
[SCRIPTABLE bool]              [STORED bool]              [USER bool]
             [CONSTANT]              [FINAL])
其中的一些主要关键字意义:

READ:指定一个读取属性值的函数。

WRITE:指定一个设定属性值的函数。

MEMBER:指定一个成员变量与属性关联。

RESET:指定一个设置属性缺省值的函数,可选。

NOTIFY:设置一个信号,当属性值发生变化时触发。可选。

Q_PROPERTY宏定义声明一个返回值类型为Type,名称为name的属性,用READ和WRITE关键字定义属性的读取和写入函数。它看上去更像是类的成员变量。如下述的例子:
class MyClass : public QObject { Q_OBJECT Q_PROPERTY(Priority priority READ
priority WRITE setPriority NOTIFY priorityChanged) public: MyClass(QObject
*parent = 0); ~MyClass(); enum Priority { High, Low, VeryHigh, VeryLow };
Q_ENUM(Priority) void setPriority(Priority priority) { m_priority = priority;
emit priorityChanged(priority); } Priority priority() const { return
m_priority; } signals: void priorityChanged(Priority); private: Priority
m_priority; };
 

三、对象树和所有权(Object Trees & Ownership)

当存在多个关联的对象时,这些对象可以在树中组织自己。比如,当您创建一个派生类的对象时,它将被添加到父类对象的子对象列表中,并在父对象释放时同时被释放。这个技术实现了在开发Qt项目时,你可能不需要逐个释放你new出来的GUI控件,因为他们继承于QObject类,该类的父对象被删除析构时,这些所有的GUI控件都将删除析构,不会造成内存泄漏。当然你也可以手动删除这些控件,当你delete时实际上也从其父类对象中删掉了该子对象。

看下面的例子:
int main() { QWidget window; QPushButton quit("Quit", &window); ... }

复习下C++的知识,分析下上述代码的析构过程。函数内部的临时变量(局部变量)是在栈上分配控件,析构则是相反顺序进行(先进后出)。quit是类QPushButton的对象,代码指定了它的父对象是window。析构时先析构quit,同时将该对象从window的子对象列表中删除,再析构父对象window。这个过程是正确的。如果是下面的这种情况:
int main() { QPushButton quit("Quit"); QWidget window;
quit.setParent(&window); ... }

先析构windows会将其所有的子对象析构,包括quit。当再析构quit时,相当于对同一对象执行两次delete,我们知道这在C++标准中是不允许的,程序会“崩溃”。

在开发Qt是几乎每个官方类和自定义类都会有父类,指定其parent。理解好对象树和所有权的概念是至关重要的。

四、信号与槽(Signals & Slots)

信号与槽是Qt的核心的核心,它是区别于其他框架的重要特性。信号与槽是对象间通信的机制,需要Qt元对象系统的支持。

信号与槽隐藏了复杂的底层实现,完成信号和槽的关联后,发射信号并不需要知道Qt是如何找到槽函数的。这类似于一些嵌入式设备的回调函数,但与回调函数相比,信号与槽稍微慢一些,但它的灵活性比回调函数强很多。

信号在某个事件发生的时候发射出去,槽是一个回应这个信号的函数。把这两者联系起来用到connect函数。

connect函数的参数有多种方式,一种是:

connect(sender, SIGNAL(signal(int)), receiver, SLOT(slot(int)));

其中用宏SIGNAL和SLOT指定信号和槽函数,如果信号需要传递参数,那么必须在函数内注明参数类型。

另一种方式是:

connect(sender, QSender::signal, receiver, QSlot::slot);

对具有默认参数的信号和槽,即信号名称是唯一的,没有参数不同而同名的两个信号,可以使用这种方式。

举个自定义信号的例子:
class MyClass:public QObject { Q_Object private: int counter = 0; public:
MyClass(QObject *parent = 0){}; ~MyClass(); int add(); signals: void
valChanged(int val); } void MyClass::add() { counter++; emit
valChanged(counter); } //mainwindow.h class QtGuiApplication1 : public
QMainWindow { Q_OBJECT public: QtGuiApplication1(QWidget *parent = Q_NULLPTR);
private: Ui::QtGuiApplication1Class ui; private slots: void onValChanged(int
val); }; //mainwindow.cpp MainWinow::MainWindow (QWidget *parent) :
QMainWindow(parent) { m_class = new MyClass(this); connect(m_calss,
SIGNAL(valChanged(int)), this, SLOT(onValChanged(int))); } void
QtGuiApplication1::onValChanged(int val) { //do sth for val }
 

技术
©2019-2020 Toolsou All rights reserved,
el-select获取选中项label值常见的5种JAVA运行时异常PowerShell中使用WebClient 下载文件并获取下载进度(精华)2020年8月2日 TypeScript 泛型的使用@Repository注解的作用TypeScript-多态篇Java分布式系统高并发解决方案小结Java Thread之Sleep()使用方法总结用PyMC3进行贝叶斯统计分析(代码+实例)Result返回结果信息的封装