本文主要是介绍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绘制仪表盘控件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!