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

发布于:2025-07-24 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、概述

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: 640
    height: 480
    visible: true
    title: "QML基础组件演示"
    color: "#2c3e50" // 窗口背景色

    // Rectangle是一个矩形组件,是构建UI的基本形状
    Rectangle {
        id: blueRect

        width: 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组件一个id
            text: "点击我!"
            // ... (其他属性保持不变)
        }

        // MouseArea是一个不可见的组件,专门用于处理鼠标事件
        MouseArea {
            anchors.fill: parent // 让MouseArea完全覆盖父组件(蓝色矩形)

            // onClicked是一个信号处理器,当鼠标点击时,这里的代码会被执行
            onClicked: {
                // 修改蓝色矩形的颜色
                blueRect.color = (blueRect.color == "#3498db" ? "#e74c3c" : "#3498db");
                // 修改文本内容
                infoText.text = "颜色改变了!";
            }
        }
    }

    // Button是Qt Quick Controls提供的预制按钮组件
    Button {
        id: resetButton
        text: "重置"
        width: 100
        // 将按钮锚定在窗口底部,水平居中
        anchors.bottom: parent.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.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: rootWindow
    width: 960
    height: 720
    visible: true
    title: qsTr("AI螺丝瑕疵检测系统 V1.0")
    color: "#1e2a38"

    // 使用一个ColumnLayout作为根布局,使其内部组件垂直排列
    ColumnLayout {
        anchors.fill: parent // 填充整个窗口
        anchors.margins: 10  // 设置边距
        spacing: 10          // 设置子组件之间的间距

        // --- 1. 图像显示区 ---
        // 使用Frame组件来创建一个带边框和背景的容器
        Frame {
            id: videoFrame
            Layout.fillWidth: true  // 宽度填充父布局
            Layout.fillHeight: true // 高度填充父布局,权重为1(默认)
            padding: 0
            background: Rectangle {
                color: "#101a24" // 一个更深的背景色
            }

            // 这里暂时用一个文本来占位,后续将替换为真实的视频画面
            Text {
                text: "实时视频显示区"
                color: "#555"
                font.pixelSize: 24
                anchors.centerIn: parent
            }
        }

        // --- 2. 结果展示区 ---
        Frame {
            id: resultFrame
            Layout.fillWidth: true
            Layout.preferredHeight: 150 // 固定高度
            background: Rectangle {
                color: "#2c3e50"
            }

            Text {
                text: "检测结果列表区"
                color: "#7f8c8d"
                anchors.centerIn: parent
            }
        }

        // --- 3. 控制区 ---
        // 使用RowLayout使内部按钮水平排列
        RowLayout {
            id: controlBar
            Layout.fillWidth: true
            Layout.preferredHeight: 50
            Layout.alignment: Qt.AlignHCenter // 整体居中对齐
            spacing: 20

            Button {
                id: startButton
                text: "开始检测"
                Layout.preferredWidth: 120
                Layout.preferredHeight: 40
            }

            Button {
                id: stopButton
                text: "停止"
                Layout.preferredWidth: 120
                Layout.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架构思想,并实践连接前后端的关键技术。


网站公告

今日签到

点亮在社区的每一天
去签到