Qt 6 与 Qt 5 存在的兼容性差异
之前有提到。我的是Qt5,我朋友的是Qt 6,由于版本不兼容问题,在迁移时会有问题。所以这一我们说说这两个的区别。(
正文开始喽!
总结来说:Qt5迁移至 Qt 6 需:1. 破坏性变更(必须修改代码)、2. 模块和功能的变化、3. 构建系统的变化。
一、 破坏性变更(需要修改代码)
这些是您在将项目从 Qt 5 迁移到 Qt 6 时最可能遇到并必须修改代码的地方。
1. 对 C++ 标准的要求
-
Qt 5: 最低要求 C++11,但大部分模块仍兼容 C++98。
-
Qt 6: 强制要求 C++17 标准。这意味着您的编译器和代码都必须支持 C++17。这是 Qt 6 现代化改造的基础。
2. 图形架构的重大改变
这是最核心的差异之一,主要影响与图形渲染相关的代码(尤其是自定义的 QQuickItem
或使用 QPainter
的场景)。
-
Qt 5: 支持多种后端,如 OpenGL、DirectX、Software(软件渲染)。在 Qt Quick 2 中,场景图(Scene Graph)是基于 OpenGL 的。
-
Qt 6: 引入了 RHI(Rendering Hardware Interface) 抽象层。
-
是什么:RHI 是一个位于 Qt Quick 场景图和具体图形 API(如 Vulkan、Metal、Direct3D、OpenGL)之间的薄抽象层。
-
为什么:为了实现跨平台图形后端的统一和最佳性能(默认在 macOS 上使用 Metal,在 Windows 上使用 DirectX 12/Vulkan,在 Linux 上使用 Vulkan/OpenGL)。
-
影响:
-
所有直接使用 OpenGL 调用的代码(例如
QOpenGLFunctions
,QOpenGLFramebufferObject
,QOpenGLShaderProgram
)都需要重写,以使用QRhi
及其相关类(如QRhiTexture
,QRhiRenderBuffer
等)。 -
自定义的
QQuickItem
或QQuickFramebufferObject
需要适配新的渲染路径。
-
-
兼容方案:Qt 6 提供了
rhi
模块和大量示例来展示如何在新架构下进行渲染。
-
3. QString 相关变化
-
QString::mid, QString::left, QString::right:
-
Qt 5: 返回
QString
。 -
Qt 6: 返回
QStringView
。如果您需要QString
,可能需要显式构造或使用其他方法。
-
-
QStringRef 被移除:由
QStringView
替代。所有使用QStringRef
的代码都需要替换。
4. QML 注册类型的变化
-
Qt 5: 使用
qmlRegisterType
函数族进行注册。 -
Qt 6: 强烈推荐使用新的宏
QML_ELEMENT
和QML_NAMED_ELEMENT(<name>)
在类声明中直接注册。虽然旧的函数仍然存在,但新方式更简洁、更易于维护。-
Qt 5:// main.cpp qmlRegisterType<MyObject>("MyModule", 1, 0, "MyObject");Qt 6:// myobject.h #include <QtQml/qqmlregistration.h> class MyObject : public QObject {Q_OBJECTQML_ELEMENT // 自动使用类名// 或 QML_NAMED_ELEMENT("MyObject") // 自定义名称... };
-
然后在 CMake 中使用
qt6_add_qml_module
或在 QMake 中正确配置。
-
5. 容器类迭代器的行为变化
-
Qt 5:
QMap
,QHash
,QSet
等的迭代器行为类似于std::map
,it.key()
和it.value()
用于访问键值对。 -
Qt 6: 为了与 C++ STL 保持一致,迭代器解引用(
*it
)现在返回的是 值,而不是一个键值对。-
对于
QMap
和QHash
,*it
等价于it.value()
。 -
要获取键,仍然需要使用
it.key()
。 -
这会影响基于范围的 for 循环:
QMap<int, QString> map; // Qt 5 方式 (在Qt 6中错误) for (auto &pair : map) {// pair 是 QPair<int, QString> 或类似物int key = pair.key; // 错误QString value = pair.value; // 错误 } // Qt 5/6 通用正确方式 for (auto it = map.begin(); it != map.end(); ++it) {int key = it.key();QString value = it.value(); // 或 *it } // Qt 6 基于范围for循环的正确方式 for (auto &key : map.keys()) {QString value = map.value(key); } for (auto &value : map) { // *it 就是value,所以可以直接遍历值// ... }
-
二、 模块和功能的变化
1. 模块的移除和拆分
许多在 Qt 5 中处于“废弃”状态的模块在 Qt 6 中被正式移除。如果需要它们,必须单独安装或寻找替代方案。
-
被移除的模块:
-
QtScript
: 已废弃,推荐使用QJSEngine
(在QtQml
中)。 -
QtXmlPatterns
: 已废弃,推荐使用QXmlStreamReader
或第三方库。 -
QtQuick1
/QtDeclarative
: QML 1.0 已被淘汰。 -
QtWebKit
: 已被QtWebEngine
取代(但QtWebEngine
本身在 Qt 6.4 之前是附加模块,需要单独安装)。 -
QtQuickControls1
: 已被 Qt Quick Controls 2 取代。
-
-
变为附加模块(需要单独安装):
-
QtWebEngine
: 提供浏览器功能。 -
QtSerialPort
,QtBluetooth
,QtSensors
等许多不属于核心框架的模块都变成了附加模块。
-
2. 新的核心模块
-
QtCore5Compat
: 这是一个至关重要的兼容性模块。它包含了许多从 Qt 5 核心模块中移除但为了兼容性而保留的类,例如:-
QRegExp
(推荐使用QRegularExpression
) -
QTextCodec
及其子类 -
QStringRef
(已被QStringView
取代) -
旧版本的
QDateTime
API -
如果您遇到
QRegExp
等类找不到链接的错误,通常需要在.pro
文件(QT += core5compat)或CMakeLists.txt
(find_package(Qt6 COMPONENTS Core5Compat)
)中添加这个模块。
-
3. API 的清理和废弃
许多在 Qt 5 中被标记为“废弃”的旧 API 在 Qt 6 中被彻底移除。编译器会直接报错。
-
常见例子:
-
QColor::light()
/QColor::dark()
-> 使用QColor::lighter()
/QColor::darker()
-
qVariantFromValue()
-> 使用QVariant::fromValue()
-
QFontMetrics::width()
-> 使用QFontMetrics::horizontalAdvance()
-
三、 构建系统的变化
1. QMake 到 CMake 的转变
-
Qt 5: 主要支持和推荐使用 QMake (
.pro
文件)。 -
Qt 6: 官方强烈推荐并主要支持使用 CMake。虽然仍然支持 QMake,但所有新的特性和开发都优先面向 CMake。Qt 官方提供的许多工具和集成(如用于 QML 的
qt6_add_qml_module
)都是为 CMake 设计的。
2. 新的 QML 模块构建系统
-
在 Qt 6 中,使用 CMake 管理 QML 模块、资源(
qmldir
,qrc
文件)变得更加简单和强大,通过qt6_add_qml_module
宏可以一站式处理类型注册、资源打包和模块发现。
迁移建议和总结
-
检查编译器:确保您的编译器支持 C++17。
-
使用端口工具:运行
qt6_porting_tools
中的configure
和cmake
脚本来分析您的代码,它们能识别出许多常见的兼容性问题。 -
逐模块处理:
-
首先处理核心模块(QtCore, QtGui, QtWidgets)的编译错误(如废弃的 API)。
-
然后重点关注图形相关代码(OpenGL -> RHI)。
-
接着处理 QML 注册和 QML 相关代码。
-
-
添加 Core5Compat 模块:如果遇到
QRegExp
等链接错误,这是最快的解决方案。 -
查阅官方文档:Qt 官方提供了非常详细的 Porting from Qt 5 to Qt 6 指南,这是最权威的参考。