qml绘制仪表盘控件

2024-03-12 09:59
文章标签 仪表盘 绘制 控件 qml

本文主要是介绍qml绘制仪表盘控件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

刚开始上手qt,说实话迷得很啊。。。

不过qt做的界面是真的漂亮,之前在b站看qt官方发布的一些视频,仪表盘煞是好看

今天倒腾了一天,用qml绘制了一个简单的汽车仪表控件,趁现在还热着,先记下来

新建一个空的qml工程

创建工程的时候要注意,工程路径中不能有中文,否则会导致编译失败

创建完成后按例编译运行,确保工程能正常编译,否则等写得差不多却发现无法正常编译那就很尴尬了。。。

再添加一个单独的.qml文件,我们在里面编写自定义的控件,这样在别的工程中只要载入这个qml文件就能使用了~~

起个名字 (Mycar。。。哈哈,暂且就这样吧

暂时就以这个为例子吧,今天做出来的也就和这个像

从上面这张图中,我们可以看到这个表盘主要是由几个弧构成。qt官方这个例子是先通过Photoshop设计出表盘整体的样式,然后通过插件直接从ps中导出.qml,接着就是在qt软件上进行编辑。不得不说这种设计和逻辑分开的方式的确非常的棒!让天堂的归天堂,让尘土的归尘土。ps咱不熟悉,过qt的ps插件倒是挺有意思,具体的我还没有研究明白,等哪天搞懂了再写篇文章记一下。需要这个插件的朋友请往:https://code.qt.io/cgit/qt-labs/photoshop-qmlexporter.git/ 或者同性社区: https://github.com/qt-labs/photoshop-qmlexporter

因为这个仪表主要由弧构成,因此我们需要在canvas中对仪表的各个部分进行绘制。首先,我们需要绘制两个重叠的圆弧。最下面的圆弧设置成浅灰色作为背景,最顶上的圆弧则实时显示我们汽车的速度。刚刚创建的qml文件中,键入如下代码:

// file - Mycar.qmlimport QtQuick 2.0Item {id: carItemwidth: 100height: 100// 背景圆弧线宽property int btm_lineWidth: 15// 背景圆弧颜色property color btm_backgroundColor: Qt.rgba(0, 0, 0, 0.1);// 背景圆弧半径 开始角度 结束角度property int btm_r: 20property double btm_startAngle: 0property double btm_endAngle: 90onBtm_lineWidthChanged: canvas.requestPaint()onBtm_backgroundColorChanged: canvas.requestPaint()onBtm_rChanged: canvas.requestPaint()onBtm_startAngleChanged: canvas.requestPaint()onBtm_endAngleChanged: canvas.requestPaint()// 顶层圆弧线宽property int top_lineWidth: 10// 顶层圆弧颜色property color top_backgroundColor: "lightgreen"// 顶层圆弧半径 开始角度 结束角度property int top_r: 20property double top_startAngle: 0property double top_endAngle: 90onTop_lineWidthChanged: canvas.requestPaint()onTop_backgroundColorChanged: canvas.requestPaint()onTop_rChanged: canvas.requestPaint()onTop_startAngleChanged: canvas.requestPaint()onTop_endAngleChanged: canvas.requestPaint()Canvas {id: canvaswidth: carItem.widthheight: carItem.heightonPaint: {var ctx = getContext("2d");ctx.clearRect(0, 0, canvas.width, canvas.height);// 画背景圆弧ctx.lineWidth = carItem.btm_lineWidth;ctx.strokeStyle = carItem.btm_backgroundColor;ctx.beginPath();ctx.arc(carItem.width/2, carItem.width/2, carItem.btm_r, (carItem.btm_startAngle/180*Math.PI), (carItem.btm_endAngle/180*Math.PI));ctx.stroke();// 画顶层圆弧ctx.lineWidth = carItem.top_lineWidth;ctx.strokeStyle = carItem.top_backgroundColor;ctx.beginPath();ctx.arc(carItem.width/2, carItem.width/2, carItem.top_r, (carItem.top_startAngle/180*Math.PI), (carItem.top_endAngle/180*Math.PI));ctx.stroke();}}
}

这里啰嗦一下,我顶层圆弧和底层圆弧属性的命名前置分别是“top_”、“btm_”。为了在设计模式中更改这些属性的值时,图像能够马上更新,代码中使用了形似 onXxxxxxChanged(格式:on<Property>Changed)的信号处理器,当某个属性的值变更时,会触发canvas的重绘(即,canvas.requestPaint())。举个栗子,例如代码中的btm_lineWidth变量,写法就是:

on + Btm_lineWidth + Changed     ,     即:onBtm_lineWidthChanged: canvas.requestPaint()

如果开头的b没有大写,就会报:

后面在main.qml中引用Mycar.qml的控件也是如此,调用的对象名称首字母必须大写。详见qt文档:https://doc.qt.io/qt-5/qtqml-documents-definetypes.html

接着,回到main.qml文件中,引用我们刚刚写的仪表控件

点击左侧的 设计,可以看到自定义的仪表控件已经被载入到界面中。右下角的则是我们在Mycar.qml中定义的,我们可以通过修改这些达到更改顶层和底层圆弧的大小、颜色等目的

接下来,调整一下仪表的位置和大小,这里我还增加了一些控件,一个开关用来控制顶层圆弧的线宽,滑块用来模拟汽车的速度,文本框用来显示速度。。。

在main.qml中给Mycar一个id=speed_car,然后将顶层圆弧的终止角度与滑块进行绑定。当然你可以使用下图的方式进行绑定,也可以直接在Mycar中进行书写。这里我们约定这个速度仪表盘的最大刻度为200Km/h,将0~200Km/h映射到我们的仪表盘上。

点击左下角的运行,滑动滑块,可以看到开关控制的两种效果

不过作为仪表盘来说,虽然现在有了点模样,但还缺少了刻度盘。qt官方的刻度盘是ps设计好的,之后再在qt中把速度的值用文本框加上,这样就能设置字体和样式了。这里咱没有ps,不过没关系啊,用代码画出来就行,撸起袖子就是干

回到Mycar.qml文件中,将绘制刻度盘的代码加上

import QtQuick 2.0Item {id: carItemwidth: 100height: 100// 背景圆弧线宽property int btm_lineWidth: 15// 背景圆弧颜色property color btm_backgroundColor: Qt.rgba(0, 0, 0, 0.1);// 背景圆弧半径 开始角度 结束角度property int btm_r: 20property double btm_startAngle: 0property double btm_endAngle: 90onBtm_lineWidthChanged: canvas.requestPaint()onBtm_backgroundColorChanged: canvas.requestPaint()onBtm_rChanged: canvas.requestPaint()onBtm_startAngleChanged: canvas.requestPaint()onBtm_endAngleChanged: canvas.requestPaint()// 顶层圆弧线宽property int top_lineWidth: 10// 顶层圆弧颜色property color top_backgroundColor: "lightgreen"// 顶层圆弧半径 开始角度 结束角度property int top_r: 20property double top_startAngle: 0property double top_endAngle: 90onTop_lineWidthChanged: canvas.requestPaint()onTop_backgroundColorChanged: canvas.requestPaint()onTop_rChanged: canvas.requestPaint()onTop_startAngleChanged: canvas.requestPaint()onTop_endAngleChanged: canvas.requestPaint()// 刻度盘property color dial_color: "#000000"property int dial_lineWidth: 3property int dial_addR: 2          // 通过调整该变量可以控制刻度盘圆弧与底层圆弧的距离property int dial_longNum: 5       // 刻度盘长刻度线的数量property int dial_longLen: 10      // 刻度盘长刻度线的长度onDial_colorChanged: canvas.requestPaint()onDial_lineWidthChanged: canvas.requestPaint()onDial_addRChanged: canvas.requestPaint()onDial_longNumChanged: canvas.requestPaint()onDial_longLenChanged: canvas.requestPaint()Canvas {id: canvaswidth: carItem.widthheight: carItem.heightonPaint: {var ctx = getContext("2d");ctx.clearRect(0, 0, canvas.width, canvas.height);// 画背景圆弧ctx.lineWidth = carItem.btm_lineWidth;ctx.strokeStyle = carItem.btm_backgroundColor;ctx.beginPath();ctx.arc(carItem.width/2, carItem.width/2, carItem.btm_r, (carItem.btm_startAngle/180*Math.PI), (carItem.btm_endAngle/180*Math.PI));ctx.stroke();// 画大刻度盘ctx.lineWidth = carItem.dial_lineWidth;ctx.strokeStyle = carItem.dial_color;ctx.beginPath();ctx.arc(carItem.width/2, carItem.width/2, carItem.btm_r+carItem.btm_lineWidth+carItem.dial_addR, (carItem.btm_startAngle/180*Math.PI), (carItem.btm_endAngle/180*Math.PI));var tmp_step = (carItem.btm_endAngle-carItem.btm_startAngle)/carItem.dial_longNum;for(var i=carItem.btm_startAngle;i<carItem.btm_endAngle+tmp_step;i+=tmp_step) {var tmp_x = (carItem.width/2)+(carItem.btm_r+carItem.btm_lineWidth+carItem.dial_addR)*Math.cos(i/180*Math.PI);var tmp_y = (carItem.width/2)+(carItem.btm_r+carItem.btm_lineWidth+carItem.dial_addR)*Math.sin(i/180*Math.PI);ctx.moveTo(tmp_x, tmp_y);// 绘制长刻度线ctx.lineTo(tmp_x+carItem.dial_longLen*Math.cos(i/180*Math.PI), tmp_y+(carItem.dial_longLen*Math.sin(i/180*Math.PI)));}ctx.stroke();// 画顶层圆弧ctx.lineWidth = carItem.top_lineWidth;ctx.strokeStyle = carItem.top_backgroundColor;ctx.beginPath();ctx.arc(carItem.width/2, carItem.width/2, carItem.top_r, (carItem.top_startAngle/180*Math.PI), (carItem.top_endAngle/180*Math.PI));ctx.stroke();}}
}

调整刻度盘的参数,嘿嘿,还是有点模样的

接着用标签控件将速度刻度加上

运行效果:

工程完整代码如下:

main.qml

// file - main.qmlimport QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.0Window {visible: truewidth: 640height: 480title: qsTr("Hello qt")Mycar {id: speed_carx: 175y: 93width: 291height: 238dial_addR: -6dial_longNum: 10dial_longLen: 15dial_lineWidth: 3btm_lineWidth: 22top_lineWidth: 10top_endAngle: slider.value*1.3+140top_startAngle: 140btm_endAngle: 400btm_startAngle: 140btm_r: 120top_r: 120Text {id: speedx: 104y: 116width: 89height: 44text: slider.valuestyle: Text.Normalfont.weight: Font.ExtraBoldfont.capitalization: Font.MixedCasefont.pixelSize: 40font.bold: truefont.family: "Verdana"horizontalAlignment: Text.AlignHCenter}Label {id: speed_labelx: 131y: 154width: 45height: 30text: qsTr("Km/h")font.pointSize: 11font.bold: trueverticalAlignment: Text.AlignBottom}Label {id: label1x: 8y: 235width: 23height: 25text: qsTr("0")font.weight: Font.NormalhorizontalAlignment: Text.AlignHCenterfont.pointSize: 14}Label {id: label2x: 263y: 235width: 33height: 25text: qsTr("200")horizontalAlignment: Text.AlignHCenterfont.pointSize: 14font.weight: Font.Normal}Label {id: label3x: -28y: 172width: 23height: 25text: qsTr("20")horizontalAlignment: Text.AlignHCenterfont.pointSize: 14font.weight: Font.Normal}}Switch {id: sthx: 501y: 10text: "Wifi"onClicked: {if(sth.position) {speed_car.top_lineWidth = speed_car.btm_lineWidth;} else {speed_car.top_lineWidth = 10}}}Slider {id: sliderx: 220y: 367font.pointSize: 14stepSize: 1to: 200from: 0value: 0onValueChanged: {if(value<60) {speed.color = "black"}else if(value<120) {speed.color = "#f2ac28"}else {speed.color = "red"}speed_label.color = speed.color}}Label {id: label4x: 466y: 264width: 42height: 25text: qsTr("180")horizontalAlignment: Text.AlignHCenterfont.pointSize: 14font.weight: Font.Normal}Label {id: label5x: 146y: 192width: 23height: 25text: qsTr("40")horizontalAlignment: Text.AlignHCenterfont.pointSize: 14font.weight: Font.Normal}Label {id: label6x: 476y: 192width: 23height: 25text: qsTr("160")horizontalAlignment: Text.AlignHCenterfont.pointSize: 14font.weight: Font.Normal}Label {id: label7x: 172y: 123width: 23height: 25text: qsTr("60")horizontalAlignment: Text.AlignHCenterfont.pointSize: 14font.weight: Font.Normal}Label {id: label8x: 445y: 123width: 35height: 25text: qsTr("140")horizontalAlignment: Text.AlignHCenterfont.pointSize: 14font.weight: Font.Normal}Label {id: label9x: 236y: 73width: 23height: 25text: qsTr("80")horizontalAlignment: Text.AlignHCenterfont.pointSize: 14font.weight: Font.Normal}Label {id: label10x: 382y: 72width: 36height: 25text: qsTr("120")horizontalAlignment: Text.AlignHCenterfont.pointSize: 14font.weight: Font.Normal}Label {id: label11x: 310y: 62width: 23height: 25text: qsTr("100")horizontalAlignment: Text.AlignHCenterfont.pointSize: 14font.weight: Font.Normal}}

Mycar.qml

// file - Mycar.qmlimport QtQuick 2.0Item {id: carItemwidth: 100height: 100// 背景圆弧线宽property int btm_lineWidth: 15// 背景圆弧颜色property color btm_backgroundColor: Qt.rgba(0, 0, 0, 0.1);// 背景圆弧半径 开始角度 结束角度property int btm_r: 20property double btm_startAngle: 0property double btm_endAngle: 90onBtm_lineWidthChanged: canvas.requestPaint()onBtm_backgroundColorChanged: canvas.requestPaint()onBtm_rChanged: canvas.requestPaint()onBtm_startAngleChanged: canvas.requestPaint()onBtm_endAngleChanged: canvas.requestPaint()// 顶层圆弧线宽property int top_lineWidth: 10// 顶层圆弧颜色property color top_backgroundColor: "lightgreen"// 顶层圆弧半径 开始角度 结束角度property int top_r: 20property double top_startAngle: 0property double top_endAngle: 90onTop_lineWidthChanged: canvas.requestPaint()onTop_backgroundColorChanged: canvas.requestPaint()onTop_rChanged: canvas.requestPaint()onTop_startAngleChanged: canvas.requestPaint()onTop_endAngleChanged: canvas.requestPaint()// 刻度盘property color dial_color: "#000000"property int dial_lineWidth: 3property int dial_addR: 2          // 通过调整该变量可以控制刻度盘圆弧与底层圆弧的距离property int dial_longNum: 5       // 刻度盘长刻度线的数量property int dial_longLen: 10      // 刻度盘长刻度线的长度onDial_colorChanged: canvas.requestPaint()onDial_lineWidthChanged: canvas.requestPaint()onDial_addRChanged: canvas.requestPaint()onDial_longNumChanged: canvas.requestPaint()onDial_longLenChanged: canvas.requestPaint()Canvas {id: canvaswidth: carItem.widthheight: carItem.heightonPaint: {var ctx = getContext("2d");ctx.clearRect(0, 0, canvas.width, canvas.height);// 画背景圆弧ctx.lineWidth = carItem.btm_lineWidth;ctx.strokeStyle = carItem.btm_backgroundColor;ctx.beginPath();ctx.arc(carItem.width/2, carItem.width/2, carItem.btm_r, (carItem.btm_startAngle/180*Math.PI), (carItem.btm_endAngle/180*Math.PI));ctx.stroke();// 画大刻度盘ctx.lineWidth = carItem.dial_lineWidth;ctx.strokeStyle = carItem.dial_color;ctx.beginPath();ctx.arc(carItem.width/2, carItem.width/2, carItem.btm_r+carItem.btm_lineWidth+carItem.dial_addR, (carItem.btm_startAngle/180*Math.PI), (carItem.btm_endAngle/180*Math.PI));var tmp_step = (carItem.btm_endAngle-carItem.btm_startAngle)/carItem.dial_longNum;for(var i=carItem.btm_startAngle;i<carItem.btm_endAngle+tmp_step;i+=tmp_step) {var tmp_x = (carItem.width/2)+(carItem.btm_r+carItem.btm_lineWidth+carItem.dial_addR)*Math.cos(i/180*Math.PI);var tmp_y = (carItem.width/2)+(carItem.btm_r+carItem.btm_lineWidth+carItem.dial_addR)*Math.sin(i/180*Math.PI);ctx.moveTo(tmp_x, tmp_y);// 绘制长刻度线ctx.lineTo(tmp_x+carItem.dial_longLen*Math.cos(i/180*Math.PI), tmp_y+(carItem.dial_longLen*Math.sin(i/180*Math.PI)));}ctx.stroke();// 画顶层圆弧ctx.lineWidth = carItem.top_lineWidth;ctx.strokeStyle = carItem.top_backgroundColor;ctx.beginPath();ctx.arc(carItem.width/2, carItem.width/2, carItem.top_r, (carItem.top_startAngle/180*Math.PI), (carItem.top_endAngle/180*Math.PI));ctx.stroke();}}
}

工程包:https://download.csdn.net/download/t01051/12654094

这篇关于qml绘制仪表盘控件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/800906

相关文章

【WebGPU Unleashed】1.1 绘制三角形

一部2024新的WebGPU教程,作者Shi Yan。内容很好,翻译过来与大家共享,内容上会有改动,加上自己的理解。更多精彩内容尽在 dt.sim3d.cn ,关注公众号【sky的数孪技术】,技术交流、源码下载请添加微信号:digital_twin123 在 3D 渲染领域,三角形是最基本的绘制元素。在这里,我们将学习如何绘制单个三角形。接下来我们将制作一个简单的着色器来定义三角形内的像素

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

利用matlab bar函数绘制较为复杂的柱状图,并在图中进行适当标注

示例代码和结果如下:小疑问:如何自动选择合适的坐标位置对柱状图的数值大小进行标注?😂 clear; close all;x = 1:3;aa=[28.6321521955954 26.2453660695847 21.69102348512086.93747104431360 6.25442246899816 3.342835958564245.51365061796319 4.87

lvgl8.3.6 控件垂直布局 label控件在image控件的下方显示

在使用 LVGL 8.3.6 创建一个垂直布局,其中 label 控件位于 image 控件下方,你可以使用 lv_obj_set_flex_flow 来设置布局为垂直,并确保 label 控件在 image 控件后添加。这里是如何步骤性地实现它的一个基本示例: 创建父容器:首先创建一个容器对象,该对象将作为布局的基础。设置容器为垂直布局:使用 lv_obj_set_flex_flow 设置容器

qml states 状态

states 状态 在QML中,states用于定义对象在不同状态下的属性变化。每个状态可以包含一组属性设置,当状态改变时,这些属性设置会被应用到对象上。 import QtQuick 2.15import QtQuick.Controls 2.15// 定义应用程序的主窗口ApplicationWindow {visible: true // 使窗口可见width: 640 /

小程序button控件上下边框的显示和隐藏

问题 想使用button自带的loading图标功能,但又不需要button显示边框线 button控件有一条淡灰色的边框,在控件上了样式 border:none; 无法让button边框隐藏 代码如下: <button class="btn">.btn{border:none; /*一般使用这个就是可以去掉边框了*/} 解决方案 发现button控件有一个伪元素(::after

QML入门之基本元素

元素分为可视元素与非可视元素,可能元素例如Rectangle、Button等。非可视元素如Timer(定时器)、MouseArea(鼠标区域)等。非可视元素一般用于操作可视元素。 基础元素 Item Item(基础元素对象)是所有可视元素的基础对象,它们都继承自Item。可是元素存在以下共有属性。 Group(分组)Properties(属性)Geometry(几何属性)x

MFC中Spin Control控件使用,同时数据在Edit Control中显示

实现mfc spin control 上下滚动,只需捕捉spin control 的 UDN_DELTAPOD 消息,如下:  OnDeltaposSpin1(NMHDR *pNMHDR, LRESULT *pResult) {  LPNMUPDOWN pNMUpDown = reinterpret_cast(pNMHDR);  // TODO: 在此添加控件通知处理程序代码    if

MFC 控件重绘(2) NM_CUSTOMDRAW, WM_DRAWITEM, 虚函数DrawItem

控件重绘有三种方法: 1 设定界面属性 2 利用Windows的消息机制,通过Windows消息映射(Message Mapping)和反映射(Message Reflecting),在合适的时机修改控件的状态和行为。此方式涉及NM_CUSTOMDRAW和WM_DRAWITEM 3 利用虚函数机制,重载虚函数。即DrawItem虚函数。 对于NM_CUSTOMDRAW,某些支持此消息的控件

YOLOv8/v10+DeepSORT多目标车辆跟踪(车辆检测/跟踪/车辆计数/测速/禁停区域/绘制进出线/绘制禁停区域/车道车辆统计)

01:YOLOv8 + DeepSort 车辆跟踪 该项目利用YOLOv8作为目标检测模型,DeepSort用于多目标跟踪。YOLOv8负责从视频帧中检测出车辆的位置,而DeepSort则负责关联这些检测结果,从而实现车辆的持续跟踪。这种组合使得系统能够在视频流中准确地识别并跟随特定车辆。 02:YOLOv8 + DeepSort 车辆跟踪 + 任意绘制进出线 在此基础上增加了用户