![]() |
憨厚的可乐 · 國立臺灣大學建築與城鄉研究所-博士論文· 1 周前 · |
![]() |
阳刚的烤地瓜 · 达利的奇幻艺术珠宝:设计如何疯狂跨界_财经_中国网· 1 年前 · |
![]() |
内向的烤面包 · 灵异狐妖情 - 抖音· 1 年前 · |
![]() |
胡子拉碴的八宝粥 · 「漫画」全职高手:哥哥的嘴骗人的鬼 - 知乎· 1 年前 · |
![]() |
长情的荒野 · 牧午之森 - 🌈️包子漫畫· 2 年前 · |
Qt 6.2 也是 Qt 6 的第一个版本,Qt 公司将为 Qt 商业客户提供长期支持。
在 Qt 6.2 中,包含了 Qt 5.15 中的所有常用功能以及为 Qt 6 添加的新功能。随着 Qt 6.2 的发布,几乎所有的用户都应该能够将他们的代码从 Qt 5 迁移到 Qt 6。
目前Qt的支持路线为:Qt5.15.2编译的程序支持WIN7及以上的系统,Qt5.6.3编译的程序支持XP系统。
我们在 Qt 6 中进行了一些更广泛的架构更改,我们现在正在构建 Qt 6.2 和任何未来版本。
开发 Qt 6.2 的主要工作之一是重新添加我们在 Qt 6.0 中遗漏的所有模块和功能。除了极少数例外,Qt 5.15 支持的所有模块现在也支持 Qt 6.2。
在 Qt 6.2 中,我们添加了对以下附加模块的支持(在 Qt 6.1 中已有的模块之上):
这些模块的 API 主要向后兼容 Qt 5,并且在移植到 Qt 6 时只需要对用户代码进行少量调整。
Qt 6.2 支持的 完整模块列表 可以在我们的文档中找到 。
除了我们从 Qt 5 带来的许多模块之外,我们 在 6.2 中还有大量的 新特性 和功能。我们来看一下。
Qt Quick 3D 获得了一些很酷的新功能,现在支持 实例化渲染,允许您使用不同的变换渲染大量相同的对象。我们还添加了一个新的 API,用于向 场景添加 3D 粒子效果。
输入处理已得到改进,我们现在可以为嵌入在 3D 场景中的 2D 项目正确创建 Qt Quick 输入事件。我们还添加了一个新的 API,用于从场景中的任意点进行基于光线的拾取。
Qt 6.2 对 QML 工具进行了较大改进。我们现在有一个公共的 CMake API ,它极大地简化了创建您自己的 QML 模块的过程。
QML linter (qmlint) 是一种工具,用于检查 QML 源代码的最佳实践、潜在的编码和性能问题,并帮助编写更易于维护的 QML。该工具经历了很大的变化,现在可以完全配置,无论是在命令行级别,还是通过配置文件,甚至是 QML 文件本身中的各个块。此外,它现在可以生成 JSON 输出以简化与其他工具或自动化系统的集成。
QML 格式化程序 (qmlformat) 现在使用 QML dom 库,大大改进了生成的输出。
尽管如此,从 Qt 5 中的 Qt 多媒体移植到 Qt 6 应该相对简单。
Qt 6 中的 Qt 多媒体确实支持一些我们在 Qt 5 中从未设法正确支持的高度要求的功能。示例包括播放的字幕和语言选择支持以及媒体捕获的可配置设置。
内部架构已经过清理,不再像 Qt 5 那样通过公共 API 公开。这将使我们能够更快地修复错误,并使将来添加新功能变得更加容易。您可以在有关 Qt 6 中的 Qt 多媒体的单独博客文章中找到更多详细信息。
然而,由于这些巨大的变化,该模块仍然存在粗糙的边缘,并且可能在实现中存在相当多的错误。但是,我们相信多媒体是一项必不可少的功能,我们将在 Qt 6.2 中完全支持该模块。
因此,我们将在补丁级别版本的常规提交策略上有所偏离,如果需要修复较大的问题,可能会添加一些较小的 API。
此外,我们将努力在即将发布的补丁级别版本中尽快修复任何报告的错误。
我们已经移植了许多 API 以利用新的属性系统,以便您可以使用 C++ 中的属性绑定。这项工作尚未完成,我们将在未来的版本中继续。
我们还在各个地方修复了许多 API 缺点和缺失的功能。仅举几个例子:
Qt Creator 和 Qt Design Studio 也做了大量工作,以确保它们为 Qt 6.2 提供一流的支持。Qt Creator 5 包含您为 Qt 6.2 开发所需的一切。
我们今天还发布了全新版本的 Qt Design Studio。Qt Design Studio 2.2 基于 Qt 6.2,极大地支持在一个图形工具中创建基于 Qt Quick 和 Qt Quick 的 3D 用户界面。您可以轻松地在目标硬件上测试这些,无论是台式机、移动设备还是嵌入式设备。有关 更多详细信息,请查看有关Qt Design Studio 2.2的单独博客文章。
对于 Qt 6.2,我们做了很多工作来改进我们对当前支持平台的支持,包括桌面和移动端,例如,通过改进我们对 HighDPI 渲染的支持和在 iOS 上添加 NFC 后端。
最重要的是,Qt 6.2 大大扩展了支持平台的范围:
Qt 6.2 完全支持 Apple Silicon 上的 macOS。Qt 现在可以轻松创建通用二进制文件并在 Intel 和 Apple Silicon 上为 macOS 进行开发。当然,该版本也在我们的 CI 系统中进行了全面测试。一直可以通过 Rosetta 层在 Apple 芯片上运行 Qt 应用程序,但 Qt 6.2 现在提供了在 Apple 芯片上本地运行的完整支持。
Qt 6.2 还恢复了对 INTEGRITY 和 QNX 实时操作系统的支持。支持需要 C++17 工具链和最新版本的操作系统。QNX 的最低要求是 7.1 版,在 INTEGRITY 上,我们支持 19.0.13 版。
针对 Qt 6.2 的 webOS 验证也已完成,以进一步加强 Qt 对 webOS 的承诺。
有很多工作正在进行以支持 Windows 11,我们希望能够在 6.2 补丁级别版本中为其提供全面支持。Windows on ARM HW 也可作为 Qt 6.2 的技术预览版提供。
最后,我们做了进一步的工作来改进我们对 WebAssembly 的支持,它在 Qt 6.2 中作为技术预览提供支持。
Qt for Python 今天也发布了,大家可以试一试。未来几天将发布一篇单独的博客文章,重点介绍最新 Qt 6.2 更改中采用的所有功能。敬请关注!
我们尝试维护每个版本中所有公共 API 的二进制和源代码兼容性。但是为了使 Qt 成为更好的框架,一些变化是不可避免的。
在本主题中,我们总结了 Qt Core 中的这些变化,并提供了处理它们的指导。
对于自定义类型,
QHash
和
QMultiHash
依赖于您在同一命名空间中提供
自定义 qHash() 函数。
在 Qt 4 和 Qt 5 中,函数的返回值和可选的第二个参数的
qHash
类型是
uint
. 在 Qt 6 中,它是
size_t
.
也就是说,你需要改变
1 |
uint qHash(MyType x, uint seed); |
1 |
size_t qHash(MyType x, size_t seed); |
这允许 QHash 、 QMultiHash 和 QSet 在 64 位平台上保存超过 2^32 个项目。
Qt 6 中QHash 、 QMultiHash 和 QSet 的实现从基于节点的方法更改为两阶段查找表。这种设计允许保持哈希实例的内存开销非常小,同时提供良好的性能。
需要注意的一个行为变化是,当表需要增长或删除条目时,新实现将不会提供对散列中元素的稳定引用。依赖这种稳定性的应用程序现在可能会遇到未定义的行为。
在 Qt 5 中, QHash 可以通过使用 QHash::insertMulti 来创建多值散列,而 QMultiHash 是派生 vom QHash 的 。
在 Qt 6 中,类型和用例都是不同的,并且 QHash::insertMulti 被删除了。
在 Qt 6 之前, QVector 和 QList 是独立的类。在 Qt 6 中,它们是统一的:Qt 5 QList 实现消失了,两个类都使用更新 的 QVector 实现。 QList 是具有实际实现的类, QVector 是 QList 的别名(typedef) 。
QList 的 fromVector() 和 toVector() 以及 QVector 的 fromList() 和 toList() 在 Qt 6 中不再涉及数据复制。它们现在返回调用它们的对象。
QList
的(以及
QVector
的)大小类型从 更改
int
为
qsizetype
。连同大小类型,所有相关方法的签名都更新为使用
qsizetype
. 这允许
QList
在 64 位平台上保存超过 2^31 个项目。
在将代码库升级到 Qt 6 时,此 API 更改很可能会导致编译器警告关于缩小类型转换。具有以下示例代码:
1 |
void myFunction(QList<MyType> &data) { |
您需要更新它以使用其中一个
qsizetype
或一个 auto 关键字:
1 |
void myFunction(QList<MyType> &data) { |
或者,您可以使用类型转换并将所有内容转换为
int
或
qsizetype
。
**注意:**如果您想同时针对 Qt 5 和 Qt 6 进行构建,auto 关键字是一个很好的解决方案,可以覆盖版本之间的签名差异。
QList 收到了与 Qt 6 中的内存布局相关的多项更改。
在 Qt 5 中,
sizeof(QList<T>)
等于指针的大小。现在,额外的指针间接被删除,
QList
数据成员直接存储在对象中。默认情况下,期望
sizeof(QList<T>)
等于 3 个指针的大小。
同时,元素的内存布局也更新了。 QList 现在总是将其元素直接存储在分配的内存区域中,而不是 Qt 5,在 Qt 5 中,某些对象被单独分配在堆上,而指向对象的指针被放置到 QList 中。
请注意,后者尤其会影响大型对象。要具有 Qt 5 行为,您可以将对象包装成智能指针并将这些智能指针直接存储在
QList中。
在这种情况下,您的
QList
的类型将与 Qt 5 中的类型
QList<MySmartPointer<MyLargeObject>>
相反。
QList<MyLargeObject>
对QVector / QList 实现进行了一些更改。QVector相关的一个是: 优化 了开头的插入(类似于Qt 5中的 QList )。 QList 相关的一个是:元素的内存布局被简化。
**重要提示:**这些更改会影响参考的稳定性。在 Qt 6 中,您应该考虑使用任何大小或容量修改方法来使所有引用无效,即使 QList 不是 隐式共享 的。此规则的例外情况已明确记录。
依赖某些参考稳定性的应用程序在升级到使用 Qt 6 时可能会遇到未定义的行为。您应该特别注意最初使用具有非 C 兼容数组布局的 QVector 或 QList的情况。
View
Qt6有几个新的类。已经存在
QStringView
,现在伴随着
QByteArrayView
,然后是专门的
QUtf8StringView
和更通用的
QAnyStringView
。
QStringView类通过 QString API的只读子集提供统一的 UTF-16 字符串视图 。 与保留自己的字符串副本(可能是引用计数)的 QString不同, QStringView 提供了存储在其他地方的字符串的视图。
1 |
char hello[]{ "Hello." }; // // 窄多字节字符串字面量 |
字符串
"Hello."
存储在二进制文件中,在运行时不分配。
view
只是对 string 的一个视图
"Hello."
,因此不必创建副本。当我们复制
QStringView
时,
viewToView
观察到的字符串与复制源观察到的字符串相同
view
。这意味着
viewToView
不需要创建副本或原子增量。它们是现有字符串的视图
"Hello."
。
1 |
void myfun1(QStringView sv); // 首选 |
QStringView 支持让我们操作字符串视图的函数。这允许我们在不创建已查看字符串的部分副本的情况下更改视图。
1 |
QString pineapple = "Pineapple"; |
'\0'
QStringView 支持空终止和非空终止字符串。不同之处在于您初始化 QStringView 的方式:
1 |
QChar aToE[]{ 'a', 'b', 'c', 'd', 'e' }; |
由于
views
不拥有它们引用的内存,因此必须注意确保引用的数据(例如,由
QString
拥有)
view
在所有代码路径上的寿命都更长。
1 |
QStringView sayHello() |
QStringView 不会隐式或显式转换为 QString ,但可以创建其数据的深层副本:
1 |
void print(const QString &s) { qDebug() << s; } |
通过利用新的视图类,可以在许多用例中实现大量性能提升。但是,重要的是要知道可能有一些警告。因此,重要的是要记住:
从 Qt6 开始,通常建议使用
QStringView
over
QStringRef
。
QStringView
引用它不拥有的 UTF-16 字符串的连续部分。它充当各种 UTF-16 字符串的接口类型,无需先构造
QString
。QStringView类公开了几乎所有
QString
的只读方法
和
以前存在的
QStringRef
类。
**注意:**必须注意确保引用的字符串数据(例如,由 QString 拥有)在所有代码路径上都超过 QStringView 。
**注意:**如果
QStringView
包装了
QString
,则需要小心,因为与
QStringRef
QStringView不同,一旦
QString
数据重定位,将不会更新内部数据指针。
1 |
QString string = ...; |
在 Qt6中,
QStringRef
已从 Qt Core 中删除。为了在不涉及整个代码库的情况下简化现有应用程序的移植,
QStringRef
该类并没有完全消失,而是被移到了 Qt5Compat 模块中。如果您想
QStringRef
进一步使用,请参阅
使用 Qt5Compat 模块
。
不幸的是,
QString
公开的一些方法返回 a
QStringRef
,无法移动到 Qt5Compat。因此,可能需要一些手动移植。如果您的代码使用以下一个或多个函数,您需要将它们移植到使用
QStringView
或
QStringTokenizer
。对于性能关键代码,还建议使用
QStringView::tokenize
而不是
QStringView::split 。
使用以下代码更改代码
QStringRef
:
1 |
QString string = ...; |
1 |
QString string = ...; |
在 Qt 6 中, QRecursiveMutex 不再继承自 QMutex 。进行此更改是为了提高 QMutex 和 QRecursiveMutex 的性能。
由于这些更改, QMutex::RecursionMode 枚举已被删除,并且 QMutexLocker 现在是一个模板类,可以在 QMutex 和 QRecursiveMutex 上运行。
为避免意外使用 QFuture ,Qt 6 中对 QFuture API进行了一些更改,这可能会导致源代码兼容性中断。
QFuture<T>
到的转换
T
已被禁用。转换操作符调用
QFuture::result
(),如果用户在尝试进行转换之前通过
QFuture::takeResult () 从
QFuture
移动了结果,这可能会导致未定义的行为。在需要转换
QFuture<T>
到
T
。
QFuture<T>
从to的隐式转换
QFuture<void>
也被禁用。如果您真的打算进行转换,请使用显式
QFuture<void>(const QFuture<T> &)
构造函数:
1 |
QFuture<int> future = ... |
QFuture
的相等运算符已被删除。他们正在比较底层的 d 指针,而不是比较结果,这可能不是用户所期望的。如果需要比较
QFuture
对象,使用
QFuture::result()
or
QFuture::takeResult()
方法。例如:
1 |
QFuture<int> future1 = ...; |
在 Qt 6 中,对QFuture 和 QFutureWatcher 进行了一些改进,导致以下行为变化:
pause()
or
setPaused(true)
)后,
QFutureWatcher
不会立即停止传递进度和结果就绪信号。在暂停的那一刻,可能仍有计算正在进行且无法停止。此类计算的信号可能在暂停后仍被传递,而不是被推迟并仅在下一次恢复后才报告。要在暂停实际生效时得到通知,可以使用
QFutureWatcher::suspended () 信号。
此外,还有新的
isSuspending()
和
isSuspended()
方法,用于检查
QFuture
是处于挂起过程中还是已经处于挂起状态。请注意,出于一致性原因,对于两者
QFuture
和
QFutureWatcher
与暂停相关的 API 已被弃用,取而代之的是名称中带有“suspend”的类似方法。
waitForFinished()
立即退出。这同样适用于
QFutureWatcher::waitForFinished
()。此更改不会影响将
QFuture
与
QtConcurrent
一起使用的代码的行为。只有在未记录的情况下使用它的代码
QFutureInterface
可能会受到影响。
在 Qt 6 中,应该使用新的 QPromise类而不是非官方的 QFutureInterface 作为 QFuture 的“setter”对应物。
在 Qt 6 中,通过将单个命令字符串拆分为程序名称和参数来解释单个命令字符串的 QProcess::start () 重载重命名为 QProcess::startCommand ()。但是,存在采用单个字符串的 QProcess::start () 重载以及用于参数的 QStringList 。 由于 QStringList 参数默认为空列表,因此仅传递字符串的现有代码仍然可以编译,但如果它是包含参数的完整命令字符串,则将无法执行该过程。
Qt 5.15 为相应的重载引入了弃用警告,以便于发现和更新现有代码:
1 |
QProcess process; |
QProcess::pid() 和 Q_PID 类型已被移除;使用
QProcess::processId
() 来获取本机进程标识符。
PROCESS_INFORMATION
不再支持使用本机 Win32 API 以 Win32 结构访问 Q_PID 中的数据的代码。
QVariant
已被重写以
QMetaType
用于其所有操作。这意味着一些方法的行为改变:
QVariant::isNull()
现在仅
true
在 the
QVariant
为空或包含 a时返回
nullptr
。在 Qt 5 中,如果 qtbase 中的类
isNull
本身有一个方法返回 true,它也会返回 true。依赖于旧行为的代码需要检查包含的值是否返回 isNull ——然而这种代码在实践中不太可能出现,因为
isNull()
很少有人感兴趣的属性(比较
QString::isEmpty()
/
isNull()
和
QTime::isValid
/
isNull
)。
QVariant::operator==
在 Qt 6 中使用
QMetaType::equals
。因此,某些图形类型,如
QPixmap
,
QImage
并且
QIcon
永远不会比较相等。此外,存储在中的浮点数
QVariant
不再与 进行比较
qFuzzyCompare
,而是使用精确比较。
此外,QVariant::operator<、QVariant::operator<=、QVariant::operator> 和 QVariant::operator>= 被删除,因为不同的变体并不总是可订购的。这也意味着 QVariant 不能再用作 QMap 中的键。
在 Qt 6 中,比较器以及
QDebug
和
QDataStream
流操作符的注册是自动完成的。因此,、
QMetaType::registerEqualsComparator()
和不再存在。移植到 Qt 6 时,必须删除对这些方法的调用。
QMetaType::registerComparators()``qRegisterMetaTypeStreamOperators()``QMetaType::registerDebugStreamOperator()
Q_MOC_INCLUDE
宏。如果包含标头会导致循环依赖,或者会减慢编译速度,这将很有帮助。
在 Qt6 中,所有采用 的方法都已
QRegExp
从我们的代码库中删除。因此,您很可能必须将您的应用程序或库移植到
QRegularExpression
。
QRegularExpression 实现了与 Perl 兼容的正则表达式。它完全支持Unicode。 有关QRegularExpression 支持的正则表达式语法的概述,请参阅前面提到的 pcrepattern(3) 手册页。一个正则表达式由两部分组成:一个模式字符串和一组改变模式字符串含义的模式选项。
QRegularExpression
之间存在一些细微的差异
QRegExp
,本文档将对此进行解释以简化移植工作。
QRegularExpression 在正则表达式的语法方面更加严格。因此检查表达式的 有效性 总是好的。
QRegularExpression
几乎总是可以声明为 const (模式改变时除外),而
QRegExp
几乎不可能。
CaretMode 枚举没有替代品。QRegularExpression ::AnchoredMatchOption 匹配选项可用于模拟 QRegExp::CaretAtOffset 行为。 其他QRegExp::CaretMode 模式没有等价物。
QRegularExpression 仅支持与 Perl 兼容的正则表达式。尽管如此,它仍然不支持 Perl 兼容的正则表达式中可用的所有功能。最值得注意的是不支持捕获组的重复名称,使用它们可能会导致未定义的行为。这可能会在未来的 Qt 版本中改变。
在 QRegularExpression 中没有直接的方法来进行通配符匹配。但是,提供了 QRegularExpression::wildcardToRegularExpression 方法来将 glob 模式转换为可用于该目的的 Perl 兼容的正则表达式。
例如,如果您有类似的代码
1 |
QRegExp wildcard("*.txt"); |
您可以将其重写为
1 |
auto wildcard = QRegularExpression(QRegularExpression::wildcardToRegularExpression("*.txt")); |
请注意,并非所有类似于通配符模式的 shell 都可能以您期望的方式进行翻译。如果简单地使用上述函数进行转换,以下示例代码将静默中断:
1 |
const QString fp1("C:/Users/dummy/files/content.txt"); |
字符串内的前向搜索通常通过循环使用
QRegExp::indexIn
和不断增长的偏移量来实现,但现在可以使用
QRegularExpressionMatchIterator
或
QString::indexOf
轻松实现。
例如,如果您有类似的代码
1 |
QString subject("the quick fox"); |
您可以将其重写为
1 |
QRegularExpression re("(\\w+)"); |
在字符串中向后搜索通常是作为循环实现的
QRegExp::lastIndexIn
,但现在可以使用
QString::lastIndexOf
和
QRegularExpressionMatch
轻松实现。
注意: QRegularExpressionMatchIterator 不能执行向后搜索。
例如,如果您有类似的代码
1 |
int offset = -1; |
您可以将其重写为
1 |
qsizetype from = -1; |
QRegExp::exactMatch
有两个目的:将正则表达式与主题字符串完全匹配,并实现部分匹配。精确匹配表示正则表达式是否匹配整个主题字符串。例如:
1 |
QString source("abc123"); |
精确匹配未反映在 QRegularExpression 中。如果要确保主题字符串与正则表达式完全匹配,可以使用 QRegularExpression::anchoredPattern 函数包装模式:
1 |
QString source("abc123"); |
QRegExp::setMinimal()
通过简单地反转量词的贪婪来实现最小匹配(
QRegExp
不支持惰性量词,如*?、+?等)。
相反, QRegularExpression
确实支持贪婪、懒惰和所有格量词。QRegularExpression
::InvertedGreedinessOption
模式选项可用于模拟
QRegExp::setMinimal()
: 如果启用,它会反转量词的贪婪(贪婪的变得懒惰,反之亦然)。
将正则表达式从 QRegularExpression 移植
QRegExp
可能
需要
更改模式本身。因此建议检查与
QRegularExpression::isValid
方法一起使用的模式。这对于用户提供的模式或不受开发人员控制的模式尤其重要。
在其他情况下,从
QRegularExpression
QRegExp
移植的模式可能会默默地改变语义。因此,有必要审查所使用的模式。最值得注意的无声不兼容案例是:
\xHHHH
为了使用超过 2 位的十六进制转义,需要大括号。
\x2022
需要将类似的模式移植到
\x{2022}
,否则它将匹配
(0x20)
字符串后面的空格
"22"
。
\x
通常,无论指定多少位数,强烈建议始终使用带有转义的花括号。
0-to-n
量化以保留语义。否则,诸如这样的模式实际上会匹配一个数字后跟确切的字符串。
{,n}
{0,n}
\d{,3}
"{,3}"
使用 时
QRegExp::exactMatch()
,如果没有找到精确匹配,仍然可以通过调用
QRegExp::matchedLength()
. 如果返回的长度等于主题字符串的长度,则可以断定找到了部分匹配。
QRegularExpression
通过适当的
QRegularExpression::MatchType
显式支持部分匹配。
由于
QRegExp
API 的限制,不可能正确地实现全局匹配(也就是说,就像 Perl 一样)。特别是,可以匹配零个字符的模式(如“a*”)是有问题的。
QRegularExpression::wildcardToRegularExpression
正确实现 Perl 全局匹配,返回的迭代器可用于检查每个结果。
使用 时
QRegExp
,诸如 、 等字符类
\w
匹配
\d
具有相应 Unicode 属性的字符:例如,
\d
匹配具有 Unicode Nd(十进制数字)属性的任何字符。默认情况下,这些字符类仅匹配 ASCII 字符。使用
QRegularExpression
时:例如,
\d
精确匹配 0-9 ASCII 范围内的字符。可以通过使用
QRegularExpression::UseUnicodePropertiesOption
模式选项来更改此行为。
在 Qt6
中
,QRegExp从 Qt Core 中移除。如果您的应用程序现在无法移植
QRegExp
,Qt5Compat 中仍然存在以保持这些代码库正常工作。如果您想
QRegExp
进一步使用,请参阅
使用 Qt5Compat 模块
。
QEvent 类定义了一个复制构造函数和一个赋值运算符,尽管它是一个多态类。在将不同类中的对象相互分配时,使用虚拟方法复制类可能会导致切片。由于复制和分配经常隐含地发生,这可能会导致难以调试的问题。
在 Qt 6 中, QEvent 子类的复制构造函数和赋值运算符已被保护以防止隐式复制。如果您需要复制事件,请使用 clone 方法,该方法将返回 QEvent 对象的堆分配副本。确保删除克隆,可能使用 std::unique_ptr,除非您发布它(在这种情况下,Qt 将在交付后删除它)。
在您的 QEvent 子类中,覆盖 clone(),并声明受保护和默认实现的复制构造函数和赋值运算符,如下所示:
1 |
class MyEvent : public QEvent |
请注意,如果您的 MyEvent 类分配内存(例如,通过指向实现模式的指针),那么您将必须实现自定义复制语义。
在 Qt 6 中,用于将其转换为/从 Qt 的遗留 JSON 二进制格式转换的 QJsonDocument 方法被删除,以支持标准化的 CBOR 格式。Qt JSON 类型可以转换为 Qt CBOR 类型,后者又可以序列化为 CBOR 二进制格式,反之亦然。例如,参见 QCborValue::fromJsonValue () 和 QCborValue::toJsonValue ()。
如果仍然需要使用二进制 JSON 格式,可以使用 Qt5Compat 模块中提供的替换。它们可以在 QBinaryJson 命名空间中找到。请参阅 使用 Qt5Compat 模块 以了解如何在您的应用程序中使用该模块。
在 Qt 5 中, QCoreApplication::quit () 等价于调用 QCoreApplication::exit ()。这刚刚退出了主事件循环。
在 Qt 6 中,该方法将尝试通过发布关闭事件来关闭所有顶级窗口。窗口可以通过忽略该事件来自由取消关闭过程。
调用 QCoreApplication::exit () 来保持非条件行为。
由于命名不一致,不推荐使用 QLibraryInfo::location() 和 QLibraryInfo::Location。请改用新的 API QLibraryInfo::path () 和 QLibraryInfo::LibraryPath 。
Qt 状态机 被移入 Qt SCXML 模块(即将重命名为 Qt 状态机),因此它不再是 Qt 核心的一部分。Qt Core 内部很少有交叉依赖项,最终导致了这个决定。
要使用
Qt5Compat
模块,您需要在包含路径中使用它的头文件进行构建,并链接到它的库。如果您使用的是
qmake
,请将以下内容添加到您的
.pro
文件中:
1 |
QT += core5compat |
如果您使用cmake
构建应用程序或库,请将以下内容添加到您的
CMakeList.txt
:
1 |
PUBLIC_LIBRARIES |
在开发 Qt 6 时,与 Qt 5 的源代码兼容性一直是我们工作的关键部分。有一些地方我们不得不在某种程度上打破这种兼容性,以进行一些必需的架构更改或为我们带来一些巨大的性能优势。
在大多数情况下,从 Qt 5 移植到 Qt 6 应该很简单。在Qt的6移植指南 列出了所需要的步骤,并具有更多的信息。您还可以从我们的合作伙伴之一或我们的顾问那里获得移植帮助。
移植到 Qt 6 的典型步骤是:
有了这些,您就可以在 Qt 6 上运行应用程序,并可以开始使用它提供的所有新特性和功能。例如,如果您的应用程序使用 QML,请运行 qmlint 工具并修复它给出的警告。
1). 增加了很多轮子,同时原有模块拆分的也更细致,估计为了方便拓展个管理。
2). 把一些过度封装的东西移除了(比如同样的功能有多个函数),保证了只有一个函数执行该功能。
3). 把一些Qt5中兼容Qt4的方法废弃了,必须用Qt5中对应的新的函数。
4). 跟随时代脚步,增加了不少新特性以满足日益增长的客户需求。
5). 对某些模块和类型及处理进行了革命性的重写,运行效率提高不少。
6). 有参数类型的变化,比如 long * 到 qintptr * 等,更加适应后续的拓展以及同时对32 64位不同系统的兼容。
7). 源码中的double数据类型全部换成了qreal,和Qt内部数据类型高度一致和统一。
8). 我测试的都是QWidget部分,quick部分没有测试,估计quick部分更新可能会更多。
9). 强烈建议暂时不要用Qt6.0到Qt6.2之间的版本,一些模块还缺失,相对来说BUG也比较多,推荐6.2版本开始正式迁移。
1 |
QMap<std::pair<QString, QVariant>, QObject *> m_entities; |
该函数返回一个> 类型的对象,它与 . 的键类型相同。QMap 需要在键类型上定义才能知道在哪里插入新值。
it.key()``std::pair<QString, QVariant``QMap``operator<
1 |
QMap`比较该对`p1`是否小于`p2`,两者都`p1`具有`p2`类型。如果等于,则比较必须计算。G++-9 正确地抱怨没有为s 定义。G++-7 忽略了 s的缺失。`std::pair<QString, QVariant>``p1.first``p2.first``p1.second < p2.second``operator<``QVariant``operator<``QVariant |
1 |
QMap<std::pair<QString, QString>, QObject *> m_entities; |
将 a
QVariant
转换为
QString
with适用于. 由于仅对其键的第二个元素使用支持的类型,我们可以安全地替换键类型中的by 。
QVariant::toString()``QVariant``m_entities``QVariant``QString
1 |
#include <QTextCodec> |
该类
QTextCodec
已从 Qt 6 中删除,并被新的类所取代
QStringConverter
。同样,类
QTextEncoder
and
QTextDecoder
被 and 取代
QStringEncoder
(
QStringDecoder
参见这篇
文章
)。使用任何旧类的代码都无法编译。
1 |
#include <QStringConverter> |
我们包括替换类的标题
QStringConverter
。搜索 的功能最
QStringConverter
有
setEncoding
可能替代
setCodec
. 了解 ISO-8859-1 是 Latin1 的正式名称有助于我们
setEncoding
使用正确的常量调用。
QStringConverter::Latin1
1 |
#include <QRegExp> |
替换
QRegExp
——Qt 4 的遗物——
QRegularExpression
自 Qt 5.0 以来一直在酝酿之中。
QRegularExpression
是在 Qt 6 中使用正则表达式的唯一方法。界面发生了很大变化。
1 |
#include <QRegularExpression> |
将字符串与正则表达式匹配的函数有它的自然名称:(
match
而不是
indexIn
)。它返回一个
QRegularExpressionMatch
对象
match
而不是索引。我们通过调用来检索
nth
捕获的子字符串。match.hasMatch() 告诉我们字符串是否匹配正则表达式。
match.captured(nth)
1 |
QRegExp::matchedLength`返回匹配完整正则表达式的子字符串的长度。此子字符串与第 0 个捕获的子字符串相同,其长度为。`match.captured(0)``match.capturedLength(0) |
1 |
subDev = m_deviceName.midRef(splitPos + 1).toLatin1(); |
该函数在 Qt 6 中已过时。该函数也是如此。
QString::midRef``QString::leftRef
1 |
subDev = m_deviceName.mid(splitPos + 1).toLatin1(); |
我们将和分别替换为和。
QString::midRef``QString::leftRef``QString::mid``QString::left
1 |
QString extractVersion(const QFileInfo &info) const; |
在 Qt 5 中,该函数使用构造函数
extractVersion
将 隐式转换
QString
filename
为
QFileInfo
对象。在 Qt 6 中,这个构造函数被标记为,它阻止了隐式转换。
QFileInfo(const QString &)``explicit
1 |
QString extractVersion(const QFileInfo &info) const; |
我们通过显式调用构造函数来消除错误。
QFileInfo(const QString &)
1 |
class IntegerRangeModel; |
的前向声明对于Qt 5
IntegerRangeModel
中的定义来说已经足够好了,因为它被声明为一个指针。Qt 6 添加了一个静态断言。另一条错误消息记录了对静态断言的违反:
Q_PROPERTY``days
1 |
static_assert(sizeof(T), "Type argument of Q_PROPERTY or Q_DECLARE_METATYPE(T*) must be fully defined"); |
1 |
#include "IntegerRangeModel.h"; |
我们通过包含头文件
IntegerRangeModel
而不是前向声明来消除错误消息
IntegerRangeModel
。
1 |
EntityColumn(const QString &name, QVariant::Type type, ...) |
枚举列出了可以存储在. 它大致是 enumeration 的一个子集,它列出了 Qt 元对象系统已知的类型。建议自己替换。不再包含在 Qt 6 中。
QVariant::Type``QVariant``QMetaType::Type``QVariant::Type``QMetaType::Type``QVariant::Type
1 |
EntityColumn(const QString &name, QMetaType::Type type, ...) |
在 Qt 6 中,我们使用而不是. 我们还必须分别替换过时的枚举常量,例如,和,和。
QMetaType::Type``QVariant::Type``QVariant::Invalid``QVariant::UInt``QVariant::Bool``QMetaType::UnknownType``QMetaType::UInt``QMetaType::Bool
1 |
bool typesAreAffine(QMetaType::Type sqlType, ...) const; |
最初, 的第一个参数
typesAreAffine
具有 type ,它被前面的迁移步骤替换。具有返回类型的函数在 Qt 6 中不存在。
QVariant::Type``QMetaType::Type``QSqlField::type``QVariant::Type
1 |
bool typesAreAffine(QMetaType::Type sqlType, ...) const; |
我们用 Qt 6 函数替换现在已过时的 Qt 5 函数。返回一个对象。我们使用 检索对象的类型 ID ,它返回一个而不是 a来为自定义元类型打开。从整数创建一个枚举常量。
QSqlField::metaType()``QSqlField::type()``QSqlField::metaType()``QMetaType``QMetaType``id()``int``QMetaType::Type``QMetaType::Type(typeID)``typeID
1 |
QList<QFileInfo> m_dirEntries. |
从 Qt 6.0 开始,具有返回类型,相当于. DirectoryModel 类间接派生自,它像这样声明纯虚函数:
QList<T>::size()``qsizetype``long long int``QAbstractItemModel``rowCount
1 |
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const = 0 |
该
return
语句可以将一个值缩小为一个值,这可能会改变该值。似乎 Qt 开发人员忘记了将from的返回类型更改为.
long long int``int``rowCount``int``qsizetype
类似
beginInsertRows
,
beginRemoveRows
和
beginMoveRows
的函数
beginInsertColumns
也接受
int
-type 参数。我们可能还需要注意缩小转化率。
1 |
QList<QFileInfo> m_dirEntries. |
有时没有办法绕过静态演员表。这是其中之一。我们将值强制
qsizetype
转换为 int
value
并消除错误。
1 |
enum class UnitId : quint16 { ... } |
1 |
enum class UnitId : quint16 { ... } |
我们将自定义散列函数的返回类型从
uint
更改为
size_t
。
1 |
auto fs = QQmlFileSelector::get(engine); |
静态函数返回 QML 上当前活动的文件选择器。它在 Qt 6 中已过时,因为它复制了构造函数提供的功能。
QQmlFileSelector::get``engine``QQmlFileSelector
1 |
auto fs = new QQmlFileSelector(engine); |
在堆上创建
QQmlFileSelector
对象与调用. 我们必须在堆上创建对象,因为构造函数参数拥有文件选择器的所有权。
QQmlFileSelector::get``engine
1 |
class QStringList; |
包含的 Qt 头文件介绍了类型定义。有问题的行向前声明了编译器不喜欢的这种类型定义。
using QStringList = class QList<QString>
1 |
#include <QStringList> |
我们通过包含头文件来替换前向声明。
1 |
QStringList errors; |
QLatin1Literal
Qt 6 中不再存在。
1 |
QStringList errors; |
我们替换
QLatin1Literal
为
QLatin1String
。
1 |
void onStateChanged(QMediaPlayer::State state); |
我们用信号替换并连接插槽。我们还需要替换对by 的调用。
QMediaPlayer::State``QMediaPlayer::PlaybackState``onStateChanged``QMediaPlayer::playbackStateChanged``QMediaPlayer::state()``QMediaPlayer::playbackState()
1 |
void onStateChanged(QMediaPlayer::PlaybackState state); |
1 |
#include <QMediaPlaylist> |
Changes to Qt Multimedia
页面通知我们该类
QMediaPlaylist
已从 Qt 6 中删除。当显示信息、警告或错误对话框时,应用程序会播放不同的 MP3 文件。它将
QMediaPlaylist
带有正确 MP3 文件的 a 传递给
QMediaPlayer
. 用户还可以更改播放音量。
1 |
QMediaPlaylist`替换为。音量不能直接用 改变,但可以用 间接改变。以下有关 QMediaPlayer 和 QMediaPlaylist 的错误消息告诉我们要修复什么。`QMediaPlayer::setSource``QMediaPlayer::setVolume``QAudioOutput::setVolume |
1 |
m_player.setVolume(80); |
Qt 6 将函数
setVolume
和
volume
从
QMediaPlayer
移至
QAudioOutput
.
使用的客户端类必须在其构造函数中向该对象
QMediaPlayer
注册一个
QAudioOutput
对象。我们必须告诉对象它应该在哪个扬声器上播放 MP3 文件。我们可以使用 访问默认扬声器。这对于只有一个扬声器的终端硬件来说已经足够了。
QMediaPlayer``m_player``QAudioOutput``QMediaDevices::defaultAudioOutput
客户端类将音量设置为默认值(例如,0.8)。请注意,新的采用 0.0 到 1.0 之间的浮点值作为参数,而旧的采用 0 到 100 之间的整数。
QAudioOutput::setVolume``QMediaPlayer::setVolume
1 |
auto audioOut = new QAudioOutput{}; |
客户端类的函数改变音量如下:
setVolume(int vol)
1 |
m_player.audioOutput()->setVolume(0.8f); |
当然,标题错误信息并不是真正的编译器错误。由于 QMediaPlaylist 从 Qt 6 中删除,以下错误类别的播放功能不再编译。
1 |
void AudioService::Impl::playFeedback(int category) |
为给定播放 MP3 文件的修改函数
category
如下所示。
1 |
void AudioService::Impl::playFeedback(int category) |
1 |
import QtQuick 2.15 |
Qt 5 需要导入模块的版本。Qt 6 不会抱怨这些版本,但它不再需要它们。
1 |
import QtQuick |
在 Qt 6 中只导入一个没有版本的模块就足够了。我们从所有导入语句中删除了版本。
1 |
// File: RDirectBar.qml |
信号在 的实例化中
selectedPage
连接到信号处理程序。Qt 6 接受带有警告的旧式信号处理程序。
onSelectedPage``RDirectBar
新方法是使用箭头函数或匿名函数(参见Qt 6.2 文档中的 信号参数 )。我们在 C++ 中称它们为 lambda 函数。
1 |
//pro文件引入模块 |
(1). 默认Qt6开启了高分屏支持,界面会变得很大,甚至字体发虚,很多人会不习惯,因为这种模式如果程序很多坐标计算没有采用devicePixelRatio进行运算的话,100%会出现奇奇怪怪的问题,因为坐标不准确了。要取消这种效果可以设置高分屏缩放因子。
1 |
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) |
(1). 原有的随机数函数提示用QRandomGenerator替代,为了兼容所有qt版本,改动最小的办法是直接用c++中的随机数,比如qsrand函数换成srand,qrand函数换成rand,查看过源代码,其实封装的就是c++中的随机数,很多类似的封装比如qSin封装的sin。
(2). QColor的 light 改成 lighter ,dark 改成 darker,其实 lighter、darker 这两个方法以前一直有。
(3). QFontMetricsF 中的 fm.width 换成 fm.horizontalAdvance ,从5.11开始用新函数。
(4). QPalette调色板枚举值,Foreground = WindowText, Background = Window,其中 Foreground 和 Background 没有了,要用 WindowText 和 Window 替代,以前就有。类似的还有 setTextColor 改成了 setForeground 。
(5). QWheelEvent的 delta() 改成 angleDelta().y(),pos() 改成 position() 。
(6). svg模块拆分出来了svgwidgets,如果用到了该模块则需要在pro增加 QT += svgwidgets 。
(7). qlayout中的 margin() 函数换成 contentsMargins().left(),查看源码得知以前的 margin() 返回的就是 contentsMargins().left(),在四个数值一样的时候,默认四个数值就是一样。类似的还有setMargin移除了,统统用setContentsMargins。
(8). 之前 QChar c = 0xf105 全部要改成强制转换 QChar c = (QChar)0xf105,不再有隐式转换,不然编译报错提示error: conversion from ‘int’ to ‘QChar’ is ambiguous 。
(9). qSort等一些函数用回c++的 std::sort 。
1 |
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) |
(1). Qt::WA_NoBackground 改成 Qt::WA_OpaquePaintEvent 。
(2). QMatrix 类废弃了没有了,换成 QTransform ,函数功能基本一致,QTransform 类在Qt4就一直有。
(3). QTime 计时去掉了,需要改成 QElapsedTimer ,QElapsedTimer 类在Qt4就一直有。
(4). QApplication::desktop()废弃了, 换成了 QApplication::primaryScreen()。
1 |
#if (QT_VERSION > QT_VERSION_CHECK(5,0,0)) |
(1). 获取当前屏幕索引以及尺寸需要分别处理。
1 |
//获取当前屏幕索引 |
(1). QRegExp类移到了core5compat模块,需要主动引入头文件 #include 。
1 |
//设置限制只能输入数字+小数位 |
(1). QWheelEvent构造参数和对应的计算方位函数变了。
1 |
//模拟鼠标滚轮 |
(1). qVariantValue 改成 qvariant_cast ,qVariantSetValue(v, value) 改成了 v.setValue(val)。相当于退回到最原始的方法,查看qVariantValue源码封装的就是qvariant_cast。
(2). QStyleOption的init改成了initFrom。
(3). QVariant::Type 换成了 QMetaType::Type ,本身以前的 QVariant::Type 封装的就是 QMetaType::Type 。
(4). QStyleOptionViewItemV2 V3 V4 之类的全部没有了,暂时可以用 QStyleOptionViewItem 替代。
(5). QFont的 resolve 的一个重载函数换成了 resolveMask。
(6). QSettings的 setIniCodec 方法移除了,默认就是utf8,不需要设置。
(7). qcombobox 的 activated(QString) 和 currentIndexChanged(QString) 信号删除了,用int索引参数的那个,然后自己通过索引获取值。个人觉得这个没必要删除。
(8). qtscript模块彻底没有了,尽管从Qt5时代的后期版本就提示为废弃模块,一致坚持到Qt6才正式废弃,各种json数据解析全部换成qjson类解析。
(9). QByteArray 的 append indexOf lastIndexOf 等众多方法的QString参数重载函数废弃了,要直接传 QByteArray,就在原来参数基础上加上 .toUtf8() 。查看源码也看得到以前的QString参数也是转成.toUtf8()再去比较。
(10). QDateTime的时间转换函数 toTime_t + setTime_t 名字改了,对应改成了 toSecsSinceEpoch + setSecsSinceEpoch ,这两个方法在Qt5.8时候新增加的。
(11). QLabel的 pixmap 函数之前是指针 *pixmap() 现在换成了引用 pixmap()。
(12). QTableWidget的 sortByColumn 方法移除了默认升序的方法,必须要填入第二个参数表示升序还是降序。
(13). qtnetwork中的错误信号error换成了errorOccurred。
1 |
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) |
(1). XmlPatterns模块木有了,全部用xml模块重新解析。
(2). nativeEvent的参数类型变了。
1 |
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) |
(1). QButtonGroup的buttonClicked信号中int参数的函数全部改名字叫idClicked。
1 |
QButtonGroup *btnGroup = new QButtonGroup(this); |
(1). QWebEngineSettings之前是QWebEngineSettings::defaultSettings();现在改成了QWebEngineProfile::defaultProfile()->settings();通过查看之前的源码得知QWebEngineSettings::defaultSettings();封装的就是QWebEngineProfile::defaultProfile()->settings();因为Qt6去除了N多过度封装的函数。
1 |
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) |
![]() |
憨厚的可乐 · 國立臺灣大學建築與城鄉研究所-博士論文 1 周前 |
![]() |
阳刚的烤地瓜 · 达利的奇幻艺术珠宝:设计如何疯狂跨界_财经_中国网 1 年前 |
![]() |
内向的烤面包 · 灵异狐妖情 - 抖音 1 年前 |
![]() |
胡子拉碴的八宝粥 · 「漫画」全职高手:哥哥的嘴骗人的鬼 - 知乎 1 年前 |
![]() |
长情的荒野 · 牧午之森 - 🌈️包子漫畫 2 年前 |