feat:完成多坐标轴(数据类型)的动态配置,包括排列方式的动态设置

This commit is contained in:
duanshengchao 2025-07-25 17:42:15 +08:00
parent eaaafcbce0
commit c120d6afc2
7 changed files with 224 additions and 23 deletions

View File

@ -22,6 +22,8 @@ protected:
{
QColor bgColor; //背景颜色
QColor axisColor; //坐标轴颜色
QColor labelColor; //坐标轴标签颜色
QFont labelFont; //坐标轴标签字体
QColor tickColor; //刻度颜色
QColor tickLabelColor; //刻度label颜色
QFont tickLabelFont; //刻度label字体
@ -38,12 +40,36 @@ protected:
{
AxisConfig _cfg;
QCPAxis* qAxis = nullptr;
QCPAxis* qAxis;
bool bIsDefaultAxis; //一个plot包含两个默认坐标轴axis和axis2
ChartStyle style;
Axis(const AxisConfig& cfg, QCPAxisRect* rect)
Axis()
{
applyConfig(cfg);
qAxis = new QCPAxis(rect, QCPAxis::atRight);
qAxis = nullptr;
bIsDefaultAxis = false;
}
void setQCPAxis( QCPAxis* axis, bool isDefualtAxis)
{
qAxis = axis;
bIsDefaultAxis = isDefualtAxis;
if(qAxis)
{
qAxis->setLabel(_cfg.name);
qAxis->setSubTicks(false);
//颜色
qAxis->setBasePen(style.axisColor);
//label
qAxis->setLabelColor(style.labelColor);
qAxis->setLabelFont(style.labelFont);
//刻度颜色
qAxis->setTickPen(QPen(style.tickColor));
//刻度Label颜色
qAxis->setTickLabelColor(style.tickLabelColor);
qAxis->setTickLabelFont(style.tickLabelFont);
qAxis->setTickLabelColor(style.tickLabelColor);
}
}
void applyConfig(const AxisConfig& cfg)
@ -53,13 +79,19 @@ protected:
_cfg.dataType = cfg.dataType;
}
void setStyle(const ChartStyle chartStyle)
{
style = chartStyle;
}
~Axis()
{
if(qAxis)
//执行plot->axisRect()->removeAxis(axis)时axis会delete掉
/*if(qAxis && !bIsDefaultAxis) //默认坐标轴由所属plot管理
{
delete qAxis;
qAxis = nullptr;
}
}*/
}
};
};

View File

@ -79,18 +79,22 @@ void dpConfigurationDialog::initialize()
item->setEditable(false); //不可编辑
m_pModel_typeSource->appendRow(item);
}*/
QStandardItem* item = new QStandardItem(QString::fromStdWString(L"电压"));
QStandardItem* item = new QStandardItem("电压");
item->setData(RealTimeDataType::voltage, Qt::UserRole + itemRole_dataType);
item->setEditable(false); //不可编辑
m_pModel_typeSource->appendRow(item);
item = new QStandardItem(QString::fromStdWString(L"电流"));
item = new QStandardItem("电流");
item->setData(RealTimeDataType::current, Qt::UserRole + itemRole_dataType);
item->setEditable(false); //不可编辑
m_pModel_typeSource->appendRow(item);
item = new QStandardItem(QString::fromStdWString(L"功率"));
item = new QStandardItem("功率");
item->setData(RealTimeDataType::power, Qt::UserRole + itemRole_dataType);
item->setEditable(false); //不可编辑
m_pModel_typeSource->appendRow(item);
item = new QStandardItem("温度");
item->setData(RealTimeDataType::temperature, Qt::UserRole + itemRole_dataType);
item->setEditable(false); //不可编辑
m_pModel_typeSource->appendRow(item);
//typeSelectedList
ui->typeSelectedList->setSelectionBehavior(QAbstractItemView::SelectRows);
m_pModel_typeSelected = new QStandardItemModel(this);
@ -141,6 +145,8 @@ void dpConfigurationDialog::copyModelData(QStandardItemModel* sourceModel, QStan
if(item)
{
QStandardItem* newItem = new QStandardItem(item->text());
RealTimeDataType dataType = (RealTimeDataType)item->data(Qt::UserRole + itemRole_dataType).toInt();
newItem->setData(dataType, Qt::UserRole + itemRole_dataType);
itemList.push_back(newItem);
}
}
@ -187,6 +193,14 @@ void dpConfigurationDialog::createDataSourceList()
powerItem->setData(2, Qt::UserRole + itemRole_pointID);
powerItem->setData(RealTimeDataType::power, Qt::UserRole + itemRole_dataType);
componentItem->appendRow(powerItem);
QStandardItem* tempItem = new QStandardItem("温度");
tempItem->setEditable(false);
tempItem->setData("point", Qt::UserRole + itemRole_tag);
tempItem->setData(i, Qt::UserRole + itemRole_stationID);
tempItem->setData(0, Qt::UserRole + itemRole_componentID);
tempItem->setData(2, Qt::UserRole + itemRole_pointID);
tempItem->setData(RealTimeDataType::temperature, Qt::UserRole + itemRole_dataType);
componentItem->appendRow(tempItem);
}
ui->dataSourceList->expandAll();
@ -212,6 +226,9 @@ void dpConfigurationDialog::removeDataSelected(int deleteRow)
void dpConfigurationDialog::setPanel(DataPanel* pPanel)
{
if(!pPanel)
return;
m_pDataPanel = pPanel;
copyModelData(pPanel->m_cofigurationResults.m_pModel_dataType, m_pModel_typeSelected);
copyModelData(pPanel->m_cofigurationResults.m_pModel_dataSource, m_pModel_dataSelected);
@ -231,7 +248,15 @@ void dpConfigurationDialog::setPanel(DataPanel* pPanel)
ui->specialSettings->setCurrentIndex(0);
ui->specialSettings->setVisible(true);
ui->axisObject->clear();
m_axisCfgInfo.clear();
m_axisCfgMap.clear();
for(int i = 0; i< m_pModel_typeSelected->rowCount(); i++)
{
RealTimeDataType dataType = (RealTimeDataType)m_pModel_typeSelected->item(i, 0)->data(Qt::UserRole + itemRole_dataType).toInt();
QString itemText = m_pModel_typeSelected->item(i, 0)->text();
ui->axisObject->addItem(itemText, dataType);
}
m_axisCfgMap = pPanel->m_cofigurationResults.axisCfgMap;
break;
}
default:
@ -300,6 +325,7 @@ void dpConfigurationDialog::onBtnClicked_confirm()
m_pDataPanel->m_cofigurationResults.dataServiceIP = ui->serviceIP->text();
m_pDataPanel->m_cofigurationResults.dataServicePort = ui->servicePort->text().toInt();
m_pDataPanel->m_cofigurationResults.arrangement = (AxisArrangementMode)ui->axisArrangement->currentIndex();
m_pDataPanel->m_cofigurationResults.axisCfgMap = m_axisCfgMap;
m_pDataPanel->configurationComplete();
}
}
@ -326,11 +352,18 @@ void dpConfigurationDialog::onItemClicked_typeSource(const QModelIndex& index)
}
}
if(!m_pDataPanel)
{
ui->errorTip->setVisible(true);
ui->errorTip->setToolTip(QString("当前面板对象为nullptr"));
return;
}
if(!bIsHad && m_pModel_typeSelected)
{
int nMaximumType = m_pModel_typeSelected->rowCount();
if(m_pDataPanel && (m_pDataPanel->getType() == lineChart || m_pDataPanel->getType() == curveChart))
nMaximumType = 2;
nMaximumType = 4;
if(m_pModel_typeSelected->rowCount() >= nMaximumType)
{
@ -358,7 +391,9 @@ void dpConfigurationDialog::onItemClicked_typeSource(const QModelIndex& index)
case curveChart:
case barChart:
{
m_axisCfgInfo.insert(dataType, AxisCfgInfo());
AxisCfgInfo cfg;
cfg.name = item->text();
m_axisCfgMap.insert(dataType, cfg);
ui->axisObject->addItem(item->text(), dataType);
break;
}
@ -406,7 +441,7 @@ void dpConfigurationDialog::onBtnClicked_remove_type()
case curveChart:
case barChart:
{
m_axisCfgInfo.remove(itemDataType);
m_axisCfgMap.remove(itemDataType);
int index = ui->axisObject->findData(itemDataType);
if(index != -1)
ui->axisObject->removeItem(index);

View File

@ -59,7 +59,7 @@ private:
QStandardItemModel* m_pModel_dataSource;
QStandardItemModel* m_pModel_dataSelected;
QHash<RealTimeDataType, AxisCfgInfo> m_axisCfgInfo;
QHash<RealTimeDataType, AxisCfgInfo> m_axisCfgMap;
};

View File

@ -18,7 +18,8 @@ enum RealTimeDataType
{
power = 0, //功率
voltage, //电压
current //电流
current, //电流
temperature //温度
};
enum AxisArrangementMode //坐标轴排列方式
@ -40,7 +41,7 @@ struct configurationResults
QString dataServiceIP;
int dataServicePort;
AxisArrangementMode arrangement;
QHash<RealTimeDataType, AxisCfgInfo> axisCfgInfo; //可能有多个轴(数据类别)
QHash<RealTimeDataType, AxisCfgInfo> axisCfgMap; //可能有多个轴(数据类别)
configurationResults()
{

View File

@ -1,5 +1,6 @@
#include "dpLineChart.h"
//#define useDefaultAxis_Y //默认轴不可删除、不可左右移动所以做动态管理尤其是动态排列时要考虑的比较复杂可以采用不使用的策略全部以自定义轴替代默认轴yAxis只在初始化和没有实际数据轴自定义时显示用来做外观展示
dpLineChart::dpLineChart(QWidget* parent)
:dpBaseChart(parent)
{
@ -9,6 +10,7 @@ dpLineChart::dpLineChart(QWidget* parent)
initQCP();
m_timeRange = 60 * 1000;
m_axisArrangementMode = AlternateSides;
QBoxLayout* mainLayout = new QBoxLayout(QBoxLayout::LeftToRight);
mainLayout->setContentsMargins(0, 1, 0, 0);
@ -24,6 +26,8 @@ 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);
@ -38,9 +42,10 @@ void dpLineChart::initQCP()
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(true);
m_pCustomPlot->yAxis2->setVisible(false);
connect(m_pCustomPlot->xAxis, qOverload<const QCPRange &>(&QCPAxis::rangeChanged), //rangeChanged有两个版本qOverload可以指定版本
this, &dpLineChart::onSignal_rangeChanged_xAxis);
//背景颜色
@ -76,6 +81,43 @@ void dpLineChart::initQCP()
//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)
{
bool bRemoved = m_pCustomPlot->axisRect()->removeAxis(axis.qAxis);
if(bRemoved) //removeAxis执行成功被删除的axis会被delete
{
QCPAxis* qcpAxis = m_pCustomPlot->axisRect()->addAxis(position);
axis.setQCPAxis(qcpAxis, false);
}
}
//计算偏移量
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)
@ -159,14 +201,20 @@ void dpLineChart::synchronizeConfigData(const configurationResults& cfg)
//将最新配置信息中的坐标轴相关数据存储在QHash中有助于更好的判断当前坐标轴是否需要发生同步更新
QHash<RealTimeDataType, AxisConfig> axisCfgMap;
for(int i = 0; i< cfg.m_pModel_dataType->rowCount(); i++)
/*for(int i = 0; i< cfg.m_pModel_dataType->rowCount(); i++)
{
AxisConfig axisConfig;
//axisConfig.name = cfg.axisInfo.name;
//axisConfig.unit = cfg.axisInfo.unit;
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()的第二个参数接收的是副本(引起拷贝),所以采用指针可以减少拷贝从而提升效率
@ -179,17 +227,100 @@ void dpLineChart::synchronizeConfigData(const configurationResults& cfg)
//删除轴
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))
{
m_pCustomPlot->axisRect()->removeAxis(axis.qAxis);
m_axes.remove(i); //QVector::remove()会调用存储对象的析构函数
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();
}
void dpLineChart::onSignal_rangeChanged_xAxis(const QCPRange& range)

View File

@ -34,12 +34,14 @@ public slots:
private:
void initQCP();
void arrangeAxes();
QCustomPlot* m_pCustomPlot;
ChartStyle m_chartStyle;
qint64 m_timeRange;
QDateTime m_curDateTime;
QVector<Axis> m_axes;
AxisArrangementMode m_axisArrangementMode;
};
#endif

View File

@ -713,7 +713,7 @@ background-color:transparent;
</item>
<item>
<property name="text">
<string>右排列</string>
<string>右排列</string>
</property>
</item>
</widget>