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(), QVector());

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  ("DataModel", 1, 0, "ChartModel_Test");

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

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 

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 &keys, const QVector &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 &keys, const QVector &values)
{
getPlot()->graph(0)->setData(keys, values);
getPlot()->replot();
}

void QCustomLine::clearAll()
{
getPlot()->graph(0)->setData(QVector(), QVector());
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 tX;
QVector tY;
tX << 1 << 3 << 4;
tY << 1 << 5 << 1;
replace(tX, tY);
}

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

异常记录:

1) 如下异常提示

 

20200511-01 基于 QCustomPlot 移植到 QML 上(qt.514)_第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)在嵌入式等资源受限设备中出现,异常不知原因内存溢出死机情况

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

你可能感兴趣的:(Qt/QML)