QT6安装与概念介绍
文章目录
- 前言
- install
- Modules
- Qt Core
- 元对象系统
- 属性系统
- 对象模型
- 对象树和所有者
- 信号 & 槽
前言
QT不是纯粹的C++标准,它在此基础上引入MOC编译器,在调用C++编译器之前会使用该编译器将非C++的内容如 Q_OBJECT、signal:等进行处理。此外QT还引入了对象间通讯的机制 信号和槽就以来MOC。
install
https://doc.qt.io/qtcreator/creator-how-to-install.html
git clone git://code.qt.io/qt/qt5.git QT6
git clone https://code.qt.io/qt/qt5.git QT6
cd QT6
git checkout v6.8.2
mkdir build-qt6
cd build-qt6
../configure -init-submodules # 同步子库,然后执行 cmake .. 指令
cmake --build . --parallel # 编译qt库
cmake --install . # /usr/local/Qt-6.8.2/
Building Qt Creator from Git
https://wiki.qt.io/Building_Qt_Creator_from_Git
Building Qt 6 from Git
https://wiki.qt.io/Building_Qt_6_from_Git
https://doc.qt.io/qt-6/getting-sources-from-git.html
qt系列工具及其作用
Qt各平台支持插件 libqxcb.so 安装
../configure -nomake examples -nomake tests -xcb # 声明构建xcb库
[-submodules qtdeclarative]
../configure -nomake examples -nomake tests -xcb -submodules qtbaseWARNING: Could not find all necessary libraries for qpa-xcb support.
-- LIBDRM libray not found
-- XCOMPOSITE libray not found
-- XCURSOR libray not found
-- XRANDR libray not found
-- XI libray not found
-- XSHMFENCE libray not found
-- XTST libray not found
-- XKBCOMMON libray not found
-- XKBFILE libray not foundsudo apt-get install -y \libdrm-dev \libxcomposite-dev \libxcursor-dev \libxrandr-dev \libxi-dev \libxshmfence-dev \libxtst-dev \libxkbcommon-dev \libxkbfile-devsudo apt install libxcb1-dev libxcb-xkb-dev libxcb-util-dev libxcb-icccm4-dev
sudo apt install libxkbcommon-x11-dev
sudo apt install libdbus-1-dev at-spi2-coresudo apt install \libxcb1-dev \libxcb-xkb-dev \libxcb-util-dev \libxcb-icccm4-dev \libxcb-image0-dev \libxcb-keysyms1-dev \libxcb-render-util0-dev \libxcb-shm0-dev \libxcb-xinerama0-dev \libxcb-xinput-dev \libxcb-xfixes0-dev \libxcb-randr0-dev
Modules
https://doc.qt.io/qt-6/qtmodules.html
Qt Essentials
Qt要素定义了Qt在所有平台上的基础。它们可以在所有受支持的开发平台上使用。基本模块是通用的,对大多数Qt应用程序都很有用。用于特殊目的的模块被视为附加模块,即使它在所有支持的平台上都可用。
Qt Core
被其他模块使用的非图形化类核心,它向C++添加了如下特性:
- 一个强大的无缝对象通讯机制 —— signals and slots
- 可查询、可设计的对象属性
- 层次化和可查询的对象树,用保护指针(QPointer)以自然的方式组织对象所有权
- 一个跨库边界的动态转换
使用该模块
find_package(Qt6 REQUIRED COMPONENTS Core)
target_link_libraries(mytarget PRIVATE Qt6::Core)
元对象系统
qt的元对象系统提供了信号和槽机制,用于对象间通讯,运行时类型信息和动态属性系统。
- QObject类为对象提供获取元对象系统优势的基类
- Q_OBJECT宏用于确保元对象特性,如动态属性、信号和槽
- Meta-Object Compiler(MOC, 用于处理Q_OBJECT等宏的预处理器)为每个QObject子类插入必要的用于实现元对象特性的代码
moc读取C++源码,如果发现 Q_OBJECT 宏,则生成额外的moc*.cpp文件,它包含源对象代码。如何包含信号和槽机制还包含的特性:
- QObject::metaObject() returns the associated meta-object for the class.
- QMetaObject::className()在运行时返回类名string
- QObject::inherits() 函数 返回对象是否是继承QObject继承树中指定类的类的实例
- QObject::tr() 转换字符串以实现国际化
- QObject::setProperty() and QObject::property() 通过名称动态设置和获取属性
- QMetaObject::newInstance() constructs a new instance of the class.
qobject_cast 类似 dynamic_cast// MyWidget inherits from QWidget and is declared with the Q_OBJECT macro:
QObject *obj = new MyWidget;
QWidget *widget = qobject_cast<QWidget*>(obj);
MyWidget *myWidget = qobject_cast<MyWidget *>(obj);// 下面的转换都会失败,返回 nullptr
if (QLabel *label = qobject_cast<QLabel *>(obj)) {label->setText(tr("Ping"));
} else if (QPushButton *button = qobject_cast<QPushButton *>(obj)) {button->setText(tr("Pong!"));
}
对于没有Q_OBJECT的子类,它的元对象特性和它最近的QObject父类一致;如果没有添加Q_OBJECT宏则源对象特性将不可用
Therefore, we strongly recommend that all subclasses of QObject use the Q_OBJECT macro regardless of whether or not they actually use signals, slots, and properties.
属性系统
Q_PROPERTY(type name(READ getFunction [WRITE setFunction] |MEMBER memberName [(READ getFunction | WRITE setFunction)])[RESET resetFunction][NOTIFY notifySignal][REVISION int | REVISION(int[, int])][DESIGNABLE bool][SCRIPTABLE bool][STORED bool][USER bool][BINDABLE bindableProperty][CONSTANT][FINAL][REQUIRED])// 下面是分别用普通方法和 Meta-Object System
QPushButton *button = new QPushButton;
QObject *object = button;button->setDown(true);
object->setProperty("down", true);
运行时获取未知类型的属性
QObject *object = ...
const QMetaObject *metaobject = object->metaObject();
int count = metaobject->propertyCount();
for (int i=0; i<count; ++i) {QMetaProperty metaproperty = metaobject->property(i);const char *name = metaproperty.name();QVariant value = object->property(name);...
}
class MyClass : public QObject
{// MOC macroQ_OBJECTQ_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)public:MyClass(QObject *parent = nullptr);~MyClass();enum Priority { High, Low, VeryHigh, VeryLow };// moc grammaQ_ENUM(Priority)void setPriority(Priority priority){if (m_priority == priority)return;m_priority = priority;emit priorityChanged(priority);}Priority priority() const{ return m_priority; }signals:void priorityChanged(Priority);private:Priority m_priority;
};MyClass *myinstance = new MyClass;
QObject *object = myinstance;myinstance->setPriority(MyClass::VeryHigh);
object->setProperty("priority", "VeryHigh");
对象模型
C++对象模型是高效的,但在某些领域不够灵活;GUI编程需要运行时高效和高层次灵活性,Qt通过组合C++的高效率和Qt对象模型的灵活性提供了这些特性。
Qt向C++中添加了如下特性:
- 信号与槽——对象间通讯机制
- 可查询和可设计的对象属性
- 事件和事件过滤器
- 上下文string翻译国际化
- 复杂的间隔驱动计时器,可以在事件驱动的GUI中优雅地集成许多任务
- 层次化和可查询的对象树
- QPointer,指针对象析构自动设0
- 跨库的 dynamic cast
- 支持自定义类型创建
许多Qt特性通过 标准C++基于继承QObject类 实现,object communication mechanism and the dynamic property system,需要 Meta-Object System provided by Qt’s own Meta-Object Compiler (moc).
Meta-Object System 是对C++标准的扩展,让其更适用于GUI编程
QObject对象是一个身份、克隆(意味不完全一样),值拷贝会带来一些列问题,copy constructor and assignment operator disabled.
对象树和所有者
当创建从其他QObject继承的QObject对象时,会向父类的children()中添加该对象,父类析构也会析构子对象。这很符合GUI对象的需要。
调试函数 QObject::dumpObjectTree() and QObject::dumpObjectInfo() are often useful when an application looks or acts strangely.
对象树中任意节点析构会自动析构它的所有孩子节点,自动从父节点中将该节点移除
考虑下面的例子:
int main()
{QWidget window;QPushButton quit("Quit", &window);...
}
// 先析构临时变量quit,然后从父对象中移除它,最后析构windowint main()
{QPushButton quit("Quit");QWidget window;quit.setParent(&window);...
}
// 先析构window,同时它的子对象也都析构了(即,quit析构),然后再析构quit,duang!!!quit析构两次
信号 & 槽
传统的通知方法是用回调函数,当事件来临时,我的方法会被调用。信号和槽机制用于替代该方法。当某个事件发生时会发射一个信号,Qt’s widgets有许多预定义的信号,用户也可以添加自定义信号;槽是一个函数,当某一信号发射时会被调用,Qt’s widgets预定义了许多槽,用户也可以添加自己的槽。
信号是具有public属性的函数,能够从任何地方发射,但建议仅从signal处发射。当发出信号时,连接到它的插槽通常会立即执行,就像正常的函数调用一样。当这种情况发生时,信号和槽机制完全独立于任何GUI事件循环。一旦所有插槽都返回,就会执行emit语句后的代码。使用排队连接时,情况略有不同;在这种情况下,emit关键字后面的代码将立即继续,稍后将执行插槽。如果多个插槽连接到一个信号,则在发出信号时,这些插槽将按照连接的顺序一个接一个地执行。
信号由moc自动生成,不得在.cpp文件中实现。
槽是一个普通的C++函数,成员的私有属性不影响槽函数被调用。槽函数也可以是虚函数。
class Counter : public QObject
{Q_OBJECT
// Note. The Q_OBJECT macro starts a private section.
// To declare public members, use the 'public:' access modifier.
public:Counter() { value_ = 0; }int value() const { return value_; }public slots:void setValue(int value){if (value != value_) {value_ = value;emit valueChanged(value);} }
signals:void valueChanged(int newValue) {}private:int value_;
};Counter a, b;QObject::connect(&a, &Counter::valueChanged,&b, &Counter::setValue);a.setValue(12); // a.value() == 12, b.value() == 12b.setValue(48); // a.value() == 12, b.value() == 48printf("%d, %d\n", a.value(), b.value());