Qt6 | 开发经验C++、QML、安卓常见问题合集

510 阅读16分钟
Qt6 安卓改动

【一、】

    Qt6中对安卓支持部分做了大的改动,目前还不完善,如果是不涉及到与java交互的纯Qt项目,可以正常移植,涉及到的暂时不建议移植到Qt6,等所有类完善了再说。

- 移除了安卓插件androidextras,将其中部分功能类移到core模块中,不需要额外引入。

- 类名发生了变化,比如QAndroidJniObject改成了QJniObject、QAndroidJniEnvironment改成了QJniEnvironment,可能是为了统一移动开发平台类,弱化安卓的影响。

- 对应的安卓jdk要用jdk11而不是jdk1.8,Qt5.15两个都支持,建议就统一用jdk11。

- 对应封装的java类包名去掉了qt5标识,org.qtproject.qt5.android.bindings.QtActivity改成了org.qtproject.qt.android.bindings.QtActivity、org.qtproject.qt5.android.bindings.QtApplication改成了org.qtproject.qt.android.bindings.QtApplication。

- 对安卓最低sdk有要求,所以建议在配置AndroidManifest.xml文件的时候不要带上最低版本要求。

- 对AndroidManifest.xml文件内容有要求,之前Qt5安卓的不能在Qt6安卓下使用,具体内容参见示例下的文件。

- 对应示例demo在 C:\Qt\Examples\Qt-6.3.0\corelib\platform 目录下,之前是 C:\Qt\Examples\Qt-5.15.2\androidextras ,目前就一个示例,可能因为其他类还没有移植好。

- Qt6中安卓模块介绍在这里 doc.qt.io/qt-6/qtandr…

【二、】

如果想要安卓全屏遮挡住顶部状态栏,可以在main函数中将show改成showFullScreen即可,当然也可以采用java的方式在onCreate函数中加一行

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

Qt6 搭建安卓开发环境

新版的qtc搭建安卓开发环境非常简单,早期版本的非常复杂,要东下载西下载,折腾好多天才行。

现在只需要安装jdk文件(jdk_8.0.1310.11_64.exe),全部默认一步到位,然后在qtc中安卓配置界面,设置jdk的安装目录。然后打开 D:\Qt\Qt6\Tools\QtCreator\share\qtcreator\android\sdk_definitions.json

和 C:\Users\Administrator\AppData\Roaming\QtProject\qtcreator\android\sdk_definitions.json

将里面的 cmdline-tools;latest 修改为 cmdline-tools;6.0  ,这一步非常关键,默认是latest导致待会自动下载sdk/ndk的时候会下载不全。改好以后,设置sdk保存目录,单击右侧的 Set Up SDK 按钮,自动下载一堆文件,最后下面有个openssl的目录文件也设置下。该文件网上可以非常简单就能直接下载到,右侧就有按钮单击打开下载页面。然后就可以开始愉快的安卓开发之旅了。

Qt 版本迭代

【1】Qt一共有几百个版本,关于如何选择Qt版本的问题,我一般保留四个版本,为了兼容Qt4用4.8.7,最后的支持XP的版本5.7.0,最新的长期支持版本比如5.15,最高的新版本比如5.15.2。强烈不建议使用4.7以前和5.0到5.3之间的版本(Qt6.0到Qt6.2之间、不含6.2的版本也不建议,很多模块还没有集成),太多bug和坑,稳定性和兼容性相比于之后的版本相当差,能换就换,不能换睡服领导也要换。如果没有历史包袱建议用5.15.2,目前新推出的6.0版本也强烈不建议使用,官方还在整合当中,好多类和模块暂时没有整合,需要等到6.2.2版本再用。考虑到qss性能以及自带mysql驱动的因素,最终Qt5选用5.12.3,Qt4选用4.8.7,Qt6选用6.5.x。

【2】Qt和msvc编译器常见搭配是Qt5.7+VS2013、Qt5.9+VS2015、Qt5.12+VS2017、Qt5.15+VS2019、Qt6.2+VS2019,按照这些搭配来,基本上常用的模块都会有,比如webengine模块,如果选用的Qt5.12+msvc2015,则很可能官方没有编译这个模块,只是编译了Qt5.12+msvc2017的,如果一定要用msvc2015不想换msvc2017则只能选择Qt5.9+msvc2015套件,或者自行源码重新编译(这个难度超大,初学者绕过)。

【3】Qt默认有对应VS版本,在下载对应VS插件的时候心里要有个数,官方默认提供的是原配的插件,如果想要Qt4.8+VS2015的插件,需要自行编译。一般来说是Qt4.8原配VS2010,Qt5.6原配VS2013,Qt5.9原配VS2015,Qt5.12原配VS2017,Qt5.15原配VS2019,切记:原配最好。

【4】用Qt做开发机器建议用win10,尤其是2021年以后新发布的Qt版本,比如Qt5.12.12、Qt5.15.2、Qt6.2.2等,因为很可能自带的QtCreator用的最新的版本,Qt6开始不再支持win7,或者由于其他的原因,对win7的支持不友好,会出现奇奇怪怪的问题等,所以又是没得选必须用win10。建议各位拥抱新时代的变化,这世上唯一不变的只有变化。

【5】新版本Qt安装包安装的时候需要填写注册信息,如果不想填写,先禁用网卡,在运行安装包,可以直接跳过这一步进行安装。从Qt5.15开始不再提供离线安装包,意味着必须使用在线安装器安装Qt的后续版本,必须填写用户信息,没得选。

【6】写程序过程中尤其要注意32位的库和64位的库互不兼容,比如32位的程序引用64位的库,64位的程序引用32位的库,都是编译通不过的,而在windows64位系统中是能够运行32位程序的,因为64位的系统提供了32位的运行环境,一般目录在Program Files(x86) ,32位的程序在64位的环境中最终引用的还是32位的库。关于如何判断自己的Qt库是多少位,有个误区就是很多人要么看成了QtCreator的关于信息中列出的位数,要么以为自己是64位的系统就认为是64位的Qt,最终要在Qt构建套件中查看具体位数,大概从Qt5.14开始基本上很少提供32位的库,尤其是Qt6.0以后基本上默认就是只有64位的库了,这也是顺应时代潮流,毕竟不久的将来(个人预计2030年以前)基本上32位的系统占比不超过1%,放心大胆的用64位的库吧,抛弃烦人的32位以及XP系统。

【7】Qt6.0发布了,是个比较大的改动版本,很多基础的类或者组件都放到单独的源码包中,需要自行官网下载并编译,默认不提供集成在开发目录下,需要手动编译并集成,比如QRegExp,QTextCodec类,需要编译集成后pro文件 QT += core5compat 才能用, 具体说明在doc.qt.io/qt-6/qtcore…

【8】关于Qt众多版本(至少几百个)都不兼容的问题,在经过和Qt中国的xx大神和其他大神(Qt非官方技术交流群)头脑风暴以后,最终得出以下的结论。

- Qt在二进制兼容这块,已经做了最大的努力,通过将各种代码细节隐藏,Q指针+D指针技巧,尽量保持了接口的统一;

- 是否兼容最主要考虑编译器的因素,毕竟任何Qt版本都是需要通过编译器编译成对应的二进制文件,由他说了算。如果两个Qt版本采用的编译器版本一样,极大概率可执行文件是兼容的,比如 Qt5.10+msvc2015 32 位 和 Qt5.11+msvc2015 32位 编译出来的可执行文件,都用Qt5.11的库是可行的;

- mingw编译器的Qt版本也是如此,就是因为Qt官方安装包集成的mingw编译器一直在更新(极少附近版本没有更新mingw编译器版本除外),比如5.7用的mingw53,5.12用的mingw73,5.15用的mingw81,因为带的Qt库也是这个编译器编译出来的,所以导致看起来全部不兼容;

- 如果想要完全兼容,还有一个注意要素,那就是对应代码使用的类的头文件接口是否变了,按道理原有的接口极少会变,一般都是新增加,或者大版本才会改变,比如Qt4-Qt5-Qt6这种肯定没法兼容的,接口和模块都变了;

- 大胆的猜测:如果Qt5.6到Qt5.15你全部用一种编译器比如mingw73或者msvc2015重新编译生成对应的Qt运行库,然后在此基础上开发程序,最后生成的可执行文件用Qt5.15的库是都可以的,这样就轻松跨越了多个版本兼容;

- 大胆的建议:在附近的几个版本统一编译器,比如5.6-5.12之间就统一用mingw53或者msvc2015,5.12-5.15统一用msvc2017,要尝鲜其他编译器的可以自行源码编译其他版本,这样最起码附近的一大段版本(大概2-3年的版本周期)默认就兼容了。

- 本人测试的是widget部分,qml未做测试,不清楚是否机制一样;

【9】QDateTime可以直接格式化输出星期几周几,Qt6默认按照英文输出比如 ddd = 周二 Tue  dddd = 星期二 Tuesday ,此时如果只想永远是中文就需要用到QLocale进行转换。

```cpp//格式化输出受到本地操作系统语言的影响​//英文操作系统//这样获取到的是Mon到Sun,英文星期的3个字母的缩写。QDateTime::currentDateTime().toString("ddd");//这样获取到的是Monday到Sunday,英文星期完整单词。QDateTime::currentDateTime().toString("dddd");​//中文操作系统//这样获取到的是周一到周日。QDateTime::currentDateTime().toString("ddd");//这样获取到的是星期一到星期日。QDateTime::currentDateTime().toString("dddd");​//主动指定语言转换//如果没有指定本地语言则默认采用系统的语言环境。QLocale locale;//QLocale locale = QLocale::Chinese;//QLocale locale = QLocale::English;//QLocale locale = QLocale::Japanese;​//下面永远输出中文的周一到周日locale.toString(QDateTime::currentDateTime(), "ddd");//下面永远输出中文的星期一到星期日locale.toString(QDateTime::currentDateTime(), "dddd");```

【10】由于Qt版本众多,有时候为了兼容多个版本甚至跨度Qt4/Qt5/Qt6的兼容,有些头文件或者类名等变了或者新增了,需要用到Qt版本的判断。需要注意的是如果在头文件中使用 QT_VERSION_CHECK 需要先引入#include "qglobal.h"不然编译失败,因为 QT_VERSION_CHECK 这个函数在 qglobal.h 头文件中。

```cpp//至少要包含 qglobal.h,理论上Qt所有的类都包含了这个头文件,所以你引入Qt的其他头文件也行比如 qobject.h#include "qglobal.h"#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))#include "qscreen.h"#else#include "qdesktopwidget.h"#endif```

【11】- Qt4.8.7是Qt4的终结版本,是Qt4系列版本中最稳定最经典的(很多嵌入式板子还是用Qt4.8),其实该版本是和Qt5.5差不多时间发布的。参考链接 www.qt.io/blog/2015/0… blog.qt.io/blog/2015/0…

- Qt5.6.3最最后支持xp系统的长期支持版本,Qt5.7.0是最后支持xp系统的非长期支持版本(有可能有极少数函数不支持,个人没遇到过)。

- Qt5.12.3是最后提供mysql数据库插件的版本,往后的版本需要自行编译对应的mysql数据库插件,官方安装包不再提供。

- Qt5.12.5是最后样式表性能最高的版本,经过酷码大佬查阅代码发现此后版本的样式表源码中为了修复一个bug做了循环嵌套设置,导致性能急剧下降,界面越多性能暴降10倍以上。

- Qt5.14.2是最后提供二进制安装包的版本,后面的版本都需要在线安装。

- Qt5.15系列是最后支持win7的版本,后面的Qt6系列版本需要更改源码编译才能支持win7,这对于小白来说难于上青天。

- Qt6.0/6.1版本其实也是支持win7的,但是因为缺失太多模块,而且BUG成山,大佬说了狗都不用,所以使用此版本没意义。

- Qt6不支持win7,是说开发阶段和运行阶段都不支持,无论开发阶段还是运行阶段你都需要Qt的库,只要是Qt的库不支持,到哪里也不支持。

- 新版的qtc7由于采用Qt6编译,所以也只能在win10及以上运行,意味着你要用新的qtc7+Qt5做开发也必须用win10及以上。

- 欢迎各位补充,比如哪个版本以后商用需要收费之类的,貌似用Qt4,在不更改Qt本身源码,动态库发布程序,法律风险小一些?

【12】从Qt6.4版本开始多媒体模块提供了ffmpeg作为后端解码使用(6.5版本默认就是ffmpeg),可以通过设置环境变量来更改使用哪种后端解码,在main函数的第一行 qputenv("QT_MEDIA_BACKEND", "ffmpeg"); 目前已知的问题是如果选用ffmpeg则暂时不支持中文目录以及中文名称(在6.5.1修复了),如果一定要支持中文则需要改成windows。

```cpp//设置后端解码为ffmpeg/所有系统都支持qputenv("QT_MEDIA_BACKEND", "ffmpeg");//windows系统专用qputenv("QT_MEDIA_BACKEND", "windows");//linux系统专用qputenv("QT_MEDIA_BACKEND", "gstreamer");//mac系统专用qputenv("QT_MEDIA_BACKEND", "darwin");//android系统专用qputenv("QT_MEDIA_BACKEND", "android");```

【13】用QSettings类保存float类型的时候,内容会变成 @Variant 开头的一个值,后面根本看不懂什么值,比如 1.0 = @Variant(\0\0\0\x87?\x80\0\0) 一个非常奇怪的值,这样的话如果想直接修改配置文件来更改参数就无从下手。有两个办法解决问题,办法一就是在写入值的时候强制转换成QString类型数据即可,set.setValue("SaveVideoRatio", QString::number(SaveVideoRatio));,办法二就是将float参数类型改成double,比如 float SaveVideoRatio 改成 double SaveVideoRatio,推荐方法一,不用更改数据类型,就改动一行即可,而且double数据类型的精度不一样,比如 float i = 0.1 会变成 double i = 0.10000000149011612 。在Qt6中彻底修复了这个问题,不需要转换。

【14】新版的Qt6.5在ubuntu上编译运行程序后会提示 qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found. ,无法正常弹出窗体程序,你需要主动安装xcb的相关库。

sudo apt install libxcb*

【15】现在很多linux用wayland作为桌面显示,这样会出现一个问题,由于没有坐标系统,导致无边框窗体无法拖动和定位(一般是Qt6开始强制默认优先用wayland,之前Qt5是默认有xcb则优先用xcb),你需要在main函数前面加一行 

qputenv("QT_QPA_PLATFORM", "xcb");
Qt Creator

【1】很多人Qt和Qt Creator傻傻分不清楚,经常问Qt什么版本结果发一个Qt Creator的版本过来,Qt Creator是使用Qt编写的集成开发环境IDE,和宇宙第一的Visual Studio一样,他可以是msvc编译器的(windows对应的Qt集成安装环境中自带的Qt Cerator是msvc的),也可以是mingw编译的,还可以是gcc的。如果是自定义控件插件,需要集成到Qt Creator中,必须保证该插件的动态库文件(dll或者so等文件)对应的编译器和Qt版本以及位数和Qt Creator的版本完全一致才行,否则基本不大可能集成进去。特别注意的是Qt集成环境安装包中的Qt版本和Qt Creator版本未必完全一致,必须擦亮眼睛看清楚,有些是完全一致的。由于新版的Qt要求在线安装,而且在线安装选择器中Qt Creator的版本无法选择,新版的Qt Creator用的是Qt6编译的,所以就出现了win7系统不支持的情况,推荐用win10或者win11系统做开发环境。你可以在高版本的Qt Creator中做开发,选择支持win7的套件版本比如5.15或者选择支持xp的套件版本5.6即可,发布后依然可以正常在低版本的系统运行。

Qt6 STL

【1】关于是使用QList还是QVector的问题,一直是众多Qter的选择问题,主要是这两个玩意提供的的接口函数基本一致,比如插入、删除、取值等。

- 大多数情况下可以用QList。像append、prepend、insert这种操作,通常QList比QVector快的多。

- QList是基于index标签存储它的元素项在内存中,比那种依赖iterator迭代的更快捷,而且你的代码也更少。

- 如果你需要一个真正的连接着的list,且需要保证一个固定插入耗时。那就用迭代器,而不是标签。使用QLinkedList()。

- 如果你需要开辟连续的内存空间存储,或者你的元素远比一个指针大,这时你需要避免个别插入操作,出现堆栈溢出,这时候用QVector。

- 如果更在意取值的速度则用QVector,QCustomPlot用的就是QVector,需要频繁大量的取出数据进行绘制。

- 如果更在意更新数据(添加、删除等)的速度则用QList(对应操作是[]=值),但是因为QChart主要用的是QList访问数据(对应操作是at()),也是导致大数据量卡顿的原因之一,一直被诟病。

- 曲线图表这类的基本上绝大部分时间都是在访问数据,拿到设置好的数据进行绘制。

- 总之:QList更新(插入、追加等)数据速度快,QVector取数据速度快。

- 在数据量很小的情况下两者几乎没啥性能区别。

- 貌似Qt6对这两个类合并了(选择困难症的Qter解放了),QVector=QList即QVector是QList的别名,可能底层改了代码以便发挥两者的优势。

Qt6 网络模块

【1】Qt的网络库支持udp广播搜索和组播搜索,其中组播搜索可以跨网段搜索,有时候你会发现失灵,此时你可以尝试把本地的虚拟机的网卡禁用试试,估计就好了。还有就是在本地开启了代理啥的,先关掉试试。近期在使用tcpsocket连接的时候,发现在Qt4和Qt5中正常的程序,到了Qt6中就不行了,报错提示 The proxy type is invalid for this operation ,原来是本地设置了代理导致的,可能在Qt6以前会默认跳过去不处理。

```cpp//也可以通过代码设置跳过代理#include <QNetworkProxy>QNetworkProxyFactory::setUseSystemConfiguration(false);//下面这样每次设置也可以tcpSocket->setProxy(QNetworkProxy::NoProxy);​​//查阅到文章 https://www.cnblogs.com/cppskill/p/11730452.html//从5.8开始socket默认代理类型是DefaultProxy而不是NoProxy,不知道出于什么考虑。```