这里在QML中使用QCustomPlot是定义一个继承自QQuickPaintedItem的类,它包含一个QCustomPlot对象,在paint函数中将这个对象转化为pixmap绘制到布局中显示。
在QML中使用QT的Widget控件也可以借鉴这个思路实现
顺便记录一下QCustomPlot的简单设置与使用。
QCustomPlot可以直接到官网下载源码。
main.cpp中添加代码,将C++类注册为QML组件
#include "plot/liquidheightplot.h"
...
int main(int argc, char *argv[])
{
...
qmlRegisterType("LiquidHeightPlot", 1, 0, "LiquidHeightPlot");
...
}
类定义liquidheightplot.h
#ifndef LIQUIDHEIGHTPLOT_H
#define LIQUIDHEIGHTPLOT_H
#include "qcustomplot/qcustomplot.h"
#include
#include
#include
#include
class LiquidHeightPlot : public QQuickPaintedItem
{
Q_OBJECT
public:
explicit LiquidHeightPlot(QQuickItem *parent = nullptr);
~LiquidHeightPlot();
//更新实时数据
Q_INVOKABLE void updateRealData(double value);
//设置历史数据
Q_INVOKABLE void setHistoryData(QJsonObject data);
//清除数据
Q_INVOKABLE void clearData();
signals:
void sigIllegalData();
private:
virtual void paint(QPainter *painter);
private:
QCustomPlot *m_customPlot;
double m_maxY;
QVector m_x1;
QVector m_y1;
};
#endif // LIQUIDHEIGHTPLOT_H
类实现liquidheightplot.cpp
#include "liquidheightplot.h"
#include
#include
LiquidHeightPlot::LiquidHeightPlot(QQuickItem *parent)
: QQuickPaintedItem(parent)
, m_customPlot(nullptr)
{
m_customPlot = new QCustomPlot();
m_customPlot->rescaleAxes(true);
//设置轴 ============================
QFont font;
font.setPixelSize(16);
m_customPlot->xAxis->setLabelFont(font);
m_customPlot->yAxis->setLabelFont(font);
m_customPlot->xAxis->setLabel(tr("时间"));
m_customPlot->yAxis->setLabel(tr("液位高度(cm)"));
m_customPlot->xAxis->setLabelColor(QColor(Qt::gray));//轴标体色
m_customPlot->yAxis->setLabelColor(QColor(Qt::gray));
m_customPlot->xAxis->setBasePen(QPen(QColor(Qt::gray), 1));//轴色
m_customPlot->yAxis->setBasePen(QPen(QColor(Qt::gray), 1));
m_customPlot->xAxis->setTickPen(QPen(QColor(Qt::gray), 1));//轴主标色
m_customPlot->yAxis->setTickPen(QPen(QColor(Qt::gray), 1));
m_customPlot->xAxis->setSubTickPen(QPen(QColor(Qt::gray), 1));//轴次标色
m_customPlot->yAxis->setSubTickPen(QPen(QColor(Qt::gray), 1));
font.setPixelSize(14);
m_customPlot->xAxis->setTickLabelFont(font);
m_customPlot->yAxis->setTickLabelFont(font);
m_customPlot->xAxis->setTickLabelColor(QColor(Qt::gray));//轴标文本色
m_customPlot->yAxis->setTickLabelColor(QColor(Qt::gray));
m_customPlot->setBackground(QBrush(QColor(Qt::white)));
m_customPlot->setGeometry(0, 0, width()*1.6, height()*1.6);
m_customPlot->setMultiSelectModifier(Qt::KeyboardModifier::ControlModifier);
//设置边距
QCPMarginGroup *marginGroup = new QCPMarginGroup(m_customPlot);
m_customPlot->plotLayout()->setMargins(QMargins(0, 0, 0, 0));
m_customPlot->axisRect()->setMarginGroup(QCP::msLeft | QCP::msRight | QCP::msTop | QCP::msBottom, marginGroup);
//设置时间轴
QSharedPointer dateTicker(new QCPAxisTickerDateTime);
dateTicker->setDateTimeFormat("yyyyMMdd\nhh:mm");
m_customPlot->xAxis->setTicker(dateTicker);
QString sCurrent = QDateTime::currentDateTime().toString("yyyyMMdd hh:00:00");
QDateTime current = QDateTime::fromString(sCurrent, "yyyyMMdd hh:mm:ss");
m_customPlot->xAxis->setRange(current.toTime_t(), current.toTime_t() + 1800);//默认显示时间轴当前半小时
//纵轴
QSharedPointer ticker(new QCPAxisTicker);
m_customPlot->yAxis->setTicker(ticker);
m_customPlot->yAxis->setRange(0, 200);
//添加数据曲线图形
m_customPlot->addGraph();
m_customPlot->graph(0)->setPen(QPen(Qt::blue));
//显示图例
QCPLegend * legend = m_customPlot->legend;
m_customPlot->legend->setVisible(true);
legend->setFont(font);
legend->setBrush(QColor(Qt::gray));
legend->setTextColor(QColor(Qt::white));
m_customPlot->graph(0)->setName("液位曲线");//设置
clearData();
}
LiquidHeightPlot::~LiquidHeightPlot()
{
m_customPlot->deleteLater();
m_customPlot = nullptr;
}
void LiquidHeightPlot::paint(QPainter *painter)
{
m_customPlot->setGeometry(0,0,this->width()*1.6,this->height()*1.6);
painter->drawPixmap(0,0,this->width(),this->height(), m_customPlot->toPixmap());
}
//更新实时数据
void LiquidHeightPlot::updateRealData(double value)
{
QDateTime current = QDateTime::currentDateTime();
if(m_x1.size() == 0) {//第一帧实时数据
m_customPlot->xAxis->setRange(current.toTime_t(), current.toTime_t() + 1800);
}
if(m_x1.size() > 0 && m_x1.last() == current.toTime_t()) return;//同一时间的数据
while (m_x1.size() >= 30) {//半小时,30个数据
m_x1.takeFirst();
m_y1.takeFirst();
}
m_x1.push_back(current.toTime_t());
m_y1.push_back(value);
if(m_maxY < value) {//更新最大值
m_maxY = value;
m_maxY = (m_maxY / 10 + 1) * 10;
m_customPlot->yAxis->setRange(0, m_maxY);
}
if(m_x1.size() == 30) m_customPlot->xAxis->setRange(m_x1.first(), m_x1.last());//更新轴
m_customPlot->graph(0)->setData(m_x1, m_y1);
m_customPlot->replot();//刷新曲线
update();//刷新显示
}
//设置历史数据
//"data": {
// "maxLiquidLevelHeight": "68,88",
// "minLiquidLevelHeight": "68,88",
// "time": "2023-01-02 22:59:59,2023-01-02 23:59:59",
// }
void LiquidHeightPlot::setHistoryData(QJsonObject data)
{
QString maxLiquidLevelHeight = data["maxLiquidLevelHeight"].toString();
QString times = data["time"].toString();
maxLiquidLevelHeight.remove('\n');
times.remove('\n');
QStringList heightList = maxLiquidLevelHeight.split(",", QString::SkipEmptyParts);
QStringList timeList = times.split(",", QString::SkipEmptyParts);
if (heightList.count() != timeList.count()) {
qDebug() << "LiquidHeightPlot::setHistoryData error heightList.count() != timeList.count() !";
return;
}
for (int i = 0; i < heightList.count(); i++) {
QDateTime time = QDateTime::fromString(timeList[i], "yyyy-MM-dd HH:mm:ss");
qDebug() << "LiquidHeightPlot::setHistoryData time isValid " << time.isValid() << time;
if(!time.isValid()) {//存在非法数据
emit sigIllegalData();
return;
}
uint value = heightList[i].toInt();
m_x1.append(time.toTime_t());
m_y1.append(value);
if(m_maxY < value) {//更新最大值
m_maxY = value;
m_maxY = (m_maxY / 10 + 1) * 10;
m_customPlot->yAxis->setRange(0, m_maxY);
}
}
m_customPlot->xAxis->setRange(m_x1.first(), m_x1.last());
m_customPlot->graph(0)->setData(m_x1, m_y1);
m_customPlot->replot();//刷新曲线
update();//刷新显示
qDebug() << "LiquidHeightPlot::setHistoryData m_x1" << m_x1 << ",\n m_y1" << m_y1;
}
//清除数据
void LiquidHeightPlot::clearData()
{
qDebug() << "LiquidHeightPlot::clearData ---------------------------------";
m_maxY = 200;
m_x1.clear();
m_y1.clear();
m_customPlot->graph(0)->setData(m_x1, m_y1);
m_customPlot->replot();//刷新曲线
update();//刷新显示
}
QML的使用
import LiquidHeightPlot 1.0
Item {
id: root
...
LiquidHeightPlot {
id: liquidHeightPlot
anchors.fill: parent
onSigIllegalData: {
popuTip.text = qsTr("数据异常 !");
popuTip.visible = true;
}
}
...
}