当前位置: 首页 > news >正文

QML学习笔记(一)基本了解和工程配置

前言:

已经从事QT开发几年了,但对于QML这个东西始终是没有彻底掌握,一方面实际工作中没有用到过,其次它的语法对我来说是全新的东西,不像QWidget那一套可以直接在C++中去写。这就是为什么网上都说qml更简单,我却屡屡学不会的原因。
让我下定决心一定要开始学QML的契机,就是因为最近在找工作,面试官让我在纸上直接写一个按钮,并设置一段文字,结果我犹豫半天,居然没能动笔。我大概知道是写button,然后用花括号括起来,但button是否需要大写,花括号后面有没有分号,里面的属性又是什么来着……我一下子陷进了沮丧。
这其实就是没有真正使用qml写过代码的必然结果,即便对此有基本的认知,但别人考到你的时候,实际上是写不出来的。所以接下来的学习当中,我必须要做好详细的记录和笔记,这也是对自己的要求。(偷偷抱怨一句,在这个时代只会qt,是真的难找到工作啊)
话不多说,

一、QML基本了解

QML是Qt框架的声明式UI开发语言,与传统的Qt Widgets(基于C++的界面库)在语法特性、适用场景和视觉效果上存在显著差异‌。

‌1.语法特性‌: QML采用JSON-like声明式语法,支持数据绑定和热重载。‌ Qt
Widgets使用C++编写,采用命令式编码模式。‌
2.视觉效果‌: QML支持矢量渲染与复杂动画,可自定义渐变色、圆角等效果。‌ Qt Widgets基于原生控件,动画效果有限。‌
3.开发场景‌: QML适合移动端/嵌入式设备的动态UI(占比65%应用场景)。‌ Qt Widgets适合传统桌面软件(如Office类应用)。‌

以上是网上找到的内容,结合我自身的认知,再浅浅谈一下。
1.QML常常伴随Qt Quick这个词语,看上去好像是同一个东西,实则不然。QML是Quick Markup Language,它是一门语言。而Qt Quick是一个模块,可以理解为Qt的一个扩展库,你必须要先引用Qt Quick这个模块,才能用QML语言来写代码。
2.为什么会出现QML这个东西,我猜是为了将界面描述和逻辑代码彻底分开。如果你深入用QWidget来写过代码,你会有个感悟,界面层的代码和业务功能代码是深度耦合在一起的,特别是还有信号槽这种东西。而如果我们将界面相关的东西,全部用单独的文件编写起来,业务代码继续用C++写,是不是就更清晰明了,分工明确呢。所以说,QML其实是专门做界面+加动画+小逻辑的语言,为的是不抢C++业务。
3.说到QML的优势,大家说的基本上可以使用GPU硬件加速和支持动画效果,但这方面我目前还没体会到,后面学习再加上。还有一点是,QML更方便应用在嵌入式小设备和移动端。至于“学习成本低”、“语法更简单”,就见仁见智了。

二、QML工程创建
先说一下,我的QT版本是5.14.2,默认就可以创建Qt Quick工程,如果不行可能是QT安装的时候没有安装对应的模块,这个就得重新装下QT了。
咱们先创建新的工程,选Qt Quick的应用工程,这里有好几个选项,直接选空的即可。后面的步骤和QWidget是一样的,就不啰嗦了。
在这里插入图片描述
创建完工程后,直接编译运行,成功显示出空白界面。(还很贴心给了个Hello World的提示)
在这里插入图片描述

三、工程结构

在看具体的qml代码之前,我们先看一下基本的工程目录结构。
如果是传统的QWidget工程,代码一般放在Headers(头文件)和Sources(源文件)两个目录下标里面,但存放qml代码的qml文件显然不是。你可以直观看到,qml文件默认是放在Resources底下的qrc里面的。而qrc一般存放的是资源文件,最常见的就是各种ui图片。
在这里插入图片描述
也就是说,qml作为一种界面描述文件,本质上是当做一种资源来管理的(不知道说得对不对),他区别于其他正式的C++代码文件,存放在不同的目录底下。
当然,这里所说的目录不是文件实际存放的目录,说得上qt IDE里面显示的分类目录哈。
在这里插入图片描述
咱们打开工程的pro文件,发现它加载的是quick模块。这里对比一下传统QWidget应用,这里夹在的事core和gui模块。
在这里插入图片描述
当然,这并不是二选一的问题,事实上qml是可以和widget混合使用的,比如从一个qwidget界面跳转到qt quick界面,是完全可以的。我想表达的是,如果我们在qwidget工程中,想要新增一个qml工程,需要自己手动在pro中添加quick模块,这样qml才能正常使用哈。

四、C++中打开并显示QML界面

我们直接把main中的代码放上来。咋一看很复杂,没事我们慢慢来。

#include <QGuiApplication>
#include <QQmlApplicationEngine>int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);QQmlApplicationEngine engine;const QUrl url(QStringLiteral("qrc:/main.qml"));QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,&app, [url](QObject *obj, const QUrl &objUrl) {if (!obj && url == objUrl)QCoreApplication::exit(-1);}, Qt::QueuedConnection);engine.load(url);return app.exec();
}

首先是头文件,这里引用的QGuiApplication,它实际上对应的是QApplication,QGuiApplication比QApplication会更加轻量级,更适合纯QML的工程。这里换成QApplication其实也是可以的,但pro要注意把core和gui模块加上。
第二个头文件叫QQmlApplicationEngine
QML 引擎,负责解析+加载+实例化你的 main.qml 文件。
我们看main的代码,第一行是QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);这是开启高分辨率自适应,这和qwidget是一样的,它默认给你添加了。
下一行是QGuiApplication app(argc, argv);这是创建了一个应用对象。
重点是下面的代码。

QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));

engine = QML 解析器 + 加载器 + 对象工厂。
url = 要加载的 QML 文件(qrc 资源系统路径)。

QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,&app, [url](QObject *obj, const QUrl &objUrl) {if (!obj && url == objUrl)QCoreApplication::exit(-1);
}, Qt::QueuedConnection);

这句信号槽实际上是engine引擎加载url后的结果回调,返回参数是obj和obj的url,这里做了个保险丝判断,如果 main.qml 加载失败(文件不存在/语法错误),立即退出程序,防止黑屏。

engine.load(url);
return app.exec();

load = 解析 + 实例化整个 QML 对象树。
exec() = 进入事件循环,直到 QCoreApplication::quit() 或窗口关闭。

最后再说一下我自己的理解。这种写法更多是初始化一个qml引擎,然后加载url上的文件,把qml描述的界面显示出来。实际上我更喜欢另一种方法,用到了qt中的QQuickWidget,它是一种组件,可以直接嵌QWidget中。直接调用QQuickWidget中的setSource似乎是一种更方便的做法,当然以我目前的水平,还说不好谁更好。这种方法我在下一篇中会详细再说。
至此,一个qml界面便显示出来咯。

五、QML最小可运行实例

我们双击main.qml,发现里面有这段代码。

import QtQuick 2.14
import QtQuick.Window 2.14Window {visible: truewidth: 640height: 480title: qsTr("Hello World")
}

类似于C++代码,其实还蛮好理解的。

  1. import QtQuick 2.14
    引入 QtQuick 核心库(对象、动画、绘制等基础类)。

  2. import QtQuick.Window 2.14
    引入 Window 组件(ApplicationWindow 的简化版),提供顶层窗口。

  3. Window { … }
    顶层窗口对象,等价于 QMainWindow 的 QML 版。

  4. visible: true
    立即显示窗口(false 则隐藏)。

  5. width: 640 / height: 480
    窗口初始大小(像素)。

  6. title: qsTr("Hello World")
    窗口标题,qsTr() 是 Qt 的国际化函数,可被翻译系统替换。

这 7 行是 QML 的最小可运行模板: 导入 QtQuick 和 Window,创建 640×480 的顶层窗口,标题‘Hello World’,立即显示。

六、顶层窗口描述

我再补充一点,这里用到的Window事实上并不是固定的,我有见过b站up主用的Rectangle,于是用kimi问了一下:

  • Window = 顶层窗口(有标题栏、系统边框、任务栏图标)。
  • Rectangle = 普通矩形对象(无边框,必须被别的窗口或组件装载)。

1. 顶层 vs 被装载

作用是否能独立显示
Window顶层窗口(系统边框、标题栏)✅ 直接显示
Rectangle矩形+颜色+子控件❌ 必须被 Window/ApplicationWindow 装载

2. 代码对比

顶层窗口(独立显示):

import QtQuick 2.14
import QtQuick.Window 2.14Window {                       ← 顶层窗口visible: truewidth: 640; height: 480Rectangle {                ← 被装载的矩形width: 200; height: 200color: "red"}
}

仅 Rectangle(无边框,需被装载):

import QtQuick 2.14
Rectangle {                    ← 无边框矩形width: 200; height: 200color: "red"
}

后者无法独立显示——必须被 Window/ApplicationWindow 或别的组件装载。


3. 常用顶层类对照

QML 类作用备注
Window最简顶层窗口无边框菜单
ApplicationWindow带菜单栏/工具栏的主窗口QtQuick.Controls 2 推荐
Rectangle普通矩形+子控件被装载对象

这就能解释得通了,因为Rectangle的方式是加载在QQuickWidget控件里面的,不像main.qml中的Window必须是顶层窗口。(我尝试直接将Window改成Rectangle,结果QQmlApplicationEngine加载返回的信号槽没有出错,但界面没有弹出来,不符合我们的预期。)
这一点跟QMainWindow和QWidget的设计是类似的。只是QWidget不仅可以作为子控件,其实还可以用作顶层窗口,如果做了相关设置,甚至可以变成dialog弹窗,不过这个就不细说了。

七、总结

写到这里,我已经尽可能说得很详细,也努力让自己印象深刻一点,毕竟这其实是自学笔记嘛。如果有人真的读到这里,有什么问题也可以在评论区提出。
如果可以的话,接下来我会按照自己的节奏,按照自己的理解,一点点更新这个学习笔记,要是能成为一个教程就更棒了。


文章转载自:

http://8kVQlBEk.rnrwq.cn
http://4x7pCFUT.rnrwq.cn
http://au3jfZer.rnrwq.cn
http://75LlXrc3.rnrwq.cn
http://cuv5VyKf.rnrwq.cn
http://p46Bj3tt.rnrwq.cn
http://D4o1qa8J.rnrwq.cn
http://Sx43OmJj.rnrwq.cn
http://XESgi1TP.rnrwq.cn
http://jQLx8Aeb.rnrwq.cn
http://v3t0y4eb.rnrwq.cn
http://NavOfHaX.rnrwq.cn
http://IKGr28Xo.rnrwq.cn
http://GcaW2Zko.rnrwq.cn
http://NoTLKQfP.rnrwq.cn
http://vRWiOFZs.rnrwq.cn
http://ScFYt3T0.rnrwq.cn
http://CtM1o2lH.rnrwq.cn
http://krcWA5y2.rnrwq.cn
http://l2e8NoGa.rnrwq.cn
http://mqMPWdVG.rnrwq.cn
http://00GJfkZ6.rnrwq.cn
http://rpbFafYH.rnrwq.cn
http://miRafpbB.rnrwq.cn
http://sibkFPLU.rnrwq.cn
http://0MZNZKzB.rnrwq.cn
http://CyLLglNX.rnrwq.cn
http://E9ew3Izh.rnrwq.cn
http://zZFsUAHP.rnrwq.cn
http://5DG2kWT4.rnrwq.cn
http://www.dtcms.com/a/387560.html

相关文章:

  • 大数据毕业设计选题推荐-基于大数据的牛油果数据可视化分析系统-Hadoop-Spark-数据可视化-BigData
  • Hadoop单机模式下运行grep实例,output文件目录不存在
  • 【docker】清理中断构建后产生的镜像和缓存
  • Vue2项目集成打包分析工具webpack-bundle-analyzer
  • 【阶梯波发生器如何控制电压和周期】2022-12-9
  • Java 设计模式之桥接模式(Bridge Pattern)
  • Android 端启动 HTTP 服务:从基础实现到实战应用
  • 《2D横版平台跳跃游戏中角色二段跳失效与碰撞体穿透的耦合性Bug解析》
  • 基于本机知识库 + 豆包(火山引擎)+ MCP的落地方案
  • OpenCV 风格迁移、DNN模块 案例解析及实现
  • php实现火山引擎 【双向流式websocket-V3-支持复刻2.0/混音mix】开箱即用,可用于各种PHP框架。
  • 【lua】Windows环境下cffi-lua使用指南:编译、安装与测试
  • 我优化了昨天的C++/Lua插件系统:添加了插件沙箱、Lua 状态池
  • 【数据库】SQLite安装部署与使用指南
  • Android Kotlin 请求方法代码
  • 【easy_tools】一个跨平台裸机工具库,包含任务/堆栈/消息/定时器/日志等实现
  • ARM(11) - LM75
  • FPGA实现SRIO数据回环传输,基于Serial Rapidlo Gen2架构,提供6套工程源码和技术支持
  • 第十九章 Arm C1-Premium TRBE技术解析
  • HTB writeup
  • 科学研究系统性思维的理论基础:数字化研究工具
  • 基于有限元-元胞自动机法(CAFE)的增材制造过程组织模拟
  • 电视行业复兴,数字化制造如何重塑“视界”新格局?
  • 从兼容到极致性能——qData数据中台商业版核心指标解读
  • MAC-枚举反射工具类
  • 搜索百科(1):Lucene —— 打开现代搜索世界的第一扇门
  • 学习日记-JS+DOM-day57-9.17
  • Java异常处理最佳实践指南
  • Ansible简介
  • pytest使用总结笔记