3、QML基础入门:语法、绑定、信号与常用组件

好,咱们进入QML的世界。说实话,很多做QGC插件开发的朋友,一开始都被QML的写法搞懵过——它跟C++长得完全不一样,但又混在一起用。别急,我带你捋一遍核心的东西,够用就行。

3.1 QML语法:到底长什么样?

QML本质上是一种声明式语言。什么意思呢?就是你描述界面长什么样,而不是一步步告诉计算机怎么画。有点像写HTML,但比HTML灵活得多。

核心结构:每个QML文件就是一个组件,以 .qml 结尾。根元素通常是一个 Rectangle 或 Item,里面嵌套子元素。

// MyButton.qml
import QtQuick 2.15

Rectangle {
    width: 200
    height: 50
    color: "lightblue"

    Text {
        anchors.centerIn: parent
        text: "点击我"
        font.pixelSize: 18
    }
}

你看,结构很清晰:属性名: 值 的写法。我刚开始写的时候,老忘记冒号后面要加空格,结果Qt Creator直接报错——嗯,QML对格式其实挺敏感的。

3.2 属性绑定:QML的灵魂

属性绑定是QML最强大的特性之一。说白了,就是一个属性的值自动跟随另一个属性变化。不需要写什么更新函数,QML引擎帮你盯着。

Rectangle {
    width: 200
    height: 100
    color: "red"

    Rectangle {
        width: parent.width * 0.5   // 绑定:永远是一半宽度
        height: parent.height
        color: "blue"
    }
}

这里 parent.width * 0.5 就是一个绑定表达式。只要父矩形的宽度变了,子矩形的宽度自动更新。我在做QGC地图控件时,就用这个特性让侧边栏随窗口大小自适应——省了不少事。

小技巧:绑定表达式里可以用JavaScript。比如 text: "总数: " + count,或者更复杂的条件判断 color: mouseArea.containsMouse ? "green" : "gray"。

3.3 信号与槽:让组件动起来

QML里的信号与槽比C++简单得多。你不需要声明什么 Q_OBJECT 宏,直接写 onXxx 就行。

Button {
    text: "起飞"
    onClicked: {
        console.log("无人机起飞!")
        statusText.text = "飞行中..."
    }
}

为什么会这样?因为QML为每个信号自动生成了 on+信号名 的处理器。你只需要实现它。我见过不少新手在QML里试图用C++的 connect() 来连接信号,其实完全没必要——QML里直接写 onClicked 就搞定了。

注意:如果你在QML里定义自定义信号,记得用 signal 关键字。比如 signal mySignal(string msg),然后其他地方用 onMySignal: { ... } 来响应。

3.4 常用组件:Rectangle、Text、Button、Image

这几个组件是QGC界面里的常客。我列个表,你一看就明白各自的分工。

组件 用途 常用属性 QGC中的典型场景
Rectangle 矩形区域,背景容器 color, border, radius, gradient 面板背景、分隔线、状态指示块
Text 显示文本 text, font, color, elide, wrapMode 标签、数值显示、日志输出
Button 可点击按钮 text, onClicked, enabled, flat 起飞/降落、模式切换、确认弹窗
Image 显示图片 source, fillMode, sourceSize 地图图标、无人机图标、logo

3.4.1 Rectangle:一切的基础

Rectangle就像画布。你可以给它上色、加边框、设圆角,甚至做渐变。我在QGC里经常用它来做状态指示——比如飞行器连接时显示绿色矩形,断开时显示红色。

Rectangle {
    width: 120
    height: 40
    radius: 8
    color: "#2ecc71"
    border.color: "#27ae60"
    border.width: 2

    Text {
        anchors.centerIn: parent
        text: "已连接"
        color: "white"
        font.bold: true
    }
}

3.4.2 Text:别小看它

Text组件看起来简单,但坑不少。比如文字太长怎么办?用 elide 属性可以自动加省略号。多行文本?设置 wrapMode: Text.WordWrap。我曾经在QGC的日志面板里忘了设 wrapMode,结果一行日志把整个面板撑爆了——嗯,从那以后我每次都记得加。

Text {
    text: "当前位置: 39.9042°N, 116.4074°E"
    font.pixelSize: 14
    color: "#2c3e50"
    elide: Text.ElideRight   // 超出宽度时显示...
    maximumLineCount: 1
}

3.4.3 Button:交互的核心

Button在QGC里无处不在。起飞按钮、返航按钮、设置按钮……它的核心就是 onClicked。但要注意,QML的Button默认样式比较朴素,在QGC里通常需要自定义外观。

Button {
    text: "紧急降落"
    width: 150
    height: 45
    onClicked: {
        // 这里可以调用C++后端的方法
        console.log("紧急降落指令已发送")
    }
    // 自定义样式
    background: Rectangle {
        color: parent.enabled ? "#e74c3c" : "#bdc3c7"
        radius: 6
    }
    contentItem: Text {
        text: parent.text
        color: "white"
        font.bold: true
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
    }
}

避坑指南:我曾经在QGC里直接用默认Button样式,结果在深色主题下文字根本看不清。后来我养成了习惯——永远自定义Button的background和contentItem,这样不管主题怎么换,按钮都保持你想要的样子。

3.4.4 Image:图标与地图

Image组件用来显示图片。在QGC里,它常用于显示无人机图标、地图标记、或者logo。关键属性是 source,可以指向本地文件或Qt资源路径。

Image {
    source: "qrc:/icons/drone.png"
    width: 48
    height: 48
    fillMode: Image.PreserveAspectFit
    sourceSize.width: 96   // 限制加载尺寸,提高性能
    sourceSize.height: 96
}

注意 sourceSize 这个属性。如果不设,QML会加载图片的原始分辨率——一张4000x3000的图片直接拉满内存。我在优化QGC界面性能时,发现很多卡顿都是因为图片没限制加载尺寸。记住:显示多大,就加载多大。

3.5 知识结构总览

下面这张图把本章的核心知识点串起来了。你可以把它当作QML入门的路线图。

QML基础入门知识结构 QML 基础 QML 语法 属性绑定 信号与槽 常用组件 声明式结构 属性:值 表达式 自动更新 onXxx 自定义信号 Rectangle Text Button Image 掌握这些,QGC插件界面开发的基础就稳了

3.6 本章小结

QML入门其实就这几板斧:语法结构、属性绑定、信号与槽、四个常用组件。你不需要一下子全记住,写的时候回来查就行。我个人建议你从一个小例子开始——比如画一个带按钮的矩形面板,按钮点击后改变文字内容。把这个跑通了,QML的基本流程你就掌握了。