20200511-01 基于 QCustomPlot 移植到 QML 上(qt.514)

源码下载

一:要点说明

1. replot() 界面刷新(需要单独调用)

replot() 这个函数的作用是用于刷新显示界面,根据文档显示,刷新的方式总共有 4 中:

rpImmediateRefresh 立即全局更新(先立即重绘表格,然后调用 QWidget::repaint() 重绘整个 widget

rpQueuedRefresh 依次更新(先立即重绘表格,然后调用 QWidget::update() 进行更新,避免多次 repaint() 消耗资源)

rpRefreshHint (默认)取决于 hint 是否被设置为 QCP::phImmediaRefresh,可以查看 setPlottingHints)

rpQueuedReplot 整个更新事件循环进行,以避免没有意义的重复更新

所以在作者使用中,采用 300ms 执行一次 replot(QCustomPlot::RefreshPriority::rpQueuedReplot) 进行界面更新

在测试中,IMX6UL, 500M CPU 资源占用率(不是特别高,没有单独计算仅在个人写的APP上为40-50之间波动)

 

2. 清除函数

似乎没有找到单独可以调用的函数,所以使用 setData() 函数代替

void clear_all()

{

//getPlot() 就是用来获取 QCustomPlot 指针

getPlot()->graph(0)->setData(QVector<double>(), QVector<double>());

getPlot()->replot();

}

3. 单独加点

void append_data(const double& key, const double& val)

{

getPlot()->graph(0)->addData(key, val);

}

 

4. QQuickPaintItem

提供了在 QML 上绘制的 QPainter API,可以将 QWidget 或者自定义控件绘制到 QML 界面上,需要实现 paint() , 如果需要在控件上实现其他功能,比如鼠标功能,可以具体查看 QQuickItem 类中的虚拟函数定义


二:源码 (代码格式参考网上信息)

//ProFile
#需要添加
QT += quick qml printsupport core

//main.cpp
QApplication app(argc, argv); //使用这个来代替 QCoreApplication 等默认创建的 application
qmlRegisterType <QCustomLine> ("DataModel", 1, 0, "ChartModel_Test");

//main.qml
注意使用 ApplicationWindow{} 来代替 Window()
// qcustomchart.h 
#include <QtQuick>

class QCustomPlot;
class QCPAbstractPlottable;

class QCustomChart : public QQuickPaintedItem
{
Q_OBJECT
public:
explicit QCustomChart(QQuickItem *parent = nullptr);
virtual ~QCustomChart();

void paint(QPainter *painter) override;
virtual void initChartUI(){};

QCustomPlot *getPlot();

protected:

virtual void mousePressEvent( QMouseEvent* event ) override;
virtual void mouseReleaseEvent( QMouseEvent* event ) override;
virtual void mouseMoveEvent( QMouseEvent* event ) override;
virtual void mouseDoubleClickEvent( QMouseEvent* event ) override;
virtual void wheelEvent( QWheelEvent *event ) override;

void routeMouseEvents( QMouseEvent* event );
void routeWheelEvents( QWheelEvent* event );

public slots:
void onChartViewReplot(); 
void updateChartViewSize();  
private: 
    QCustomPlot* mCustomPlot {nullptr}; 
};

//qcustomchart.cpp
#include "qcustomchart.h"
#include "qcustomplot.h"
#include <QDebug>

QCustomChart::QCustomChart(QQuickItem *parent) : QQuickPaintedItem(parent),
mCustomPlot(new QCustomPlot())
{
setFlag(QQuickItem::ItemHasContents, true);
setAcceptedMouseButtons(Qt::AllButtons);
setAcceptHoverEvents(true);

connect(this, &QQuickPaintedItem::widthChanged, this, &QCustomChart::updateChartViewSize);
connect(this, &QQuickPaintedItem::heightChanged, this, &QCustomChart::updateChartViewSize);

}

QCustomChart::~QCustomChart()
{
delete mCustomPlot;
}

void QCustomChart::paint(QPainter *painter)
{
/// add active check
if (!painter->isActive())
return;
QPixmap    picture( boundingRect().size().toSize() );
QCPPainter qcpPainter( &picture );
mCustomPlot->toPainter(&qcpPainter);
painter->drawPixmap(QPoint(), picture);
}

QCustomPlot *QCustomChart::getPlot()
{
return mCustomPlot;
}



void QCustomChart::mousePressEvent(QMouseEvent *event)
{
routeMouseEvents(event);
}

void QCustomChart::mouseReleaseEvent(QMouseEvent *event)
{
routeMouseEvents(event);
}

void QCustomChart::mouseMoveEvent(QMouseEvent *event)
{
routeMouseEvents(event);
}

void QCustomChart::mouseDoubleClickEvent(QMouseEvent *event)
{
routeMouseEvents(event);
}

void QCustomChart::wheelEvent(QWheelEvent *event)
{
routeWheelEvents( event );
}


void QCustomChart::updateChartViewSize()
{
mCustomPlot->setGeometry(0, 0, (int)width(), (int)height());
mCustomPlot->setViewport(QRect(0, 0, (int)width(), (int)height()));
}

void QCustomChart::onChartViewReplot()
{
update();
}

void QCustomChart::routeMouseEvents(QMouseEvent *event)
{
QMouseEvent* newEvent = new QMouseEvent(event->type(), event->localPos(), event->button(), event->buttons(), event->modifiers());
QCoreApplication::postEvent(mCustomPlot, newEvent);
}

void QCustomChart::routeWheelEvents(QWheelEvent *event)
{
QWheelEvent* newEvent = new QWheelEvent(event->pos(), event->delta(), event->buttons(), event->modifiers(), event->orientation());
QCoreApplication::postEvent(mCustomPlot, newEvent);
}

//qcustomline.h
#include "qcustomchart.h"

class QCustomLine : public QCustomChart
{
Q_OBJECT
public:
explicit QCustomLine(QQuickItem *parent = nullptr);
virtual ~QCustomLine();
Q_INVOKABLE void initChart();

Q_INVOKABLE void appendData(const double &key, const double &value);
Q_INVOKABLE void replace(const QVector<double> &keys, const QVector<double> &values);
Q_INVOKABLE void clearAll();
Q_INVOKABLE void setXYRange(int x_s, int x_e, int y_s, int y_e);
virtual void timerEvent(QTimerEvent *event) override;

Q_INVOKABLE void append_test();
Q_INVOKABLE void replace_test();
Q_INVOKABLE void updateUI();

};
//qcustomline.cpp
#include "qcustomline.h"
#include "qcustomplot.h"

QCustomLine::QCustomLine(QQuickItem *parent) : QCustomChart(parent)
{

}

QCustomLine::~QCustomLine()
{

}

void QCustomLine::initChart()
{
updateChartViewSize();

getPlot()->addGraph();
getPlot()->graph(0)->setPen( QPen(Qt::red));
getPlot()->xAxis->setLabel( "t1" );
getPlot()->yAxis->setLabel( "S1" );
getPlot()->xAxis->setRange( 0, 1000 );
getPlot()->yAxis->setRange( 0, 10 );
getPlot()->setInteractions( QCP::iRangeDrag | QCP::iRangeZoom );

startTimer(1000);

connect(getPlot(), &QCustomPlot::afterReplot, this, &QCustomLine::onChartViewReplot);

//    getPlot()->replot();
}

void QCustomLine::appendData(const double &key, const double &value)
{
getPlot()->graph(0)->addData(key, value);
}

void QCustomLine::replace(const QVector<double> &keys, const QVector<double> &values)
{
getPlot()->graph(0)->setData(keys, values);
getPlot()->replot();
}

void QCustomLine::clearAll()
{
getPlot()->graph(0)->setData(QVector<double>(), QVector<double>());
getPlot()->replot();
}

void QCustomLine::setXYRange(int x_s, int x_e, int y_s, int y_e)
{
getPlot()->xAxis->setRange(x_s, x_e);
getPlot()->yAxis->setRange(y_s, y_e);
getPlot()->replot();
}

void QCustomLine::timerEvent(QTimerEvent */*event*/)
{
static double t, U;
U = ((double)rand() / RAND_MAX) * 5;
getPlot()->graph(0)->addData(t++, U);
getPlot()->replot();
}

void QCustomLine::append_test()
{
static double t, U;
U = ((double)rand() / RAND_MAX) * 5;
appendData(t++, U);
}

void QCustomLine::replace_test()
{
QVector<double> tX;
QVector<double> tY;
tX << 1 << 3 << 4;
tY << 1 << 5 << 1;
replace(tX, tY);
}

void QCustomLine::updateUI()
{
getPlot()->replot(QCustomPlot::RefreshPriority::rpQueuedReplot);
}

异常记录:

1) 如下异常提示

 

如果出现这种异常提示:

QPainter::begin: Paint device returned engine = 0, type: 2

QPainter::setRenderHint: Painter must be active to set rendering hints

 

请检查,

一:QML 是否已经用 ApplicationWindow 代替 Window 控件

二:在使用中,作者是通过 loader 加载的控件,并且在启动之后默认调用了 replot() 根本原因在于时间差,paint device 并没有准备好,所以可以略微延时执行,在构造函数中不要调用 replot()

 

2)在嵌入式等资源受限设备中出现,异常不知原因内存溢出死机情况

很有可能是由于过快过多的图表刷新导致,内存资源耗尽导致程序嗝屁,建议跟新界面刷新策略,比如降低界面刷新的频率,去掉没有意义的界面刷新步骤

  • 7
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
基于QtQuick的QCustomPlot是一种用于在Qt Quick应用程序中绘制图形和图表的库。QCustomPlot提供了一组用于绘制线条、散点、柱状图等的函数和类,可以轻松地在Qt Quick应用程序中创建交互式和可视化的图表。 在QML中实现QCustomPlot可以按照以下步骤进行: 首先,确保已将QCustomPlot库引入到项目中。可以通过将其作为外部库链接到项目,或直接将其文件复制到项目目录中来实现。确保在QML文件中包含必要的引用。 然后,在QML文件中,创建一个独立的Item或一个自定义的QQuickItem来容纳QCustomPlot。可以使用一个矩形来表示图表区域。 接下来,为QCustomPlot创建一个自定义的属性,用于设置和更新图表数据。这可以是一个JavaScript对象,其中包含需要显示的数据。可以使用这些数据来更新QCustomPlot的属性,例如曲线或散点的坐标。 然后,在自定义的QQuickItem中,创建一个QCustomPlot实例,并将其添加到QQuickItem的内容中。 在QQuickItem的内容中,可以使用Rectangle、PathLine、Circle等基本的Qt Quick绘图元素来绘制坐标轴、曲线、散点和其他图表元素。根据图表数据的更新,可以重新绘制这些元素以反映新的数据。 最后,可以为自定义的QQuickItem创建信号和槽函数,使其与其他QML组件进行交互。例如,当鼠标在图表中移动时,可以通过鼠标事件捕获鼠标位置,并将其转换为数据坐标。 通过这些步骤,可以基于QtQuick的QCustomPlot实现一个功能强大的图表库,可以轻松地在QML应用程序中绘制各种类型的图表。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值