一文入门Qt Quick

以下内容为本人的著作,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/dvamU6q5lZQb5hztfD2zNg

初识Qt Quick

很高兴可以来到这一章,终于可以开始讲讲最近几年Qt的热门技术Quick这一块了。

啥是Qt?

哦,这是一个宣称可以跨任意平台,开发各种场景应用软件的开发框架。从三个维度来讲,就是开发库framework,集成开发平台IDE,以及成熟的开发思维模式。

Qt Quick最早出现在Qt的4.7版本中,目标是在UI设计者与开发者之间搭建一个更高效合作平台,给开发者更好的UI开发体验。虽然几经易手,Qt在digia公司这些年的努力迭代更新下,Qt Quick终于迎来了成熟稳定的版本(这也是我愿意在最近的项目里转用它的原因)。

至于Qt Quick和老一套开发核心Qwidget的区别,其中最重点的就是提供了新的UI描述语言QML(Qt Meta-object Language,Qt元对象描述语言)。QML乍看起来有点像json,但是核心思想却是模仿web页面。没错,在QML文件中允许搭配Javascript代码,就可以辅助实现丰富的UI交互逻辑。

如果你以往习惯QWidget开发,那么Qt Quick真的非常值得上手试试。

好了,口水吐多了招人厌,下面直入庐山一窥真面目!

手剥一个简单的功能程序开发栗子

在Qt开发过程中,Qt官方IDE(Qt Creator)提供了好几种工程构建工具,比如简单易懂的qmake,火上天的cmake,还有貌似没人听说过的Qbs。而目前Qt主推的构建方式就是cmake,下面要讲的例子也是用cmake。

1.开发环境配置

Win10
Qt 6.2.4
Qt Creator 8.0.1
Mingw 11.2.0 64bit
Cmake 3.23.2

这里选用的Qt版本是写作时最新的LTS版本,LTS意思就是官方长期支持更新。比如说,一两年内还会发布一下补丁和安全更新,至于新功能特性就别想了。

2.创建Qt Quick工程

先用Qt Creator创建一个简单的quick工程,工程构建描述的内容就保存在工程根目录的配置文件CMakeLists.txt中,如下:

cmake_minimum_required(VERSION 3.16)project(instance VERSION 0.1 LANGUAGES CXX)set(CMAKE_AUTOMOC ON)
#set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)find_package(Qt6 6.2 COMPONENTS Quick REQUIRED)file(GLOB_RECURSE SOURCE_FILES./src/*.cpp./src/*.h
)qt_add_resources(SOURCE_FILES instance.qrc)qt_add_executable(instance${SOURCE_FILES}
)set_target_properties(instance PROPERTIESMACOSX_BUNDLE_GUI_IDENTIFIER my.example.comMACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}MACOSX_BUNDLE TRUEWIN32_EXECUTABLE TRUE
)target_link_libraries(instancePRIVATE Qt6::Quick)install(TARGETS instanceBUNDLE DESTINATION .LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})

cmake_minimum_required用于声明当前的配置文件适用于的cmake最低版本,同时为了防止使用过于低级的版本来构建当前工程,避免某些指令不支持或者不兼容。

project用于声明当前工程名称,和开发语言。CXX代表了C++。

CMAKE_AUTOMOC用于标记是否开启自动MOC。Qt不仅仅是开发库,它同时也对开发语言(比如C++)做了拓展,那么源码文件中就会多多少少会包含有通用编译器无法识别的部分内容。MOC就是用于对Qt的扩展内容进行转换的工具。

CMAKE_AUTORCC用于标记是否开启自动RCC。在Qt工程中会包含有被最终输出的执行程序所需要的资源内容,比如音视频,图片等等。那么为了高效调用这些资源,势必需要对原本的资源文件进行处理再保存到额外的二进制文件中,甚至内嵌到执行文件中。RCC就是对这些资源文件进行处理和再输出工具。

由于我的例子工程中需要用到MOC和RCC,所以CMAKE_AUTOMOCCMAKE_AUTORCC都打开。

CMAKE_AUTOUIC用于标记是否开启自动UIC。如果开发界面用的技术栈是QWidget,那么在Qt工程中就需要创建.ui文件并保存设计内容到其中,编译的时候也需要用UIC把.ui文件转换成.h文件。不过,这里用Quick技术栈开发界面,因此无需打开CMAKE_AUTOUIC,在最前面添加#表示注释掉该行语句(该行语句会被解析器忽略)。

find_package导入Qt的Quick模块。

由于本工程需要用到多个C++源文件,所以这里采用了递归引用文件的方式把特定文件夹下面的所有.cpp、.h等文件都囊括进来,需要辅以通配符*。所有被囊括的文件路径被追加到动态数组SOURCE_FILES中,方便后边引用。语法格式如下:

file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS][LIST_DIRECTORIES true|false] [RELATIVE <path>][<globbing-expressions>...])

格式里的variable实际使用SOURCE_FILES代替。

qt_add_resources的作用是调用RCC对资源文件(.qrc)编译成qrc_开头的源文件再输出,并且把输出的源码文件路径追加到动态数组SOURCE_FILES中。

当然,动态数组SOURCE_FILES这个名字可以按照需求自定义设定,这里取名为源文件。

qt_add_executable指明构建的目标是二进制文件instance,引用的源文件来自于动态数组SOURCE_FILES。

target_link_libraries用于指明构建时链接Qt6::Quick的相关库。

剩余的语句都是Qt Creator创建工程时自动添加的内容,这里略过。

然后看看我的工程目录结构在Qt Creator中的展示:

如果用VSCODE打开工程目录,可以看到:

3.使用元对象描述文件(QML)描述界面

使用Qt Creator自动创建的Quick应用,除了会自动生成配置文件CMakeLists.txt之外,还包含了main.cpp和main.qml文件,源码只实现了启动之后弹出一个窗口。

这里稍作修改,实现简单的文件选择,以及将选中的文件路径名显示出来。这个功能用QWidget技术栈来实现其实是很简单的啦,不过我们这里目的是演示Quick技术框架怎么用,所以下面来具体看看界面这块怎么玩:

1)主页面

// main.qml

import QtQuickWindow {width: 640height: 200visible: truetitle: qsTr("Tool V1.0.0")Viewer {anchors.fill: parent}
}

main.qml 这个文件是首页元对象的描述文件,一般QML引擎加载的第一个元对象所在的文件就命名为main.qml。不过,命名为main.qml不是硬性规定。

首先,可以看到这里通过import导入了模块QtQuick。同一行,后边还可以加上版本号。

Window是一种模块里预定义的类型,用于窗体描述。当然,类型也可以自定义,下面会说到。这里通过对类型Window的实例化来描述一个窗体对象。

类型后边的{}内部包含了类型实例化后的成员属性、函数、信号、信号处理句柄等,比如width、height、visible、title等都是预定义的属性,这些属性如字面意思比较简单易懂就不一一展开了,大伙要是有兴趣可以反馈给我,我再看看意见给大家细聊。

Viewer其实是自定义的类型,这里通过对类型Viewer的实例化来补充添加新的界面元素。Viewer对象内部的属性anchors.fill: parent描述的是对象在其父对象(Window)中把父对象填充满。QML一般通过anchors属性来锚定对象的位置。

上面这个对象里并没有定义或者引用到函数或者信号等。

2)自定义类型

下面来看看怎么自定义类型

// Viewer.qml

import QtQuick 2.15
import QtQuick.Controls 6.2
import Qt.labs.platform 1.1Item {function log(...msg) {let msgs = "";msg.forEach((item) => {if (msgs.length != 0) {msgs += " ";}msgs += item;});console.log(msgs);}FileDialog {id: fileDialogobjectName: "fileDialog"currentFile: selectedFileTextArea.textonFileChanged: {log(objectName + ".file =", file.toString().slice(8));fileMgrInstance.run(file.toString().slice(8));}}Label {id: fileLablex: 292y: 26text: qsTr("文件:")verticalAlignment: Text.AlignVCenterfont.pointSize: 14}TextField {id: selectedFileTextAreax: 70y: 70width: 500objectName: "selectedFileTextArea"text: fileDialog.file.toString().slice(8)font.pointSize: 12placeholderText: qsTr("选择文件")}Button {id: selectFileButtonx: 268y: 124width: 105height: 54text: qsTr("选择")font.pointSize: 10onClicked: {log(text, "clicked");fileDialog.open();}}
}

Item是类库QtQuick的预定义组件类型,描述的是一个基础可视组件,quick中所有的可视组件都继承于它。

一般在QML中自定义类型都会使用基础的类型Item,然后在其基础上定制内部属性、函数、信号、信号处理句柄等。

Item的继承链是这样的:

Item -> QtObject -> QObject

看到这里,可以猜测一下,其实所有的Quick预定义组件都是继承于QObject,和QWidget里提供的类库太相似了。

function log(...msg)定义了函数log,function是关键词,log是函数名,后边小括号里的...表示参数不定,这样子在调用log时就可以不限制输入的参数个数了。

要注意的是,QML内部的函数使用的语法是ECMAScript,也就是我们常常听到的JavaScript。

FileDialog是类库Qt.labs.platform的预定义组件类型,描述的是一个文件选择窗口。属性id,继承于QObject,用于标记唯一的对象,也就是说所有对象的id都不能重复,无论对象是否处于同一个QML文件。objectName描述的属性可用于对对象树中的对象进行查找。currentFile描述了当前选中的文件名(包括路径),在确定最后选中的文件之前,此属性也会跟随选择而改变。onFileChanged描述了当属性file值改变时,自动产生的信号的处理句柄(handle),用{}限定处理范围。log是上面定义的函数调用,输入两个参数。fileMgrInstance是C++源文件暴露给QML引擎的特定对象id,通过该id可以调用C++中的相应对象的方法属性(代码中调用了run方法,方法的详情定义看下文)。

C++和QML源文件之间的对象相互调用,会有后续的文章专门介绍,这里不再细聊,敬请关注。

Label是类库QtQuick.Controls的预定义组件类型,描述的是一个文本标签。x、y描述的是坐标。text描述的是显示文本。qsTr()用于标识文本可被翻译,类似Qt C++里的tr()。verticalAlignment描述的是垂直排列方式,这里的属性值标识垂直中间排列。font.pointSize描述字体的大小。

TextField是类库QtQuick.Controls的预定义组件类型,描述的是一个单行文本编辑窗。width是几何宽度。placeholderText描述的是占位符。

Button是类库QtQuick.Controls的预定义组件类型,描述的是一个可点击的按键。height描述的是几何高度。onClicked描述了当信号clicked发生时,该信号的处理句柄(handle),用{}限定处理范围,这里调用了上面的文件选择窗的打开函数。

信号的处理句柄(handle)中,在on后边书写时信号的首字母需要大写。

3)预览界面

什么?代码没写完就可以预览界面了?

是的,QML文件支持用工具预览,非常方便于UI设计过程中的调试。

打开预览的方式是调用qmlscene或者用Qt Design Studio,如下图用的是qmlscene。

看看Viewer.qml页面预览的实际效果。

4.使用C++代码实现逻辑处理

Qt Quick使用QML的目的是为了简化界面的设计开发,而软件除了界面的互动之外还有大量的后台逻辑处理功能也需要实现,针对这块业务,Qt其实还是推荐使用C++,正所谓术业有专攻,毕竟C++对性能的利用还是有两把刷子的。废话不多说,马上看下文。。。

既然如此,那么就来看看负责逻辑处理功能的C++代码部分。不过,这里假设各位看官已经熟悉C++的各项业务技能,所以下面只针对和QML对象的互动来简单介绍一下。

后续也会有更加详细的专题文章介绍这部分,敬请留意哈!

1)QML对象的加载和C++对象传递

QML对象的创建和展现是通过QML引擎来加载的,一般每个程序会由单个引擎对象负责管理。不过,QML引擎对象不是直接管理QML对象,而是通过管理上下文(context)对象来分别管理QML对象。所以在C++里边如果需要往QML对象传递信息也是直接传给对应的上下文对象,然后再在QML对象中通过传入对象时指定的id名调用对应的方法属性。

// main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "./FileMgr/FileMgr.h"int main(int argc, char *argv[])
{QGuiApplication app(argc, argv);QQmlApplicationEngine engine;const QUrl url(u"qrc:/src/QML/main.qml"_qs);QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,&app, [url](QObject *obj, const QUrl &objUrl) {qInfo("%s start\n", QCoreApplication::applicationName().toLatin1().data());if (!obj && url == objUrl)QCoreApplication::exit(-1);}, Qt::QueuedConnection);FileMgr fileMgr;engine.rootContext()->setContextProperty(QStringLiteral("fileMgrInstance"), &fileMgr);engine.load(url);return app.exec();
}

上面的main.cpp文件代码中,把对象fileMgr传入引擎的根上下文中,并设定id为fileMgrInstance。传入根上下文,意味着引擎加载的所有QML对象都可以通过id=fileMgrInstance访问fileMgr对象内容。这里要注意,fileMgr对象是在C++源码中实例化了的。

把C++对象暴露给QML对象的方法,除了上面这种通过上下文的方式外,还有一种是通过直接往元对象系统(Meta-Object System)注册类型的方式,这种方式也是最根本的方式,因为Qt Quick框架的底层实现原理就依赖于元对象系统。

使用的接口原型:

template <typename T> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)

如果通过这种注册的方式实现,上面的栗子可以掰成这样:

 qmlRegisterType<FileMgr>("com.englyf.qmlcomponents", 1, 0, "FileMgrItem");

根据上面的定义,com.englyf.qmlcomponents是命名空间,1.0是命名空间的版本号,FileMgrItem是QML中的类型名。

然后在QML文件内,导入并实例化这个类:

 import com.englyf.qmlcomponents 1.0FileMgrItem {// ...}

要强调的是,通过注册的方式,类型的实例化会放在QML里边做,而C++源码就不需要再对类FileMgr作实例化了

2)C++类型的定义

既然Qt Quick依赖于元对象系统,那么对C++类型的定义就有必然的要求了。

C++类型需要继承于QObject,并且类开头应该声明宏Q_OBJECT,这样才可以使用元对象系统提供的服务,包括信号槽机制等等。

需要被QML对象调用的方法应该添加修饰Q_INVOKABLE。这个修饰符表明该方法可被元对象系统调用。同时,该方法的参数类型和返回类型,都推荐使用类型QVariant。

如有开放给QML对象的可访问属性,那么也需要对属性声明为Q_PROPERTY。这里暂不举例,可关注后续的专题文章。

// FileMgr.h

#ifndef FILEMGR_H
#define FILEMGR_H#include <QObject>
#include <QVariant>class FileMgr : public QObject
{Q_OBJECT
public:explicit FileMgr(QObject *parent = nullptr);Q_INVOKABLE QVariant run(QVariant file);
};#endif // FILEMGR_H

// FileMgr.cpp

#include "FileMgr.h"FileMgr::FileMgr(QObject *parent): QObject{parent}
{}QVariant FileMgr::run(QVariant file)
{QString fileStr = file.toString();qDebug("C++ get file:%s selected", fileStr.toStdString().data());return 0;
}

自动化部署

这部分讲点高级的内容,以往看到网上的教程都是教初学者部署的时候,进入exe生成的目录,然后手动调用windeployqt执行部署。这个程序是Qt自带的,会自动把所有依赖的动态库拷贝过来存放在指定目录下。

这里就介绍一下怎么在Qt Quick软件工程编译结束时自动部署所有依赖项。

首先,debug开发模式下是不需要部署软件的,那么我们就先切换到release模式下。

然后,在Build的步骤下,Build步骤之后新添加一个Custom Process Step的步骤。

我把配置都拷过来:

Command:            windeployqt
Arguments:          --qmldir %{ActiveProject:NativePath}\src\QML\ %{ActiveProject:RunConfig:Executable:NativeFilePath}
Working directory:  %{Qt:QT_INSTALL_BINS}

由于Qt Quick工程涉及到QML文件,所以这里需要带上选项--qmldir,这个选项后边紧跟着参数值是代码工程中存放自定义的QML文档的根目录。

%{ActiveProject:NativePath}代表着当前工程的主目录的本地化路径。

%{ActiveProject:RunConfig:Executable:NativeFilePath}代表着当前工程的exe文件输出目录的本地化路径。

Working directory项意思是Command命令的工作目录,这里填上%{Qt:QT_INSTALL_BINS},代表Qt安装目录下的bin目录。

按照上面的介绍过程配置完整,以后如果需要部署输出,只需要切换到release模式下,然后点击编译,等编译完成就会自动进入部署流程,整个过程就是这么舒心。

生活简单才是美好,部署也不例外!


到最后,一起来看看跑起来的程序:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/305891.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云30个公共云地域、89个可用区、5个金融云和政务云地域

阿里云基础设施目前已面向全球四大洲&#xff0c;公共云地域开服运营30个公共云地域、89个可用区&#xff0c;此外还拥有5个金融云、政务云地域&#xff0c;并且致力于持续的新地域规划和建设&#xff0c;从而更好的满足用户多样化的业务和场景需求。伴随着基础设施的加速投入和…

AWS SSM中切换AWS不同的profile

问题 在自己的开发笔记本上面&#xff0c;通过AWS SSM方式访问EC2服务&#xff0c;只需要通过简单的命令就可以访问EC2了&#xff0c;如下&#xff1a; aws ssm start-session --target i-xxxx12350这个命令就是利用aws命令行工具中ssm提供的会话管理能力访问ec2服务&#xf…

日志框架简介-Slf4j+Logback入门实践 | 京东云技术团队

前言 随着互联网和大数据的迅猛发展&#xff0c;分布式日志系统和日志分析系统已广泛应用&#xff0c;几乎所有应用程序都使用各种日志框架记录程序运行信息。因此&#xff0c;作为工程师&#xff0c;了解主流的日志记录框架非常重要。虽然应用程序的运行结果不受日志的有无影…

【UE5.1】程序化生成Nanite植被

目录 效果 步骤 一、下载Gaea软件和树林资产 二、使用Gaea生成贴图 三、 生成地形 四、生成草地 五、生成树林 六、生成湖泊 七、其它功能介绍 7.1 调整树林生成的面积 7.2 让植物随风飘动 7.3 玩家和植物互动 7.4 雪中树林 7.5 环境音效 效果 步骤 一、下载Ga…

18-网络安全框架及模型-信息系统安全保障模型

信息系统安全保障模型 1 基本概念 信息系统安全保障是针对信息系统在运行环境中所面临的各种风险&#xff0c;制定信息系统安全保障策略&#xff0c;设计并实现信息系统安全保障架构或模型&#xff0c;采取工程、技术、管理等安全保障要素&#xff0c;将风险减少至预定可接受的…

如何使用群晖Webdav将Obsidian笔记软件远程同步到公网访问

文章目录 1. 群晖开启Webdav服务2. 群晖安装Cpolar3. 配置Webdav远程地址4. Obsidian 安装Remotely Save5. Obsidian远程连接Webdav6. 固定Cpolar公网地址7. PC和移动端笔记同步演示 Obsidian是一款笔记软件&#xff0c;它基于Markdown&#xff0c;支持Windows、macOS、iOS和An…

自然语言处理(第16课 机器翻译4、5/5)

一、学习目标 1.学习各种粒度的系统融合方法 2.学习两类译文评估标准 3.学习语音翻译和文本翻译的不同 4.学习语音翻译实现方法 二、系统融合 以一个最简单的例子来说明系统融合&#xff0c;就是相当于用多个翻译引擎得到不同的翻译结果&#xff0c;然后选择其中最好的作为…

大创项目推荐 深度学习YOLO抽烟行为检测 - python opencv

文章目录 1 前言1 课题背景2 实现效果3 Yolov5算法3.1 简介3.2 相关技术 4 数据集处理及实验5 部分核心代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习YOLO抽烟行为检测 该项目较为新颖&#xff0c;适合作为竞赛课…

华为鸿蒙应用--登录页:网络请求、自定义Loading、MD5密码加密、emitter订阅状态变化、持久化登录状态、隐藏软键盘-ArkTs

HarmonyOS系列 华为鸿蒙应用--底部导航栏Tabs&#xff08;自适应手机和平板&#xff09;-ArkTs_华为鸿蒙应用 csdn 底部导航栏-CSDN博客 华为鸿蒙应用--欢迎页SplashPage倒计时跳过&#xff08;自适应手机和平板&#xff09;-ArkTs_app.media.ic_splash_page_background-CSDN…

喜讯,思迈特签约南方电网搭建云景数字化运营管控平台

近日&#xff0c;思迈特软件签约南方电网共同搭建云景数字化运营管控平台。 Smartbi将助力云景平台构建“全域协同&#xff0c;全员参与、全员创新”的数字化运营新生态。该平台以“工具数据”赋能基层&#xff0c;充分释放基层“业务人员数字化人员”专业能力&#xff0c;实现…

电子设计从零开始(2)-----走进电子技术之电阻器

同学们大家好&#xff0c;今天我们继续学习杨欣的《电子设计从零开始》&#xff0c;这本书从基本原理出发&#xff0c;知识点遍及无线电通讯、仪器设计、三极管电路、集成电路、传感器、数字电路基础、单片机及应用实例&#xff0c;可以说是全面系统地介绍了电子设计所需的知识…

NFC刷卡soc芯片SI3262集成刷卡+触摸+ACD超低功耗一体

简介 13.56mhz刷卡soc芯片SI3262集成刷卡触摸ACD超低功耗&#xff0c;ACD模式刷卡距离可达到5cm以上&#xff0c;非常适用于小体积门锁&#xff0c;密码锁&#xff0c;柜锁&#xff0c;接下来介绍一下这款芯片的具体功能。 优势 1.超低功耗&#xff0c;最低功耗达 1.7uA&…