Game Programming using Qt 5 Beginner’s Guide, 2nd Editio 学习笔记-dashboard

在这里插入图片描述

创建的汽车仪表板。我们将使用动画来展示时钟更新其值的方式的真实感,打开仪表板项目并导航到 main.qml 文件。 找到 Needle 对象的声明,该对象负责可视化车辆的速度。 将以下声明添加到对象中:

main.cpp

#include <QtWidgets>
#include <QQuickView>
#include <QQmlEngine>
#include <QQmlContext>
#include <QtQml>
#include "ui_form.h"
#include "carinfoengine.h"
#include "carinfoproxy.h"

int main(int argc, char **argv) {
    QApplication app(argc, argv);
    QQmlApplicationEngine engine;
    //这些修改为我们的场景创建了一个 QML 引擎,将 CarInfo 实例导出到 QML 引擎的全局上下文,并从位于资源中的文件加载和显示场景。
    //重要的是首先导出所有对象,然后才加载场景。这是因为我们希望在初始化场景时所有名称都已可解析,以便可以立即使用它们。如果我们颠倒调用顺序,我们会在控制台上收到许多关于身份未定义的警告。
    QString msg = QStringLiteral("Objects of type CarInfoEngine cannot be created");
    qmlRegisterUncreatableType<CarInfoEngine>("CarInfo", 1, 0, "CarInfoEngine", msg);
    qmlRegisterType<CarInfoProxy>("CarInfo", 1, 0, "CarInfo");

//    qmlRegisterType<CarInfo>("CarInfo", 1, 0, "CarInfo");

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
      return -1;
    return app.exec();
}

carinfo.h

#ifndef CARINFO_H
#define CARINFO_H

#include <QObject>
#include <QWidget>
#include <QDate>

namespace Ui {
class Form;
}

class CarInfoEngine;

class CarInfo : public QWidget {
    //这些属性是只读的,并且 NOTIFY 子句定义了在相应属性值更改时发出的信号。 继续为每个属性实现适当的功能。 除了 getter,还可以将 setter 实现为公共插槽。 这是控制汽车速度的属性的示例:
    Q_OBJECT
    Q_PROPERTY(int speed READ speed WRITE setSpeed NOTIFY speedChanged)
    Q_PROPERTY(double distance READ distance WRITE setDistance NOTIFY distanceChanged)
    Q_PROPERTY(CarInfoEngine* engine READ engine NOTIFY engineChanged)
public:
    CarInfo();
    int speed() const;
    void setSpeed(int s);
    double distance() const;
    void setDistance(double d);
    CarInfoEngine* engine() const;

signals:
    void speedChanged(int);
    void distanceChanged(double);
    void engineChanged();
private:
    CarInfoEngine *m_engine;
    Ui::Form *ui;
};

#endif // CARINFO_H

carinfo.cpp

#include "carinfo.h"
#include "ui_form.h"
#include "carinfoengine.h"

CarInfo::CarInfo() : QWidget(0), ui(new Ui::Form) {
    //在widget中建立适当的信号槽连接,以便为给定参数修改任何widget或使用设置器槽直接更新该参数的所有widgets。
    //您需要向构造函数添加 connect() 语句,以确保从 ui 表单传播信号
    ui->setupUi(this);
    m_engine = new CarInfoEngine(this);
    m_engine->setGear(ui->gearBox->value());
    m_engine->setRpm(ui->rpmBox->value());
    connect(ui->speedBox, SIGNAL(valueChanged(int)),
            this, SIGNAL(speedChanged(int)));
    connect(ui->distanceBox, SIGNAL(valueChanged(double)),
            this, SIGNAL(distanceChanged(double)));

    connect(ui->distanceSlider, &QSlider::valueChanged,
            ui->distanceBox, &QDoubleSpinBox::setValue);
    connect(this, &CarInfo::distanceChanged,
            ui->distanceSlider, &QSlider::setValue);

    connect(ui->gearBox, SIGNAL(valueChanged(int)),
            m_engine, SLOT(setGear(int)));
    connect(ui->rpmBox, SIGNAL(valueChanged(int)),
            m_engine, SLOT(setRpm(int)));
    connect(m_engine, SIGNAL(gearChanged(int)),
            ui->gearBox, SLOT(setValue(int)));
    connect(m_engine, SIGNAL(rpmChanged(int)),
            ui->rpmBox, SLOT(setValue(int)));
}

int CarInfo::speed() const {
    return ui->speedBox->value();
}
void CarInfo::setSpeed(int s) {
    ui->speedBox->setValue(s);
}
double CarInfo::distance() const {
    return ui->distanceBox->value();
}
void CarInfo::setDistance(double d) {
    ui->distanceBox->setValue(d);
}

CarInfoEngine *CarInfo::engine() const {
    return m_engine;
}


由于我们要使用小部件来调整属性值,因此请使用表单编辑器为其设计用户界面。 它看起来像这样:

在这里插入图片描述

import QtQuick 2.9
import QtQuick.Window 2.3
import CarInfo 1.0
Window {
    visible: true
    width: backgroundImage.width
    height: backgroundImage.height
	//我们在这里所做的是将图像添加到窗口并创建三个项目作为仪表板不同元素的容器。

    Image {
        id: backgroundImage
        source: "qrc:/dashboard_1080x720.png"
        CarInfo {
            id: carData
            visible: true
        }

        Item {
            id: leftContainer
            property real value: carData.engine.rpm/1000
			//容器都在父项中居中,我们使用 horizontalCenterOffset 属性将两个外部项目横向移动
            //偏移量和宽度的值基于背景图像的几何形状
            //如果您不使用我们的文件,而是使用 Qt Quick 项目自己创建三个部分,则容器将简单地锚定到三个黑色项目的中心。
            anchors.centerIn: parent
            anchors.horizontalCenterOffset: -350
            width: 300
            height: 300
			//这是一个函数的实现,用于计算沿圆周的位置,基于圆的半径和应放置item的角度
            function calculatePosition(angle, radius) {
                //该函数将度数转换为弧度并返回所需的点。 该函数期望 width 属性可用,以帮助计算圆心,如果没有给出半径,则用作计算其可行值的方法。
                if(radius === undefined) {
                    radius = width / 2 * 0.8;
                }
                var a = angle * Math.PI / 180;
                var px = width / 2 + radius * Math.cos(a);
                var py = width / 2 + radius * Math.sin(a);
                return Qt.point(px, py);
            }
			//有了这样的功能,我们可以使用已经熟悉的 Repeater 元素将项目放置在我们想要的位置。 让我们把这个函数放在 middleContainer 中并声明汽车速度的刻度盘
            Repeater {
                model: 7

                Item {
                    property point pt: leftContainer.calculatePosition(120+index*35)
                    x: pt.x
                    y: pt.y
                    Label {
                        anchors.centerIn: parent
                        text: index
                    }
                }
            }

            Text {
                anchors.centerIn: parent
                anchors.verticalCenterOffset: 40
                text: "RPM\n[x1000]"
                horizontalAlignment: Text.AlignHCenter
                color: "#aaa"
                font.pixelSize: 16
            }

            Item {
                id: gearContainer
                anchors.centerIn: parent
                anchors.horizontalCenterOffset: 10
                anchors.verticalCenterOffset: -10

                Text {
                    id: gear
                    property int value: carData.engine.gear
                    property var gears: [
                        "R", "N", "1<sup>st</sup>", "2<sup>nd</sup>",
                        "3<sup>rd</sup>", "4<sup>th</sup>", "5<sup>th</sup>"
                    ]
                    anchors.left: parent.left
                    anchors.bottom: parent.bottom
                    text: gears[value + 1]
                    color: "yellow"
                    font.pixelSize: 32
                    textFormat: Text.RichText
                }
            }

            Needle {
                anchors.centerIn: parent
                length: parent.width * 0.35
                rotation: 120 + 90 + (leftContainer.value * 35)
                	//Behavior 元素不是立即接受新值
                    //而是拦截请求并启动 SmoothedAnimation 类以逐渐达到请求的值
                    Behavior on rotation {
                    //SmoothedAnimation 类是一种动画类型
                    //,可以为数字属性设置动画。
                    //动画的速度不是由它的持续时间决定的
                    //设置了速度属性。 此属性指示更改值的速度
                    //但是,动画使用的是非线性路径——它开始缓慢,然后加速到给定的速度,并在动画接近尾声时以平滑的方式减速
                    SmoothedAnimation {
                        velocity: 100
                    }
                }

            }

        }
        Item {
            id: middleContainer
            property int value: carData.speed
            anchors.centerIn: parent
            width: 400
            height: width

            function calculatePosition(angle, radius) {
                if(radius === undefined) {
                    radius = width / 2 * 0.8;
                }
                var a = angle * Math.PI / 180;
                var px = width / 2 + radius * Math.cos(a);
                var py = width / 2 + radius * Math.sin(a);
                return Qt.point(px, py);
            }

            Repeater {
                model: 24 / 2
				//表盘由一个repeater组成,可创建 12 个元素。每个元素都是一个使用前面描述的函数定位的项目。该项目有一个标签锚定在它上面,显示给定的速度。我们使用 120 + index * 12 * 2 作为角度表达式,因为我们希望“0”定位在 120 度处,并且接下来的每个项目都进一步定位 24 度。
                Item {
                    property point pt:
                        middleContainer.calculatePosition(120 + index * 12 * 2)
                    x: pt.x; y: pt.y
                    Label {
                        anchors.centerIn: parent
                        text: index * 20
                    }
                }
            }
            Repeater {
                model: 120 - 4

                Item {
                    property point pt: middleContainer.calculatePosition(
                        120 + index * 1.2 * 2, middleContainer.width * 0.35)
                    x: pt.x
                    y: pt.y
                    Rectangle {
                        width: 2
                        height: index % 5 ? 5 : 10
                        color: "white"
                        rotation: 90 + 120 + index * 1.2 * 2
                        anchors.centerIn: parent
                        antialiasing: true
                    }
                }
            }

            Text {
                anchors.centerIn: parent
                anchors.verticalCenterOffset: 40
                text: "SPEED\n[kph]"
                horizontalAlignment: Text.AlignHCenter
                color: "#aaa"
                font.pixelSize: 16
            }

            Needle {
                anchors.centerIn: parent
                length: parent.width * 0.35
                size: 4
                rotation: 210 + (middleContainer.value * 1.2)
                color: "yellow"
                Behavior on rotation {
                    SmoothedAnimation {
                        velocity: 50
                    }
                }
            }

        }
        Item {
            id: rightContainer

            anchors.centerIn: parent
            anchors.horizontalCenterOffset: 425
            width: 150
            height: 150

            Rectangle {
                id: digitalDisplay
                anchors.right: parent.right
                anchors.verticalCenter: parent.verticalCenter

                border.color: "white"
                border.width: 1
                radius: 10
                width: 250
                height: 200
                color: "transparent"
                Item {
                    anchors.fill: parent
                    anchors.margins: 10
                    Label {
                        id: dateLabel
                        text: Qt.formatDate(new Date)
                        anchors.horizontalCenter: parent.horizontalCenter
                    }
                    Grid {
                        id: topGrid
                        anchors.top: dateLabel.bottom
                        anchors.topMargin: 15
                        anchors.left: parent.left
                        anchors.right: parent.right
                        anchors.bottom: bottomGrid.top
                        anchors.bottomMargin: 15
                        columns: 2
                        spacing: 5
                        Label {
                            text: "Temperature:"
                            width: parent.width/2
                        }
                        Label {
                            text: "+17°C"
                            horizontalAlignment: Text.AlignRight
                            width: parent.width / 2 - 5
                        }
                        Label {
                            text: "Humidity:"
                            width: parent.width / 2
                        }
                        Label {
                            text: "68.4%"
                            horizontalAlignment: Text.AlignRight
                            width: parent.width / 2 - 5
                        }

                    }
                    Grid {
                        id: bottomGrid
                        anchors.left: parent.left
                        anchors.right: parent.right
                        anchors.bottom: parent.bottom
                        anchors.bottomMargin: 15
                        Label {
                            text: "Distance:"
                            width: parent.width / 2
                        }
                        Label {
                            text: carData.distance+" km"
                            horizontalAlignment: Text.AlignRight
                            width: parent.width / 2 - 5
                        }
                    }
                }
            }
            Row {
                anchors.top: digitalDisplay.bottom
                anchors.topMargin: 10
                anchors.left: digitalDisplay.left
                anchors.right: digitalDisplay.right
                spacing: 10

                Label {
                    text: "ABS"
                    color: "#222"
                }
                Label {
                    text: "ESP"
                    color: "#da0"
                }
                Label {
                    text: "BRK"
                    color: "#222"
                }

                Label {
                    text: "CHECK"
                    color: "#222"
                }
            }
        }
    }
}

Needle.qml

import QtQuick 2.9

Item {
    id: root
    //该文档定义了一个具有四个属性的项目——针的长度(默认为表盘半径的 80%)
    //、针的颜色、middleColor,代表针的固定颜色,以及尺寸,定义如何 针很宽。
    property int length: parent.width * 0.4
    property color color: "white"
    property color middleColor: "red"
    property int size: 2
        
    //Item本身没有任何尺寸,仅作为视觉元素的锚点——针本身是一个垂直方向的细长矩形,从末端固定 20 个单位。
    Rectangle { //needle
        width: root.size
        height: length + 20
        color: root.color
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.bottom: parent.bottom
        anchors.bottomMargin: -20
        antialiasing: true
    }
	//固定是一个与针颜色相同的圆圈,中间有一个较小的圆圈,使用不同的填充颜色。 内圆的较小半径是通过从每侧填充外圆 25% 的余量来获得的。
    Rectangle { //fixing
        anchors.centerIn: parent
        width: 8 + root.size
        height: width
        radius: width / 2
        color: root.color
        Rectangle { //middle dot
            anchors {
                fill: parent
                margins: parent.width * 0.25
            }
            color: root.middleColor
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值