PowerMaster/dataPanel/dpLineChart.cpp

398 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "dpLineChart.h"
//#define useDefaultAxis_Y //默认轴不可删除、不可左右移动所以做动态管理尤其是动态排列时要考虑的比较复杂可以采用不使用的策略全部以自定义轴替代默认轴yAxis只在初始化和没有实际数据轴自定义时显示用来做外观展示
dpLineChart::dpLineChart(QWidget* parent)
:dpBaseChart(parent)
{
setAttribute(Qt::WA_TranslucentBackground,true);
m_pCustomPlot = new QCustomPlot(this);
initQCP();
m_timeRange = 60 * 1000;
m_axisArrangementMode = AlternateSides;
m_updateData = false;
QBoxLayout* mainLayout = new QBoxLayout(QBoxLayout::LeftToRight);
mainLayout->setContentsMargins(0, 1, 0, 0);
mainLayout->addWidget(m_pCustomPlot);
setLayout(mainLayout);
}
dpLineChart::~dpLineChart()
{
}
void dpLineChart::initQCP()
{
m_chartStyle.bgColor = Qt::transparent;
m_chartStyle.axisColor = QColor(87, 100, 120);
m_chartStyle.labelColor = QColor(250, 250, 250);
m_chartStyle.labelFont = QFont("黑体", 12);
m_chartStyle.tickColor = QColor(87, 100, 120);
m_chartStyle.tickLabelColor = QColor(250, 250, 250);
m_chartStyle.tickLabelFont = QFont("黑体", 12);
m_chartStyle.gridPen = QPen(QColor(87, 100, 120), 1, Qt::DotLine);
//m_pCustomPlot->axisRect()->setupFullAxesBox();
m_pCustomPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
m_pCustomPlot->axisRect()->setRangeDrag(Qt::Horizontal); //只允许x轴方向的拖拽
double zoomFactor = m_pCustomPlot->axisRect()->rangeZoomFactor(Qt::Horizontal);
m_pCustomPlot->axisRect()->setRangeZoomFactor(zoomFactor, 1); //只做x轴的缩放
m_pCustomPlot->xAxis->setSubTicks(false);
m_pCustomPlot->xAxis2->setTicks(false);
m_pCustomPlot->xAxis2->setSubTicks(false);
m_pCustomPlot->yAxis->setSubTicks(false);
//m_pCustomPlot->yAxis->setVisible(false);
//m_pCustomPlot->yAxis2->setTicks(false);
m_pCustomPlot->yAxis2->setSubTicks(false);
m_pCustomPlot->yAxis2->setVisible(false);
connect(m_pCustomPlot->xAxis, qOverload<const QCPRange &>(&QCPAxis::rangeChanged), //rangeChanged有两个版本qOverload可以指定版本
this, &dpLineChart::onSignal_rangeChanged_xAxis);
//背景颜色
m_pCustomPlot->setBackground(QBrush(m_chartStyle.bgColor));
//坐标轴颜色
m_pCustomPlot->xAxis->setBasePen(m_chartStyle.axisColor);
m_pCustomPlot->xAxis2->setBasePen(m_chartStyle.axisColor);
m_pCustomPlot->yAxis->setBasePen(m_chartStyle.axisColor);
m_pCustomPlot->yAxis2->setBasePen(m_chartStyle.axisColor);
//坐标刻度颜色
m_pCustomPlot->xAxis->setTickPen(QPen(m_chartStyle.tickColor));
m_pCustomPlot->yAxis->setTickPen(QPen(m_chartStyle.tickColor));
m_pCustomPlot->yAxis2->setTickPen(QPen(m_chartStyle.tickColor));
//坐标刻度Label颜色
m_pCustomPlot->xAxis->setTickLabelColor(m_chartStyle.tickLabelColor);
m_pCustomPlot->xAxis->setTickLabelFont(m_chartStyle.tickLabelFont);
m_pCustomPlot->yAxis->setTickLabelColor(m_chartStyle.tickLabelColor);
m_pCustomPlot->yAxis->setTickLabelFont(m_chartStyle.tickLabelFont);
m_pCustomPlot->yAxis2->setTickLabelColor(m_chartStyle.tickLabelColor);
m_pCustomPlot->yAxis2->setTickLabelFont(m_chartStyle.tickLabelFont);
//网格线颜色
m_pCustomPlot->xAxis->grid()->setPen(m_chartStyle.gridPen);
m_pCustomPlot->xAxis->grid()->setZeroLinePen(m_chartStyle.gridPen);
//m_pCustomPlot->xAxis2->grid()->setPen(m_chartStyle.gridPen);
m_pCustomPlot->yAxis->grid()->setPen(m_chartStyle.gridPen);
m_pCustomPlot->yAxis->grid()->setZeroLinePen(m_chartStyle.gridPen);
m_pCustomPlot->yAxis2->grid()->setPen(m_chartStyle.gridPen);
//x轴用时间格式
QSharedPointer<QCPAxisTickerDateTime> timeTicker(new QCPAxisTickerDateTime);
timeTicker->setDateTimeFormat("hh:mm:ss:zzz\nyyyy/MM/dd");
//qDebug() << timeTicker->dateTimeFormat();
m_pCustomPlot->xAxis->setTicker(timeTicker);
//qDebug() << m_pCustomPlot->xAxis->range();
}
void dpLineChart::arrangeAxes()
{
QMap<QCPAxis::AxisType, int> axisTypeCounter;
for(int i = 0; i < m_axes.size(); i++)
{
Axis& axis = m_axes[i];
QCPAxis::AxisType position = axis.qAxis->axisType();
if(m_axisArrangementMode == AlternateSides)
position = (i % 2 == 0) ? QCPAxis::atLeft : QCPAxis::atRight;
else if(m_axisArrangementMode == AllRight)
{
if(i == 0)
position = QCPAxis::atLeft;
else
position = QCPAxis::atRight;
}
//更新位置因为QCPAxis没有更新位置的结构只能采用从plot中移除再添加的方式
if(axis.qAxis->axisType() != position)
{
//因为要进行轴的移除再添加工作所以其上相关的graph也要同步处理
QVector<QCPGraph*> affectedGraphs;
for(int j = 0; j < m_pCustomPlot->graphCount(); j++)
{
QCPGraph* graph = m_pCustomPlot->graph(j);
if(graph->valueAxis() == axis.qAxis)
affectedGraphs.append(graph);
}
bool bRemoved = m_pCustomPlot->axisRect()->removeAxis(axis.qAxis);
if(bRemoved) //removeAxis执行成功被删除的axis会被delete
{
QCPAxis* qcpAxis = m_pCustomPlot->axisRect()->addAxis(position);
axis.setQCPAxis(qcpAxis, false);
for(QCPGraph* graph : affectedGraphs)
graph->setValueAxis(qcpAxis);
}
}
//计算偏移量
int offset = axisTypeCounter.value(position, 0) * 30;
axis.qAxis->setOffset(offset);
axisTypeCounter[position] = axisTypeCounter.value(position, 0) + 1;
}
}
void dpLineChart::setTimeRange(TimeUnit unit)
{
switch(unit)
{
case TU_Year:
m_timeRange = m_curDateTime.date().daysInYear() * 24 * 60 * 60 * (qint64)1000;
break;
case TU_Month:
m_timeRange = m_curDateTime.date().daysInMonth() * 24 * 60 * 60 * (qint64)1000;
break;
case TU_Day:
m_timeRange = 24 * 60 * 60 * 1000;
break;
case TU_Hour:
m_timeRange = 60 * 60 *1000;
break;
case TU_Minute_30:
m_timeRange = 30 * 60 *1000;
break;
case TU_Minute_20:
m_timeRange = 20 * 60 *1000;
break;
case TU_Minute_15:
m_timeRange = 15 * 60 *1000;
break;
case TU_Minute_10:
m_timeRange = 10 * 60 *1000;
break;
case TU_Minute_5:
m_timeRange = 5 * 60 *1000;
break;
case TU_Minute_3:
m_timeRange = 3 * 60 *1000;
break;
case TU_Minute_1:
m_timeRange = 60 * 1000;
break;
case TU_Second_30:
m_timeRange = 30 * 1000;
break;
case TU_Second_10:
m_timeRange = 10 * 1000;
break;
case TU_Second_1:
m_timeRange = 1 * 1000;
break;
case TU_MSecond_500:
m_timeRange = 500;
break;
case TU_MSecond_100:
m_timeRange = 100;
break;
case TU_MSecond_50:
m_timeRange = 50;
break;
case TU_MSecond_10:
m_timeRange = 10;
break;
default:
break;
}
}
void dpLineChart::setDateTime(const QDateTime& dateTime)
{
qint64 timeValue = dateTime.toMSecsSinceEpoch() / 1000.0;
//qint64 timeValue = QCPAxisTickerDateTime::dateTimeToKey(dateTime);
m_pCustomPlot->xAxis->setRange(timeValue, m_timeRange / 1000.0, Qt::AlignRight);
m_pCustomPlot->replot();
m_curDateTime = dateTime;
if(m_updateData)
{}
}
void dpLineChart::viewHistoricalData(const QDateTime& dateTime)
{
}
void dpLineChart::synchronizeConfigData(const configurationResults& cfg)
{
m_updateData = false; //停止更新数据
//1.Y坐标轴-数量由数据类型决定
//将最新配置信息中的坐标轴相关数据存储在QHash中有助于更好的判断当前坐标轴是否需要发生同步更新
QHash<RealTimeDataType, AxisConfig> axisCfgMap;
/*for(int i = 0; i< cfg.m_pModel_dataType->rowCount(); i++)
{
AxisConfig axisConfig;
RealTimeDataType dataType = (RealTimeDataType)cfg.m_pModel_dataType->item(i, 0)->data(Qt::UserRole + itemRole_dataType).toInt();
axisConfig.dataType = dataType;
axisCfgMap.insert(dataType, axisConfig);
}*/
for(auto it = cfg.axisCfgMap.begin(); it != cfg.axisCfgMap.end(); ++it)
{
AxisConfig axisConfig;
axisConfig.dataType = it.key();
axisConfig.name = it.value().name;
axisConfig.unit = it.value().unit;
axisCfgMap.insert(it.key(), axisConfig);
}
//QHash::insert()的第二个参数接收的是副本(引起拷贝),所以采用指针可以减少拷贝从而提升效率
QHash<RealTimeDataType, Axis*> axesMap;
for(auto& axis : m_axes)
{
axesMap.insert(axis._cfg.dataType, &axis);
}
//删除轴
for(int i = 0; i < m_axes.size();)
{
#ifdef useDefaultAxis_Y
Axis& axis = m_axes[i];
if(!axisCfgMap.contains(axis._cfg.dataType))
{
if(axis.qAxis != m_pCustomPlot->yAxis && axis.qAxis != m_pCustomPlot->yAxis2)
m_pCustomPlot->axisRect()->removeAxis(axis.qAxis);
else
axis.qAxis->setVisible(false);
m_axes.remove(i);
}
else
i++;
#else
Axis& axis = m_axes[i];
if(!axisCfgMap.contains(axis._cfg.dataType))
{
//先删除轴上的Graph(注意先后顺序QCustomPlot的removeGraph会析构graph所以先做m_graphs的删除)
// for(auto it = m_graphs.begin(); it != m_graphs.end();)
// {
// if(it.value().qGraph && it.value().qGraph->valueAxis() == axis.qAxis)
// it = m_graphs.erase(it); //使用erase方法可以更新迭代器从而避免迭代器失效
// else
// ++it;
// }
QMutableHashIterator<QString, Graph> it(m_graphs); //使用QMutableHashIterator可以再遍历相关容器的同时安全的修改例如删除元素
while (it.hasNext())
{
it.next();
if (it.value().qGraph && it.value().qGraph->valueAxis() == axis.qAxis)
it.remove(); // 直接删除当前项,无需手动管理迭代器
}
for(int j = 0; j < m_pCustomPlot->graphCount(); j++)
{
QCPGraph* graph = m_pCustomPlot->graph(j);
if(graph->valueAxis() == axis.qAxis)
{
m_pCustomPlot->removeGraph(graph);
}
}
//删除轴
m_pCustomPlot->axisRect()->removeAxis(axis.qAxis);
m_axes.remove(i);
}
else
++i;
#endif
}
if(m_axes.isEmpty()) //若所有轴(数据类型)全部清楚,将默认左轴xAxis显示出来
m_pCustomPlot->xAxis->setVisible(true);
//处理应用配置(新增或更新)
for(int i = 0; i < cfg.m_pModel_dataType->rowCount(); i++) //能保证和选取顺序一致
//for(auto it = axisCfgMap.begin(); it != axisCfgMap.end(); ++it)
{
RealTimeDataType dataType = (RealTimeDataType)cfg.m_pModel_dataType->item(i, 0)->data(Qt::UserRole + itemRole_dataType).toInt();
//RealTimeDataType dataType = it.key();
if(axesMap.contains(dataType)) //更新配置
{
}
else //新增轴
{
#ifdef useDefaultAxis_Y
AxisConfig cfg = axisCfgMap.value(dataType);
Axis axis;
if(m_axes.isEmpty()) //使用默认轴yAxis
{
axis.setQCPAxis(m_pCustomPlot->yAxis, true);
}
else if(m_axes.count() == 1 && m_axes.at(0).qAxis == m_pCustomPlot->yAxis) //默认轴yAxis被使用使用yAxis2
{
axis.setQCPAxis(m_pCustomPlot->yAxis2, true);
m_pCustomPlot->yAxis2->setVisible(false);
}
else
{
QCPAxis* pAxis = new QCPAxis(m_pCustomPlot->axisRect(), QCPAxis::atRight);
axis.setQCPAxis(pAxis, false);
axis.setStyle(m_chartStyle);
}
axis.applyConfig(cfg);
m_axes.append(axis);
#else
AxisConfig axisCfg = axisCfgMap.value(dataType);
Axis axis;
axis.applyConfig(axisCfg);
axis.setStyle(m_chartStyle);
if(m_axes.isEmpty())
{
//隐藏默认左轴
m_pCustomPlot->yAxis->setVisible(false);
//创建自定义轴代替
//QCPAxis* pAxis = new QCPAxis(m_pCustomPlot->axisRect(), QCPAxis::atLeft);
QCPAxis* pAxis = m_pCustomPlot->axisRect()->addAxis(QCPAxis::atLeft);
if(!pAxis)
continue;
//qDebug() << "add axis on:" << QCPAxis::atLeft << ", name: " << axisCfg.name;
axis.setQCPAxis(pAxis, false);
}
else
{
//默认放在右边
//QCPAxis* pAxis = new QCPAxis(m_pCustomPlot->axisRect(), QCPAxis::atRight);
QCPAxis* pAxis = m_pCustomPlot->axisRect()->addAxis(QCPAxis::atRight);
if(!pAxis)
continue;
axis.setQCPAxis(pAxis, false);
}
m_axes.append(axis);
#endif
}
}
//重新排列坐标轴
m_axisArrangementMode = cfg.arrangement;
arrangeAxes();
//2.曲线-数据源决定
QVector<Graph> deleteGraphs;
for(int i = 0; i < cfg.m_pModel_dataSource->rowCount(); i++)
{
QString stationID = cfg.m_pModel_dataSource->item(i, 0)->data(Qt::UserRole + itemRole_stationID).toString();
QString compoentID = cfg.m_pModel_dataSource->item(i, 0)->data(Qt::UserRole + itemRole_componentID).toString();
QString pointID = cfg.m_pModel_dataSource->item(i, 0)->data(Qt::UserRole + itemRole_pointID).toString();
QString graphID = stationID + "-" + compoentID + "-" + pointID;
if(!m_graphs.contains(graphID))
{
RealTimeDataType dataType = (RealTimeDataType)cfg.m_pModel_dataSource->item(i, 0)->data(Qt::UserRole + itemRole_dataType).toInt();
Graph graph;
graph.dataID = graphID;
graph.dataType = dataType;
}
else
{
}
}
m_updateData = true;
}
void dpLineChart::onSignal_rangeChanged_xAxis(const QCPRange& range)
{
// qDebug() << "m_timeRange: " << m_timeRange;
// qDebug() << "range size: " << range.size();
if(m_timeRange != range.size() * 1000)
m_timeRange = range.size() * 1000;
}