最近想在Qml中实现绘制实时曲线,一开始想到了好用且强大的[*QCustomPlot][1],但伤心的发现其在Qml中无法使用。因为其是基于QWidget开发而来的第三方库,所以只好用Qt自带的Chart来在Qml中画曲线了。
而且Chart的画图功能也很是炫酷
Qt的chart可以用在 QWidgets, QGraphicsWidget, or QML 工程中。如果在Qml文件中使用chart需要导入 如下包
import QtCharts 2.2
如果在C++类中使用Qt Charts ,则需要使用include和using
#include <QtCharts>
using namespace QtCharts;
在Qml文件中使用Chart,有一点需要特别注意。就是需要替换qml模板中的QGuiApplication为QApplication,因为Chart需要依赖于Qt的 Graphics View Framework来渲染。
最后还需要在qmake project文件中添加 QT += charts
Page {
width: 600
height: 400
ChartView {
id:chartView
title: "Spline"
anchors.fill:parent;
theme: ChartView.ChartThemeBlueNcs
antialiasing: true
animationOptions:SeriesAnimations
ValueAxis {
id: axisX
min: 0
max: 10
tickCount:50
}
ValueAxis {
id: axisY
min: -1.5
max: 2.5
}
SplineSeries {
id:spline1;
name: "SplineSeries"
axisX: axisX
axisY: axisY
color:"green"
width:2
capStyle:"RoundCap";
style :"DashLine"
XYPoint { x: 0; y: 0.19 }
XYPoint { x: 1; y: 2.0 }
XYPoint { x: 2; y: 0.8 }
XYPoint { x: 3; y: 1.5 }
XYPoint { x: 5; y: 0.5 }
XYPoint { x: 7; y: 2.3 }
}
}
}
在C++类中定义信号
signals:
void dataReady(qreal x , qreal y);
在Qml文件中定义信号处理器
Connections{
target:MAVLinkProtocol;
onDataReady:{
spline1.append(x,y);
//console.log("x:"+x.toString()+" "+"y:"+y.toString());
}
需要注意
1.信号处理器onDataReady 是on+信号首字母大写
2.在信号处理器中可直接应用C++中信号声明时的参数名字,这里是 x和y
3.这里曲线当超出axisX .max 属性的值时将不会更新了
onDataReady:{
spline1.append(x,y);
if(scatter1.count>40) { //这里40仅用于测试
chartView.scrollRight(1);
}
//console.log("x:"+x.toString()+" "+"y:"+y.toString());
}
一开始不知道怎么实现滚动条的效果,惊奇的发现ChartView QML Type 有个scrollRight(real pixels)方法,很是开心。
但是实际运行起来很是卡顿,单单一个exe的CPU占用达到20%,说明Scroll效率极低,而且这里参数像素,和想要移动的距离是什么关系不知道。
onDataReady:{
spline1.append(x,y);
if(x>axisX.max){
axisX.min = axisX.min + (x-axisX.max);
axisX.max = x;
//axisX.min = axisX.min + (x-axisX.max)+0.2;//0.2是往左侧偏移一点,便于观察最右侧点
//axisX.max = x+0.2;
}
// console.log("x:"+MAVLinkProtocol.xQml.toString()+" "+"y:"+MAVLinkProtocol.yQml.toString());
}
实际运行起来,还是很卡顿,说明可能并不是Scroll导致的卡顿而是append效率低下。
signals:
void dataReady(QVector<QPointF> pointsBuffer);
private slots:
void handleDataGeneratorTimeout();
private:
static const int sampleCount = 5; //测试取5
QTimer *myDataGeneratorTimer;
qreal x=0;
qreal y=0;
QVector<QPointF> pointsVector;
在构造函数中初始化Timer ,连接到相应槽,并启动,槽函数如下
void MAVLinkProtocol::handleDataGeneratorTimeout()
{
x+=0.2; //生成实时数据点
y= sin(x);
if (pointsVector.isEmpty()) {
pointsVector.reserve(sampleCount);
for (int i = 0; i < sampleCount; ++i)
pointsVector.append(QPointF(i, 0));
}
int start = 0;
const int availableSamples = 1;
if (availableSamples < sampleCount) {
start = sampleCount - availableSamples;
for (int s = 0; s < start; s++)
{
pointsVector[s].setY(pointsVector.at(s + availableSamples).y());
pointsVector[s].setX(pointsVector.at(s + availableSamples).x());
}
}
for (int s = start; s < sampleCount; s++)
{
pointsVector[s].setY(y);
pointsVector[s].setX(x);
}
// qDebug()<
emit dataReady(pointsVector);
}
这里的槽函数参考Chart的例程Audio Example , pointsVector[s]类似于链表,每次更新availableSamples 个点。
这里sampleCount取5,总共5个点,每次更新一个点,实际情况画图便知。下面便来到Qml的大坑……
onDataReady:{
spline1.replace(pointsBuffer)
}
运行起来后报错
qrc:/Page2Form.ui.qml:54: Error: Unable to determine callable overload. Candidates are:
replace(int,double,double)
replace(double,double,double,double)
竟然不能调用重载,Qml竟然没有这个函数的重载……
后来一想onDataReady带来的C++中的QVector类型变量,这在Qml中是没有的,不可能有这个重载……
MD,Qml写界面是漂亮,但缺少很多C++的东西,还是用QtWidget 拥抱C++吧,说多都是泪……
如果您觉得本文对您有些许帮助,请小小打赏一下作者,我会更加有动力写出高质量Qt 、QGC、 PX4开发的文章
[1][https://www.qcustomplot.com/]