使用QML绘制仪表
Qt看了一段时间了,一直觉得适宜做仪表等显示界面。对于QML也只是看看网上的代码,不求甚解。现在模仿网友的例子,自己亲手试一试,记录一下过程,也是在这里的第一篇文章。
本文参考 https://blog.csdn.net/t01051/article/details/107518308
Here we go!
新建QML工程
新建一个空的Quick应用。
完成后,得到一个HelloWorld的空项目。
添加自定义控件
添加一个.qml文件,用来定义自己的时速表,然后在main.qml中导入使用。
在工程树的多个位置都可使用Add New来添加新的文件,添加的文件会默认放在当前位置下。这里在Resources/qml.qrc/下添加
定义控件
这里编写qml文件了。
根据所引文章,首先绘制了两个重叠的圆弧。使用property定义控件的必要属性,并使用onXxxxxxChanged(格式:onChanged)的信号处理器,当某个属性的值变更时,会触发Canvas的重绘(即mycanvas.requestPaint())
//Mymeter.qml
import QtQuick 2.0
Item {
id: meterItem
width: 100
height: 100
property int btm_lineWidth: 15
property color btm_bgcolor: Qt.rgba(0,0,0,0.1)
property int btm_r: 20
property double btm_startangle: 0
property double btm_endangle: 90
onBtm_bgcolorChanged:mycanvas.requestPaint()
onBtm_endangleChanged: mycanvas.requestPaint()
onBtm_lineWidthChanged: mycanvas.requestPaint()
onBtm_rChanged: mycanvas.requestPaint()
onBtm_startangleChanged: mycanvas.requestPaint()
property int top_lineWidth: 15
property color top_bgcolor: "lightgreen"
property int top_r: 20
property double top_startangle: 0
property double top_endangle: 90
onTop_bgcolorChanged: mycanvas.requestPaint()
onTop_endangleChanged: mycanvas.requestPaint()
onTop_lineWidthChanged: mycanvas.requestPaint()
onTop_startangleChanged: mycanvas.requestPaint()
onTop_rChanged: mycanvas.requestPaint()
Canvas{
id: mycanvas
width: parent.width
height: parent.height
onPaint: {
var ctx = getContext("2d");
ctx.clearRect(0,0,mycanvas.width,mycanvas.height);
ctx.lineWidth = meterItem.btm_lineWidth;
ctx.strokeStyle = meterItem.btm_bgcolor;
ctx.beginPath();
ctx.arc(meterItem.width/2,meterItem.width/2,meterItem.btm_r,meterItem.btm_startangle/180*Math.PI,meterItem.btm_endangle/180*Math.PI);
ctx.stroke();
ctx.lineWidth = meterItem.top_lineWidth;
ctx.strokeStyle = meterItem.top_bgcolor;
ctx.beginPath();
ctx.arc(meterItem.width/2,meterItem.width/2,meterItem.top_r,meterItem.top_startangle/180*Math.PI,meterItem.top_endangle/180*Math.PI);
ctx.stroke();
}
}
}
在main.qml中引用自定义的仪表控件
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Mymeter{
}
}
切换到设计界面,自定义的仪表控件已经被载入到界面中,点击导航下方的Mymeter类,右方可以看到其属性,更改自定义属性,可以直接看到更改效果。
同时,在main.qml中,Mymeter的属性已经更新。
添加刻度盘
在仪表上绘制表盘,首先定义表盘的一些属性,然后进行绘制
...
// 刻度盘
property color dial_color: "#000000"
property int dial_lineWidth: 3
property int dial_addR: 2 // 刻度盘圆弧与底层圆弧的距离
property int dial_longNum: 5 // 刻度盘长刻度线的数量
property int dial_longLen: 10 // 刻度盘长刻度线的长度
onDial_addRChanged: mycanvas.requestPaint()
onDial_colorChanged: mycanvas.requestPaint()
onDial_lineWidthChanged: mycanvas.requestPaint()
onDial_longLenChanged: mycanvas.requestPaint()
onDial_longNumChanged: mycanvas.requestPaint()
...
Canvas{
...
onPaint: {
...
ctx.lineWidth = meterItem.dial_lineWidth;
ctx.strokeStyle = meterItem.dial_color;
ctx.beginPath();
ctx.arc(meterItem.width/2, meterItem.height/2, meterItem.btm_r+meterItem.btm_lineWidth+meterItem.dial_addR, meterItem.btm_startangle/180*Math.PI, meterItem.btm_endangle/180*Math.PI);
var tmp_step = (meterItem.btm_endangle-meterItem.btm_startangle)/meterItem.dial_longNum;
for(var i=meterItem.btm_startangle;i<meterItem.btm_endangle+tmp_step;i+=tmp_step) {
var tmp_x = (meterItem.width/2)+(meterItem.btm_r+meterItem.btm_lineWidth+meterItem.dial_addR)*Math.cos(i/180*Math.PI);
var tmp_y = (meterItem.height/2)+(meterItem.btm_r+meterItem.btm_lineWidth+meterItem.dial_addR)*Math.sin(i/180*Math.PI);
ctx.moveTo(tmp_x, tmp_y);
// 绘制长刻度线
ctx.lineTo(tmp_x+meterItem.dial_longLen*Math.cos(i/180*Math.PI), tmp_y+(meterItem.dial_longLen*Math.sin(i/180*Math.PI)));
}
ctx.stroke();
...
}
添加刻度
直接使用basic控件,设置好位置,文字等。
添加控件
为仪表盘添加几个控件,一个开关用来控制顶层圆弧的线宽,滑块用来模拟汽车的速度,文本框用来显示速度。。。
代码
main.qml完整代码如下
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.3
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Mymeter{
id: meter_car
x: 195
y: 49
width: 228
height: 213
dial_longNum: 10
top_startangle: 140
btm_startangle: 140
btm_r: 80
top_r: 80
top_lineWidth: 15
top_endangle: slider.value*1.3+140
btm_lineWidth: 13
btm_endangle: 400
Text {
id: element5
x: 106
y: -11
width: 12
height: 13
text: qsTr("100")
font.pixelSize: 12
}
Text {
id: element6
x: 156
y: -1
width: 13
height: 14
text: qsTr("120")
font.pixelSize: 12
}
Text {
id: element7
x: 194
y: 28
width: 12
height: 14
text: qsTr("140")
font.pixelSize: 12
}
Text {
id: element9
x: 218
y: 77
width: 12
height: 14
text: qsTr("160")
font.pixelSize: 12
}
Text {
id: element12
x: 23
y: 171
width: 12
height: 14
text: qsTr("0")
font.pixelSize: 12
}
Text {
id: speed_unit
x: 102
y: 112
width: 22
height: 13
text: qsTr("Km/h")
font.pixelSize: 12
}
Text {
id: speed
x: 101
y: 94
width: 22
height: 12
text: slider.value
font.pixelSize: 15
horizontalAlignment: Text.AlignHCenter
}
Slider {
id: slider
x: 12
y: 218
stepSize: 1
to: 200
value: 0
onValueChanged: {
if(value<60) {
speed.color = "black"
}
else if(value<120) {
speed.color = "#f2ac28"
}
else {
speed.color = "red"
}
speed_unit.color = speed.color
}
}
Switch {
id: swi
x: 251
y: -38
width: 101
height: 40
text: qsTr("Switch")
onClicked: {
if(swi.position)
meter_car.top_lineWidth = meter_car.btm_lineWidth*0.6;
else
meter_car.top_lineWidth = meter_car.btm_lineWidth;
}
}
Text {
id: element
x: 15
y: 37
width: 12
height: 14
text: qsTr("60")
font.pixelSize: 12
}
Text {
id: element1
x: -1
y: 132
width: 12
height: 14
text: qsTr("20")
font.pixelSize: 12
}
Text {
id: element2
x: -5
y: 82
width: 12
height: 14
text: qsTr("40")
font.pixelSize: 12
}
Text {
id: element4
x: 54
y: 0
width: 18
height: 14
text: qsTr("80")
font.pixelSize: 12
}
Text {
id: element10
x: 218
y: 127
width: 12
height: 14
text: qsTr("180")
font.pixelSize: 12
}
Text {
id: element11
x: 199
y: 171
width: 12
height: 14
text: qsTr("200")
font.pixelSize: 12
}
}
}
后续
后续完善代码,增加C++交互。