页面的布局
- 第一层 绿色Rectangle作为打底,布满窗口
- 第二层 两条灰色Rectangle布局成灰色十字型
anchors.leftMargin
anchors.horizontalCenter
用这两个进行中心布局都可以 - 第三层 分流线(矩形画)和斑马线(图片)和十字路口(矩形)
- 第四层 车(图片)
- 第五层 灯 (图片)
红绿灯逻辑(Connections)
十字路口如果只考虑直行,那么只需要封装一个红绿灯就可以,考虑横竖红绿灯角度不一致,图片不一样那就写成两个组件,唯一不同的就是图片的路径不一致
接收后端红绿灯变化的信号,一旦后端信号灯变化就改变红绿灯的展示,onCurrentLightStateChanged
这边灯用了一个图片,外加三个矩形化成的圆形进行覆盖
import QtQuick 2.12
Image {
id: lightLeftRight
source: "qrc:/cars/light"
sourceSize.height: 100
Rectangle {
id: yellowLight
width: 17
height: 17
radius: 50
color: "#808080"
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
id: redLight
width: 17
height: 17
radius: 50
color: "#808080"
anchors.horizontalCenter: parent.horizontalCenter
y: 20
}
Rectangle {
id: greenLight
width: 17
height: 17
radius: 50
color: "#808080"
anchors.horizontalCenter: parent.horizontalCenter
y: 62
visible: false
}
Connections {
target: trafficLightController // 假设trafficLightController是C++对象,已在QML上下文中设置
onCurrentLightStateChanged: {
if (newState === "Red") {
redLight.visible = false;
yellowLight.visible = true;
greenLight.visible = true;
} else if (newState === "Yellow") {
redLight.visible = true;
yellowLight.visible = false;
greenLight.visible = true;
} else {
redLight.visible = true;
yellowLight.visible = true;
greenLight.visible = false;
}
}
}
}
汽车运动的逻辑(Timer)
- 设置两个Timer,一个控制循环走,一个控制遇到信号灯处理
- 第一个Timer interval: 16 //每16毫秒检查一次位置repeat: true // 重复触发running: true // 开始时即运行
onTriggered触发
- 没到底按正常走
- 到底了重新设置x或y
- 第二个
- 红灯停 moveTimer.running = false;(moveTimer是第一个Timer的名称)
- 绿灯 moveTimer2.running = true; 启动 speed3 = fasterSpeed; 恢复正常速度
- 快到斑马线的某段距离减速 speed3 = slowerSpeed;
- 其他时候正常速度
Image {
id: downcar
source: "qrc:/cars/downcar"
sourceSize.height: 100
anchors.left: loadCenter.left
y: -downcar.height
// 定时器组件
Timer {
id: moveTimer2
interval: 16 // 约60fps
running: true // 默认开始运行
repeat: true
onTriggered: {
if (downcar.y < window.height) {
downcar.y += speed3;
} else {
downcar.y = -downcar.height;
}
}
}
// 定时器组件
Timer {
id: positionCheckTimer2
interval: 16 // 每50毫秒检查一次位置
repeat: true // 重复触发
running: true // 开始时即运行
onTriggered: {
if(downcar.y > positionCheckHeight-positionCheckLengthRange && downcar.y< positionCheckHeight &&
trafficLightController1.currentLightState === "Red"){
moveTimer2.running = false;
} else if(trafficLightController1.currentLightState === "Green") {
moveTimer2.running = true;
speed3 = fasterSpeed;
} else if(downcar.y > positionCheckHeight-positionCheckLengthRange && downcar.y< positionCheckHeight &&
(trafficLightController1.currentLightState === "Yellow" ||
trafficLightController1.currentLightState === "Red")) {
speed3 = slowerSpeed;
} else {
speed3 = fasterSpeed;
}
}
}
}
后端逻辑代码
逻辑梳理
- Timer计时器 初始化时就设置好时间,当Timer一完成就改变信号灯
- 当信号灯改变,重新设置Timer计时器,并发出灯已经改变的信号
- 灯的属性值发生改变,发出currentLightStateChanged信号
- 前端通过onCurrentLightStateChanged获取信号 再根据newState判断信号灯
- 车的话就直接获取当前的信号灯trafficLightController1.currentLightState
Connections {
target: trafficLightController1
onCurrentLightStateChanged: {// 业务代码}
}
两部分代码
TrafficLightController .h
#ifndef TRAFFICLIGHTCONTROLLER_H
#define TRAFFICLIGHTCONTROLLER_H
#include <QObject>
#include <QTimer>
class TrafficLightController : public QObject
{
Q_OBJECT // 宏
Q_PROPERTY(QString currentLightState READ currentLightState NOTIFY currentLightStateChanged)
public:
explicit TrafficLightController(QObject *parent = nullptr);
QString currentLightState() const; // 返回当前的红绿灯状态
signals:
void currentLightStateChanged(QString newState); // 当前红绿灯信号改变
private slots:
void changeLight(); // 改变红绿灯信号
private:
enum LightState { Green, Yellow, Red }; // 枚举类型设置灯的信号
int durations[3] = {7000, 3000, 7000}; // 持续时间:绿灯5秒,黄灯2秒,红灯5秒 水平方向信号灯
// int durations[3] = {10000, 4000, 3000}; // 持续时间:红灯7秒,绿灯4秒,黄灯1秒,垂直方向信号灯
LightState currentState; // 当前的红绿灯状态
QTimer timer; // 控制红绿灯时间
QString lightStateToString(LightState state) const; // 返回红绿灯使用String类型返回
};
#endif // TRAFFICLIGHTCONTROLLER_H
TrafficLightController.cpp
#include "TrafficLightController.h"
TrafficLightController::TrafficLightController(QObject *parent)
: QObject(parent), currentState(Green) // 初始化信号灯为绿灯
{
connect(&timer, &QTimer::timeout, this, &TrafficLightController::changeLight); // Timer到时间发出信号改变信号灯
timer.start(durations[currentState]);
}
QString TrafficLightController::currentLightState() const
{
return lightStateToString(currentState);
}
void TrafficLightController::changeLight()
{
static const int nextStateIndices[] = {1, 2, 0}; // 下一个状态索引
int nextStateIndex = nextStateIndices[static_cast<int>(currentState)];
currentState = static_cast<LightState>(nextStateIndex);
timer.setInterval(durations[currentState]);
emit currentLightStateChanged(lightStateToString(currentState));
}
QString TrafficLightController::lightStateToString(LightState state) const
{
switch (state) {
case Green:
return "Green";
case Yellow:
return "Yellow";
case Red:
return "Red";
default:
return "Unknown";
}
}
解释一下
Q_PROPERTY(QString currentLightState READ currentLightState NOTIFY currentLightStateChanged)
完整代码
代码目录
App.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
ApplicationWindow {
id: window
visible: true
width: 1200
height: 800
title: qsTr("模拟十字路口车流")
/**
// 十字路口 模拟车辆在十字路口 运动 图像的动画 运行轨迹 红绿灯显示 画一个矩形 设置不同
// 颜色 定时器(C++)变化 影响车的流动
// 界面 十字路口
// 红绿灯
// 运动 红灯绿灯 走直线 定时器(C++)控制变化
*/
// 背景图片 绿地
property real grassWidth: 50
property real treeWidth: 66
Rectangle {
color: "#2a4d00"
width: parent.width
height: parent.height
}
// 上下方向的道路
Rectangle {
id: upDownLoad
color: "grey";
height: parent.height
width: 200
// anchors.left: parent.left
// anchors.leftMargin: (parent.width-200)/2
anchors.horizontalCenter: parent.horizontalCenter
Rectangle {
color: "lightyellow";
height: parent.height
width: 10
anchors.left: parent.left
anchors.centerIn: parent;
}
}
// 左右方向的道路
Rectangle {
id: leftRightLoad
color: "grey";
width: parent.width
height: 200
anchors.top: parent.top
anchors.topMargin: (parent.height-200)/2
Rectangle {
color: "lightyellow";
width: parent.width
height: 10
anchors.left: parent.left
anchors.centerIn: parent;
}
}
// 中间十字路口
Rectangle {
id: loadCenter
color: "grey";
width: 200
height: 200
anchors.centerIn: parent;
}
property real zebraCrossingLength: 35
// 斑马线 水平方向
Image {
source: "qrc:/cars/zebraCrossing"
anchors.right: loadCenter.left
height: 120
width: zebraCrossingLength
y: window.height/2 - 120
}
Image {
source: "qrc:/cars/zebraCrossing"
anchors.right: loadCenter.left
height: 120
width: zebraCrossingLength
y: window.height/2
}
Image {
source: "qrc:/cars/zebraCrossing"
anchors.left: loadCenter.right
height: 120
width: zebraCrossingLength
y: window.height/2 - 120
}
Image {
source: "qrc:/cars/zebraCrossing"
anchors.left: loadCenter.right
height: 120
width: zebraCrossingLength
y: window.height/2
}
// 斑马线 垂直方向
Image {
source: "qrc:/cars/zebraCrossing1"
anchors.bottom: loadCenter.top
width: 120
height: zebraCrossingLength
x: window.width/2
}
Image {
source: "qrc:/cars/zebraCrossing1"
anchors.bottom: loadCenter.top
width: 120
height: zebraCrossingLength
x: window.width/2 - 120
}
Image {
source: "qrc:/cars/zebraCrossing1"
anchors.top: loadCenter.bottom
width: 120
height: zebraCrossingLength
x: window.width/2
}
Image {
source: "qrc:/cars/zebraCrossing1"
anchors.top: loadCenter.bottom
width: 120
height: zebraCrossingLength
x: window.width/2 - 120
}
// 车辆停止的范围
property real speed1: 7
property real speed2: 7
property real speed3: 7
property real speed4: 7
property real fasterSpeed: 7
property real slowerSpeed: 4
property real slowerslowerSpeed: 2
property real carLength: 100
property real loadCenterLength: 200
property real positionCheckLength: (window.width - loadCenterLength) / 2 - carLength
property real positionCheckLength1: (window.width - loadCenterLength) / 2 + loadCenterLength
property real positionCheckLengthRange: 50
property real positionCheckHeight: (window.height - loadCenterLength) / 2 - carLength
property real positionCheckHeight1: (window.height - loadCenterLength) / 2 + loadCenterLength
// 左右方向的车辆
Image {
id: rightcar
source: "qrc:/cars/rightcar"
sourceSize.height: 100
anchors.bottom: loadCenter.bottom
x: -rightcar.width
// 定时器组件
Timer {
id: moveTimer
interval: 16 // 约60fps
running: true // 默认开始运行
repeat: true
onTriggered: {
if (rightcar.x < window.width) {
rightcar.x += speed1;
} else {
rightcar.x = -rightcar.width;
}
}
}
// 定时器组件
Timer {
id: positionCheckTimer
interval: 16 // 每50毫秒检查一次位置
repeat: true // 重复触发
running: true // 开始时即运行
onTriggered: {
// console.log(rightcar.x)
if(rightcar.x > positionCheckLength-positionCheckLengthRange && rightcar.x< positionCheckLength&&
trafficLightController.currentLightState === "Red"){
moveTimer.running = false;
} else if(trafficLightController.currentLightState === "Green") {
moveTimer.running = true;
speed1 = fasterSpeed;
} else if(rightcar.x > positionCheckLength-positionCheckLengthRange && rightcar.x< positionCheckLength &&
(trafficLightController.currentLightState === "Yellow" ||
trafficLightController.currentLightState === "Red")) {
speed1 = slowerslowerSpeed;
} else {
speed1 = fasterSpeed;
}
}
}
}
Image {
id: leftcar
source: "qrc:/cars/leftcar"
sourceSize.height: 100
anchors.top: loadCenter.top
// 定时器组件
Timer {
id: moveTimer1
interval: 16 // 约60fps
running: true // 默认开始运行
repeat: true
onTriggered: {
// console.log(leftcar.x)
if (leftcar.x < -leftcar.width) {
leftcar.x = window.width + leftcar.width;
} else {
leftcar.x -= speed2;
}
}
}
// 定时器组件
Timer {
id: positionCheckTimer1
interval: 16 // 每50毫秒检查一次位置
repeat: true // 重复触发
running: true // 开始时即运行
onTriggered: {
// console.log(leftcar.x)
if(leftcar.x > positionCheckLength1 && leftcar.x< positionCheckLength1 + positionCheckLengthRange &&
trafficLightController.currentLightState === "Red"){
moveTimer1.running = false;
} else if(
trafficLightController.currentLightState === "Green") {
moveTimer1.running = true;
speed2 = fasterSpeed;
} else if(leftcar.x > positionCheckLength1 && leftcar.x< positionCheckLength1 + positionCheckLengthRange &&
(trafficLightController.currentLightState === "Yellow" ||
trafficLightController.currentLightState === "Red")) {
// console.log(leftcar.x)
speed2 = slowerslowerSpeed;
} else {
speed2 = fasterSpeed;
}
}
}
}
// 上下方向的车辆
Image {
id: downcar
source: "qrc:/cars/downcar"
sourceSize.height: 100
// anchors.top : loadCenter.top
anchors.left: loadCenter.left
y: -downcar.height
// 定时器组件
Timer {
id: moveTimer2
interval: 16 // 约60fps
running: true // 默认开始运行
repeat: true
onTriggered: {
if (downcar.y < window.height) {
downcar.y += speed3;
} else {
downcar.y = -downcar.height;
}
}
}
// 定时器组件
Timer {
id: positionCheckTimer2
interval: 16 // 每50毫秒检查一次位置
repeat: true // 重复触发
running: true // 开始时即运行
onTriggered: {
if(downcar.y > positionCheckHeight-positionCheckLengthRange && downcar.y< positionCheckHeight &&
trafficLightController1.currentLightState === "Red"){
// console.log(downcar.y)
moveTimer2.running = false;
} else if(trafficLightController1.currentLightState === "Green") {
moveTimer2.running = true;
speed3 = fasterSpeed;
} else if(downcar.y > positionCheckHeight-positionCheckLengthRange && downcar.y< positionCheckHeight &&
(trafficLightController1.currentLightState === "Yellow" ||
trafficLightController1.currentLightState === "Red")) {
speed3 = slowerSpeed;
} else {
speed3 = fasterSpeed;
}
}
}
}
Image {
id: upcar
source: "qrc:/cars/upcar"
sourceSize.height: 100
anchors.right: loadCenter.right
y: window.height
// 定时器组件
Timer {
id: moveTimer3
interval: 16 // 约60fps
running: true // 默认开始运行
repeat: true
onTriggered: {
if (upcar.y < -upcar.height) {
upcar.y = window.height+upcar.height;
} else {
upcar.y -= speed4;
}
}
}
// 定时器组件
Timer {
id: positionCheckTimer3
interval: 16 // 每50毫秒检查一次位置
repeat: true // 重复触发
running: true // 开始时即运行
onTriggered: {
// console.log(upcar.x)
if(upcar.y > positionCheckHeight1 && upcar.y< positionCheckHeight1 + positionCheckLengthRange &&
trafficLightController1.currentLightState === "Red"){
moveTimer3.running = false;
} else if(trafficLightController1.currentLightState === "Green") {
moveTimer3.running = true;
speed4 = fasterSpeed;
} else if(upcar.y > positionCheckHeight1 && upcar.y< positionCheckHeight1 + positionCheckLengthRange &&
(trafficLightController1.currentLightState === "Yellow" ||
trafficLightController1.currentLightState === "Red")) {
speed4 = slowerSpeed;
} else {
speed4 = fasterSpeed;
}
}
}
}
// 向右红绿灯
LightLeftRight {
id: lightright
anchors.left: loadCenter.right
y: window.height/2-loadCenter.height/8
}
// 向左红绿灯
LightLeftRight {
id: lightleft
anchors.right: loadCenter.left
y: window.height/2-loadCenter.height/8*3
}
// 向上红绿灯
LightUpDown {
id: lightup
anchors.bottom: loadCenter.top
x: window.width/2-loadCenter.height/8
}
// 向下红绿灯
LightUpDown {
id: lightdown
anchors.top: loadCenter.bottom
x: window.width/2-loadCenter.height/8*3
}
}
App.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "MyCppClass.h"
#include "MyListModel.h"
#include "TrafficLightController.h"
#include "TrafficLightController1.h"
#include <QIcon>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
app.setWindowIcon(QIcon(":/images/music.png"));
// 创建TrafficLightController实例
TrafficLightController trafficLightController;
TrafficLightController1 trafficLightController1;
QQmlApplicationEngine engine;
// ... 添加更多项目
MyListModel model;
// 创建C++对象实例
MyCppClass cppObject;
// 将C++对象注册到QML上下文中
engine.rootContext()->setContextProperty("myCppObject", &cppObject);
engine.rootContext()->setContextProperty("myListModel", &model);
// 将TrafficLightController实例设置为QML的上下文属性
// 这样就可以在QML文件中通过id "trafficLightController" 访问它了
engine.rootContext()->setContextProperty("trafficLightController", &trafficLightController);
engine.rootContext()->setContextProperty("trafficLightController1", &trafficLightController1);
const QUrl url(QStringLiteral("qrc:/App.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
// 加载QML文件
engine.load(url);
return app.exec();
}