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

《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——3. QML入门:像搭积木一样构建UI

目录

  • 一、概述
    • 1.1 背景介绍:从后端逻辑到前端界面
    • 1.2 学习目标
  • 二、QML基础入门
    • 2.1 QML语法与核心组件
    • 2.2 响应用户输入:`MouseArea`与`Button`
  • 三、项目UI骨架搭建
  • 四、总结与展望

一、概述

1.1 背景介绍:从后端逻辑到前端界面

在上一篇文章中,我们深入了C++的“后厨房”,掌握了构建程序“大脑”和“骨骼”的核心技能,包括类、对象以及Qt独有的信号与槽通信机制。一个功能强大的后端逻辑已经蓄势待发。然而,对于最终用户而言,他们直接与之交互的是软件的“面孔”——用户界面(User Interface, UI)

本篇文章的核心任务,就是为我们强大的C++后端,打造一个现代化、美观且响应迅速的UI。我们将正式从ScrewDetector项目入手,学习Qt Quick技术的核心——QML语言。QML是一种声明式的语言,它允许开发者像“搭积木”一样快速、直观地构建界面,并将界面的外观与程序的逻辑彻底分离。

1.2 学习目标

通过本篇的学习,读者将能够:

  1. 理解QML的基本语法和核心概念。
  2. 熟练使用最常用的QML组件来构建静态界面。
  3. 掌握QML中的布局技巧,实现响应式的界面设计。
  4. 最终完成ScrewDetector项目主界面的静态布局搭建。

二、QML基础入门

从本章开始,我们将回到在第1篇文章中创建的ScrewDetector项目,并主要在Main.qml文件中进行工作。

2.1 QML语法与核心组件

【核心概念:声明式的UI描述】

与C++这种命令式语言(告诉计算机“如何做”)不同,QML是一种声明式语言,它更侧重于描述“是什么”。在QML中,通过层层嵌套的**组件(Item)来描述界面的结构,并用属性(Property)**来定义每个组件的外观和行为。

【例3-1】 基础组件与属性

我们将从最基础的组件开始,熟悉QML的语法。

1. 打开项目

  • 打开第一章创建的ScrewDetector项目。

2. 编写代码 (Main.qml)

  • 用以下代码替换Main.qml的全部内容:
import QtQuick// Window是所有桌面应用的根组件,代表一个窗口
Window {id: rootWindow // 为根组件指定一个id,方便内部引用width: 640height: 480visible: truetitle: "QML基础组件演示"color: "#2c3e50" // 窗口背景色// Rectangle是一个矩形组件,是构建UI的基本形状Rectangle {id: blueRectwidth: 200  // 宽度height: 100 // 高度color: "#3498db" // 矩形颜色// anchors是一种强大的布局方式,这里让矩形在父组件(窗口)中居中anchors.centerIn: parent// Text组件用于显示文本Text {text: "这是一个蓝色矩形" // 显示的文本内容color: "white"         // 文本颜色font.bold: true        // 字体加粗font.pixelSize: 16     // 字体大小(像素)// 让文本在父组件(蓝色矩形)中居中anchors.centerIn: parent}}
}

3. 运行结果
单击运行按钮,将看到一个深色背景的窗口,中央有一个带有文字的蓝色矩形。
在这里插入图片描述

关键代码分析:
(1) import QtQuick: 类似于C++的#include,用于导入包含基础QML组件的模块。
(2) 组件层级: QML代码通过大括号{}形成层级结构。Text组件被定义在Rectangle组件内部,意味着TextRectangle子组件
(3) id属性: id是一个特殊的属性,它为组件提供一个在当前QML文件内唯一的名称,方便其他组件引用它。例如,anchors.centerIn: parent中的parent就隐式地引用了其父组件的id
(4) 属性绑定: width: 200这种 属性名: 值 的语法称为属性赋值。QML更强大的地方在于属性绑定,如果一个属性的值依赖于另一个属性,当后者改变时,前者会自动更新。我们将在后续章节深入了解。
(5) anchors布局: anchors(锚)是一种相对布局系统。anchors.centerIn: parent表示将当前组件的中心点与父组件的中心点对齐。

2.2 响应用户输入:MouseAreaButton

一个静态的界面是不够的,UI需要能够响应用户的操作。

【例3-2】 添加交互功能。

1. 编写代码 (Main.qml)

  • 在上一个示例的基础上,为蓝色矩形添加一个MouseArea,并额外添加一个Button组件。
import QtQuick
import QtQuick.Controls // Button组件需要导入此模块Window {id: rootWindow// ... (属性保持不变)Rectangle {id: blueRect// ... (属性保持不变)Text {id: infoText // 给Text组件一个idtext: "点击我!"// ... (其他属性保持不变)}// MouseArea是一个不可见的组件,专门用于处理鼠标事件MouseArea {anchors.fill: parent // 让MouseArea完全覆盖父组件(蓝色矩形)// onClicked是一个信号处理器,当鼠标点击时,这里的代码会被执行onClicked: {// 修改蓝色矩形的颜色blueRect.color = (blueRect.color == "#3498db" ? "#e74c3c" : "#3498db");// 修改文本内容infoText.text = "颜色改变了!";}}}// Button是Qt Quick Controls提供的预制按钮组件Button {id: resetButtontext: "重置"width: 100// 将按钮锚定在窗口底部,水平居中anchors.bottom: parent.bottomanchors.horizontalCenter: parent.horizontalCenteranchors.bottomMargin: 20// 按钮的点击事件处理器onClicked: {blueRect.color = "#3498db";infoText.text = "点击我!";}}
}

2. 运行结果
现在,窗口下方出现了一个“重置”按钮。

  • 单击蓝色矩形,它的颜色会在蓝色和红色之间切换,并且文本会改变。
  • 单击“重置”按钮,矩形会恢复到初始的蓝色和文本。
    在这里插入图片描述在这里插入图片描述
    关键代码分析:
    (1) import QtQuick.Controls: 像Button, Slider, Frame等更高级的、带样式的控件,都位于此模块中,需要单独导入。
    (2) MouseArea: 这是一个非常核心的组件,用于为任何非交互式组件(如Rectangle, Image)添加鼠标响应能力。它本身是透明不可见的。
    (3) 信号处理器: onClicked就是一个信号处理器。它的命名规则是 on + 信号名(首字母大写)。当MouseAreaButton发出clicked信号时,对应的onClicked块内的JavaScript代码就会被执行。
    (4) JavaScript代码: 在信号处理器中,可以编写简单的JavaScript代码来改变其他组件的属性,实现动态交互。这里的 (条件 ? 值1 : 值2) 是一个三元运算符,是if-else的简洁写法。

三、项目UI骨架搭建

现在,我们具备了搭建静态界面的基础知识。是时候开始构建ScrewDetector项目的主界面了。

根据项目需求,主界面可以划分为三个区域:

  1. 图像显示区:占据大部分空间,用于显示一个实时视频画面。
  2. 结果展示区:位于视频下方,用于显示检测结果的列表。
  3. 控制区:位于最下方,包含“开始检测”、“停止”等操作按钮。

我们将使用ColumnLayout(垂直布局)和RowLayout(水平布局)来组织这些区域。布局组件可以自动管理其子组件的位置和大小,非常适合创建响应式界面。

【例3-3】 ScrewDetector主界面静态布局。

1. 编写代码 (Main.qml)

  • 用以下代码再次替换Main.qml的全部内容。
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts // 布局组件需要导入此模块Window {id: rootWindowwidth: 960height: 720visible: truetitle: qsTr("AI螺丝瑕疵检测系统 V1.0")color: "#1e2a38"// 使用一个ColumnLayout作为根布局,使其内部组件垂直排列ColumnLayout {anchors.fill: parent // 填充整个窗口anchors.margins: 10  // 设置边距spacing: 10          // 设置子组件之间的间距// --- 1. 图像显示区 ---// 使用Frame组件来创建一个带边框和背景的容器Frame {id: videoFrameLayout.fillWidth: true  // 宽度填充父布局Layout.fillHeight: true // 高度填充父布局,权重为1(默认)padding: 0background: Rectangle {color: "#101a24" // 一个更深的背景色}// 这里暂时用一个文本来占位,后续将替换为真实的视频画面Text {text: "实时视频显示区"color: "#555"font.pixelSize: 24anchors.centerIn: parent}}// --- 2. 结果展示区 ---Frame {id: resultFrameLayout.fillWidth: trueLayout.preferredHeight: 150 // 固定高度background: Rectangle {color: "#2c3e50"}Text {text: "检测结果列表区"color: "#7f8c8d"anchors.centerIn: parent}}// --- 3. 控制区 ---// 使用RowLayout使内部按钮水平排列RowLayout {id: controlBarLayout.fillWidth: trueLayout.preferredHeight: 50Layout.alignment: Qt.AlignHCenter // 整体居中对齐spacing: 20Button {id: startButtontext: "开始检测"Layout.preferredWidth: 120Layout.preferredHeight: 40}Button {id: stopButtontext: "停止"Layout.preferredWidth: 120Layout.preferredHeight: 40}}}
}

2. 运行结果
一个结构清晰、布局合理的界面框架就完成了。无论如何拖动改变窗口大小,各个区域都会按比例自动调整,保持美观。
在这里插入图片描述
关键代码分析:
(1) Layout附加属性: 当一个组件被放置在布局(如ColumnLayout)内部时,可以使用Layout.前缀的附加属性来控制其在布局中的行为。Layout.fillWidth: true表示让组件的宽度自动填满布局的可用宽度。Layout.preferredHeight则指定了一个期望的高度。
(2) Frame: 这是一个带背景和可选边框的容器,非常适合用于对UI元素进行分组和区域划分。
(3) 布局的组合: 通过ColumnLayoutRowLayout的嵌套组合,可以构建出几乎任何复杂的界面布局,并且代码结构非常清晰。

四、总结与展望

在本篇文章中,我们正式踏入了QML的世界。通过学习基础组件交互处理布局系统,我们成功地为ScrewDetector项目搭建了一个专业、响应迅速的UI骨架。

至此,我们已经分别掌握了C++后端逻辑和QML前端界面的基础。但目前,它们仍然是两个独立的世界。如何将它们连接起来,让QML的按钮能够触发C++的复杂操作,让C++的计算结果能够显示在QML的界面上?这将是我们下一篇文章的核心主题。

在下一篇文章**【《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——4. 前后端联动:打通QML与C++的任督二脉】**中,我们将探索MVVM架构思想,并实践连接前后端的关键技术。

http://www.lryc.cn/news/597147.html

相关文章:

  • ESP32-S3学习笔记<4>:I2C的应用
  • DeepSeek 助力 Vue3 开发:打造丝滑的日历(Calendar),日历_家庭维护示例(CalendarView01_31)
  • WebGIS 中常用空间数据格式
  • 2025暑期—06神经网络-常见网络3
  • 2025暑期—06神经网络-常见网络2
  • 2026 拼多多秋招内推码(提前批)
  • 为什么设置 git commit签名是公钥而不是私钥?
  • yo easy-ui5生成项目,ui5版本降级处理
  • Tang Prime 20K板I2S输入输出例程
  • Hexo - 免费搭建个人博客01 - 安装软件工具
  • Java应用程序内存占用分析
  • 大致自定义文件I/O库函数的实现详解(了解即可)
  • 软件开发、项目开发基本步骤
  • Java从入门到精通!第十二天(泛型)
  • 搭建 Android 开发环境JAVA+AS
  • 阿里云ODPS十五周年重磅升级发布:为AI而生的数据平台
  • HTTP性能优化终极指南:从协议原理到企业级实践
  • k8s pvc是否可绑定在多个pod上
  • 【Kubernetes】集群启动nginx,观察端口映射,work节点使用kubectl配置
  • 优化 Elasticsearch JVM 参数配置指南
  • 每日一算:华为-批萨分配问题
  • 谷粒商城篇章13--P340-P360--k8s/KubeSphere【高可用集群篇一】
  • 常用的正则表达式
  • 代码随想录算法训练营第五十二天|图论part3
  • 图论的题目整合(Dijkstra)
  • 【图论,拓扑排序】P1347 排序
  • 算法竞赛备赛——【图论】最小生成树
  • Modbus协议详解与c#应用
  • 算法竞赛备赛——【图论】拓扑排序
  • CI/CD与DevOps集成方法