DYTSrouce/src/ui/chartPlot/FitCurveDialog.cpp
2025-01-07 23:45:23 +08:00

782 lines
21 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 "fitcurvedialog.h"
#include <QLogValueAxis>
#include "../DockTitleBar.h"
#include "../DockWidget.h"
#include "common/SpdLogger.h"
#include "workspace/WorkSpace.h"
#include "workspace/Timestep.h"
#include "workspace/WorkSpaceManager.h"
FitCurveDialog::FitCurveDialog(int iType, QWidget* parent) :
QDialog(parent),
ui(new Ui::FitCurveDialog)
{
m_iType = iType;
if (1 == m_iType)
{
setWindowTitle("2D Curve");
}
else
{
setWindowTitle("2D(y(lg)) Curve");
}
qApp->setOverrideCursor(Qt::ArrowCursor); //允许系统弹窗、提示
initQChartView();
connect(&WorkSpaceManager::Get(), &WorkSpaceManager::WorkSpaceChanged, this, &FitCurveDialog::OnWorkSpaceChanged);
}
void FitCurveDialog::initQChartView() {
//创建图表框架
curveChartView = new FitCurveChartView(this);
curveChartView->setMaximumWidth(1730);
curveChartView->setMinimumHeight(480);
curveChart = new QChart();
curveChart->setTheme(QChart::ChartThemeBlueIcy);
//curveChart->setContentsMargins(0, 0, 0, 0); //设置外边界全部为0, 根据自己实际情况设置
//curveChart->setMargins(QMargins(5, -30, 5, 10)); //设置内边界, 根据自己实际情况设置
curveChart->setBackgroundRoundness(0); //设置表格边框圆角半径
curveChartView->setChart(curveChart);
//QObject::connect(fitPointsSeriesS, &QSplineSeries::clicked, [=](const QPointF& point)mutable {
// QPointF tempPoint;
// QVector<QPointF> tempList(fitPointsSeriesS->pointsVector()); //复制曲线中的数据进行计算, 因为直接使用会导致卡顿
// int tempX = qRound(point.x());
// int tempY = -999;
// for (int i = 0; i < tempList.size(); i++) {
// if (tempList[i].x() == tempX) {
// tempY = tempList[i].y();
// tempPoint.setX(tempX);
// tempPoint.setY(tempY);
// break;
// }
// }
// if (tempY != -999) {
// QToolTip::showText(QCursor::pos(), QString("(%1,%2)").arg(tempX).arg(tempY));
// QVector<QPointF> tipList;
// tipList.append(tempPoint);
// tipSeries->replace(tipList);
// updateXYGuideLine();
// }
// });
//创建坐标轴
m_pAxisX = new QValueAxis;
m_pAxisX->setRange(0, 10);
m_pAxisX->setTickCount(21);
m_pAxisX->setLabelFormat("%d");
m_pAxisX->setLabelsAngle(-90); //坐标刻度文字显示角度
curveChart->addAxis(m_pAxisX, Qt::AlignBottom);
//xGuideSeries->attachAxis(m_pAxisX);
//yGuideSeries->attachAxis(m_pAxisX);
// fitPointsSeriesS->attachAxis(m_pAxisX);
// tipSeries->attachAxis(m_pAxisX);
if (1 == m_iType)
{
m_pAxisY = new QValueAxis;
m_pAxisY->setRange(0, 10);
m_pAxisY->setTickCount(11);
m_pAxisY->setLabelFormat("%d");
curveChart->addAxis(m_pAxisY, Qt::AlignLeft);
}
else
{
m_pLgAxisY = new QLogValueAxis();
m_pLgAxisY->setLabelFormat("%e");
m_pLgAxisY->setMinorTickCount(9);
//axisY->setMinorTickLineVisible(true);
m_pLgAxisY->setLabelsAngle(90);
m_pLgAxisY->setRange(1, 100);
curveChart->addAxis(m_pLgAxisY, Qt::AlignLeft);
}
// axisX->setGridLineVisible(false); //隐藏背景网格X轴框线
// axisY->setGridLineVisible(false); //隐藏背景网格Y轴框线
/*curveChart->legend()->markers()[0]->setVisible(false);
curveChart->legend()->markers()[1]->setVisible(false);
curveChart->legend()->markers()[2]->setVisible(false);
curveChart->legend()->markers()[3]->setVisible(false);*/
curveChartView->setRenderHint(QPainter::Antialiasing); //除锯齿
connect(curveChartView, &FitCurveChartView::signalMouseEvent, this, &FitCurveDialog::theSlotMouseEvent);
connect(curveChartView, &FitCurveChartView::signalWheelEvent, this, &FitCurveDialog::theSlotWheelEvent);
//curveChartView->show();
QHBoxLayout* pLayout = new QHBoxLayout(this);
pLayout->addWidget(curveChartView);
// ui->chartLayout->addWidget(curveChartView);
}
void FitCurveDialog::theSlotMouseEvent(int eventId, QMouseEvent* event) {
if (eventId == 0) { //单击按下
isPressed = true;
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
pressedPoint = mouseEvent->pos();
}
else if (eventId == 1) { //鼠标移动
if (isPressed) {
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
curveChart->scroll(-(mouseEvent->pos().x() - pressedPoint.x()) / 10,
(mouseEvent->pos().y() - pressedPoint.y()) / 10);
updateXYGuideLine();
}
}
else if (eventId == 2) { //单击抬起
isPressed = false;
}
else if (eventId == 3) { //双击
resetZoomAndScroll();
updateXYGuideLine();
}
}
void FitCurveDialog::theSlotWheelEvent(QWheelEvent* event) {
int delta = event->angleDelta().y();
if (delta > 0) {
curveChart->zoom(0.95);
}
else {
curveChart->zoom(1.05);
}
updateXYGuideLine();
}
void FitCurveDialog::slotAddSeries(QVariant varSeriesData)
{
// 数据无效
if (!varSeriesData.isValid())
{
return;
}
QString strName = varSeriesData.toMap().value("Name").toString(); // 曲线名称
QVariantList varCurDataList = varSeriesData.toMap().value("Data").toList(); // 数据
int iYType = varSeriesData.toMap().value("yType").toInt(); // y轴类型
QColor color = varSeriesData.toMap().value("Color").value<QColor>(); // 曲线颜色
QString strXName = varSeriesData.toMap().value("xTitle").toString(); // x轴名称
QString strYName = varSeriesData.toMap().value("yTitle").toString(); // y轴名称
QString strZName = varSeriesData.toMap().value("zTitle").toString(); // z轴名称
int iCurveType = varSeriesData.toMap().value("curveType").toInt(); // 曲线类型
int iID = varSeriesData.toMap().value("ID").toInt(); // y轴类型
bool bAdd = varSeriesData.toMap().value("Add").toBool(); // 曲线名称
if (iCurveType == 3) // 非二维曲线
{
return;
}
bool bNew = false;
QSplineSeries* pSeries = NULL;
if (m_seriesIDMap.contains(iID))
{
pSeries = m_seriesIDMap.value(iID);
pSeries->setName(strName); // 设置曲线名称
pSeries->setColor(color); // 设置曲线颜色
pSeries->setUseOpenGL(true);
}
else
{
if (!bAdd)
{
return;
}
pSeries = new QSplineSeries(this);
pSeries->setName(strName); // 设置曲线名称
pSeries->setColor(color); // 设置曲线颜色
pSeries->setUseOpenGL(true);
bNew = true;
}
m_pAxisX->setTitleText(strXName); // 设置x轴名称
// xy轴的范围
m_iXMax = m_pAxisX->max(); // 当前x轴的最大值
m_iXMin = m_pAxisX->min(); // 当前x轴的最小值
if (0 == iYType) // 一般曲线
{
m_iYMax = m_pAxisY->max(); // 当前y轴的最大值
m_iYMin = m_pAxisY->min(); // 当前y轴的最大值
}
else // 对数曲线
{
m_iYMax = m_pLgAxisY->max(); // 当前y轴的最大值
m_iYMin = m_pLgAxisY->min(); // 当前y轴的最大值
}
// 曲线数据
QVector<QPointF> listKey;
for (size_t i = 0; i < varCurDataList.size(); i++)
{
QPointF ptKey = varCurDataList[i].toPointF();
listKey.push_back(ptKey);
// 更新x轴最大值最小值
if (m_iXMin > ptKey.x())
{
m_iXMin = ptKey.x();
}
if (m_iXMax < ptKey.x())
{
m_iXMax = ptKey.x();
}
if (m_iYMin > ptKey.y())
{
m_iYMin = ptKey.y();
}
if (m_iYMax < ptKey.y())
{
m_iYMax = ptKey.y();
}
}
// 更新x轴范围
m_pAxisX->setRange(m_iXMin, m_iXMax);
pSeries->replace(listKey);
if (bNew)
{
curveChart->addSeries(pSeries);
}
// 更新y轴范围
if (iYType == 0)
{
m_pAxisY->setRange(m_iYMin, m_iYMax);
if (bNew)
{
pSeries->attachAxis(m_pAxisY);
}
m_pAxisY->setTitleText(strYName);
}
else
{
m_pLgAxisY->setRange(m_iYMin, m_iYMax);
if (bNew)
{
pSeries->attachAxis(m_pLgAxisY);
}
m_pLgAxisY->setTitleText(strYName);
}
if (bNew)
{
pSeries->attachAxis(m_pAxisX);
m_seriesIDMap.insert(iID, pSeries);
}
//QObject::connect(fitPointsSeriesS, &QSplineSeries::clicked, [=](const QPointF& point)mutable {
// QPointF tempPoint;
// QVector<QPointF> tempList(fitPointsSeriesS->pointsVector()); //复制曲线中的数据进行计算, 因为直接使用会导致卡顿
// int tempX = qRound(point.x());
// int tempY = -999;
// for (int i = 0; i < tempList.size(); i++) {
// if (tempList[i].x() == tempX) {
// tempY = tempList[i].y();
// tempPoint.setX(tempX);
// tempPoint.setY(tempY);
// break;
// }
// }
// if (tempY != -999) {
// QToolTip::showText(QCursor::pos(), QString("(%1,%2)").arg(tempX).arg(tempY));
// QVector<QPointF> tipList;
// tipList.append(tempPoint);
// tipSeries->replace(tipList);
// updateXYGuideLine();
// }
// });
}
void FitCurveDialog::slotDelCurve(int iID)
{
QSplineSeries* pSeries = NULL;
if (m_seriesIDMap.contains(iID))
{
pSeries = m_seriesIDMap.value(iID);
curveChart->removeSeries(pSeries);
m_seriesIDMap.remove(iID);
}
}
void FitCurveDialog::slotInserKeyPoint(int iID, float xValue, float yValue)
{
QSplineSeries* pSeries = NULL;
if (m_seriesIDMap.contains(iID))
{
pSeries = m_seriesIDMap.value(iID);
}
if (pSeries)
{
QVector<QPointF> vecKeyPoints = pSeries->pointsVector();
vecKeyPoints.push_back(QPointF(xValue, yValue));
if (m_iXMax < xValue)
{
m_iXMax = xValue;
}
if (m_iXMin > xValue)
{
m_iXMin = xValue;
}
if (m_iYMax < yValue)
{
m_iYMax = yValue;
}
if (m_iYMin > yValue)
{
m_iYMin = yValue;
}
if (1 == m_iType)
{
m_pAxisY->setRange(m_iXMin, m_iXMax);
}
else
{
m_pLgAxisY->setRange(m_iYMin, m_iYMax);
}
pSeries->replace(vecKeyPoints);
}
}
void FitCurveDialog::slotUpdateTime(double dTime)
{
if (m_vecWavePoint.size() > 0)
{
if (dTime < 1)
{
QMap<int, QSplineSeries*>::iterator pItor = m_seriesIDMap.begin();
while (pItor != m_seriesIDMap.end())
{
pItor.value()->clear();
pItor++;
}
return;
}
if ((dTime - 1) >= m_vecWavePoint.size())
{
return;
}
std::vector<std::vector<float>> batch = m_vecWavePoint[dTime - 1];
m_pAxisX->setTitleText("x"); // 设置x轴名称
// xy轴的范围
m_iXMax = m_pAxisX->max(); // 当前x轴的最大值
m_iXMin = m_pAxisX->min(); // 当前x轴的最小值
m_iYMax = m_pAxisY->max(); // 当前y轴的最大值
m_iYMin = m_pAxisY->min(); // 当前y轴的最大值
m_iXMin = 0;
m_iYMin = 0;
for (int i = 0; i < batch.size(); i++)
{
bool bNew = false;
QSplineSeries* pSeries = nullptr;
if (m_seriesIDMap.contains(i+1))
{
pSeries = m_seriesIDMap.value(i + 1);
}
else
{
pSeries = new QSplineSeries(this);
m_seriesIDMap.insert(i + 1, pSeries);
bNew = true;
}
QString strName = QString::fromLocal8Bit("目标") + QString::number(i+1);
pSeries->setName(strName); // 设置曲线名称
if (bNew)
{
qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime())); // 初始化随机数生成器
QColor color(QRandomGenerator::global()->bounded(255), QRandomGenerator::global()->bounded(255), QRandomGenerator::global()->bounded(255));
pSeries->setColor(color); // 设置曲线颜色
}
pSeries->setUseOpenGL(true);
std::vector<float> vecData = batch[i];
if (m_iXMax < vecData.size())
{
m_iXMax = vecData.size();
}
QList<QPointF> listKey;
for (int j = 0; j < vecData.size(); j++)
{
float fY = vecData[j];
listKey.push_back(QPointF(j+1, fY));
if (m_iYMax < fY)
{
m_iYMax = fY;
}
if (m_iYMin > fY)
{
m_iYMin = fY;
}
}
m_pAxisX->setRange(m_iXMin, m_iXMax);
pSeries->replace(listKey);
if (bNew)
{
curveChart->addSeries(pSeries);
}
m_pAxisY->setRange(m_iYMin, m_iYMax);
if (bNew)
{
pSeries->attachAxis(m_pAxisY);
}
m_pAxisY->setTitleText(QString::fromLocal8Bit("幅值"));
if (bNew)
{
pSeries->attachAxis(m_pAxisX);
}
}
}
if (m_vecReportPoint.size() > 0)
{
if (dTime < 1)
{
QMap<int, QSplineSeries*>::iterator pItor = m_seriesIDMap.begin();
while (pItor != m_seriesIDMap.end())
{
pItor.value()->clear();
pItor++;
}
return;
}
if ((dTime - 1) >= m_vecReportPoint.size())
{
return;
}
std::vector<std::vector<float>> batch = m_vecReportPoint[dTime - 1];
m_pAxisX->setTitleText(QString::fromLocal8Bit("距离")); // 设置x轴名称
// xy轴的范围
m_iXMax = m_pAxisX->max(); // 当前x轴的最大值
m_iXMin = m_pAxisX->min(); // 当前x轴的最小值
m_iYMax = m_pAxisY->max(); // 当前y轴的最大值
m_iYMin = m_pAxisY->min(); // 当前y轴的最大值
for (int i = 0; i < batch.size(); i++)
{
bool bNew = false;
QSplineSeries* pSeries = nullptr;
if (m_seriesIDMap.contains(i + 1))
{
pSeries = m_seriesIDMap.value(i + 1);
}
else
{
pSeries = new QSplineSeries(this);
m_seriesIDMap.insert(i + 1, pSeries);
bNew = true;
}
QString strName = QString::fromLocal8Bit("目标") + QString::number(i + 1);
pSeries->setName(strName); // 设置曲线名称
if (bNew)
{
qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime())); // 初始化随机数生成器
QColor color(QRandomGenerator::global()->bounded(255), QRandomGenerator::global()->bounded(255), QRandomGenerator::global()->bounded(255));
pSeries->setColor(color); // 设置曲线颜色
}
pSeries->setUseOpenGL(true);
std::vector<float> vecData = batch[i];
float fX = vecData[0];
float fY = vecData[1];
if (m_iYMax < fY)
{
m_iYMax = fY;
}
if (m_iYMin > fY)
{
m_iYMin = fY;
}
if (m_iXMax < fX)
{
m_iXMax = fX;
}
if (m_iXMin > fX)
{
m_iXMax = fX;
}
m_pAxisX->setRange(m_iXMin, m_iXMax);
pSeries->append(QPointF(fX,fY));
if (bNew)
{
curveChart->addSeries(pSeries);
}
m_pAxisY->setRange(m_iYMin, m_iYMax);
if (bNew)
{
pSeries->attachAxis(m_pAxisY);
}
m_pAxisY->setTitleText(QString::fromLocal8Bit("速度"));
if (bNew)
{
pSeries->attachAxis(m_pAxisX);
}
}
}
}
void FitCurveDialog::updateXYGuideLine() {
/* if (tipSeries->points().size() > 0) {
QVector<int> axisRanges = getAxisRanges();
QVector<QPointF> xGuideList, yGuideList;
int tempX = tipSeries->points()[0].x();
int tempY = tipSeries->points()[0].y();
xGuideList.append(QPointF(tempX, axisRanges[2]));
xGuideList.append(QPointF(tempX, tempY));
yGuideList.append(QPointF(axisRanges[0], tempY));
yGuideList.append(QPointF(tempX, tempY));
xGuideSeries->replace(xGuideList);
yGuideSeries->replace(yGuideList);
}
else
{
xGuideSeries->clear();
yGuideSeries->clear();
}*/
}
void FitCurveDialog::resetZoomAndScroll() {
curveChart->zoomReset();
QList<QAbstractAxis*> axesX, axesY;
axesX = curveChart->axes(Qt::Horizontal);
axesY = curveChart->axes(Qt::Vertical);
QValueAxis* curAxisX = (QValueAxis*)axesX[0];
QValueAxis* curAxisY = (QValueAxis*)axesY[0];
curAxisX->setRange(m_iXMin, m_iXMax);
curAxisY->setRange(m_iYMin, m_iYMax);
}
QVector<int> FitCurveDialog::getAxisRanges() {
QList<QAbstractAxis*> axesX, axesY;
axesX = curveChart->axes(Qt::Horizontal);
axesY = curveChart->axes(Qt::Vertical);
QValueAxis* curAxisX = (QValueAxis*)axesX[0];
QValueAxis* curAxisY = (QValueAxis*)axesY[0];
QVector<int> ranges = { int(curAxisX->min()), int(curAxisX->max()), int(curAxisY->min()), int(curAxisY->max()) };
return ranges;
}
void FitCurveDialog::InitWaveFile(const QString& strFile, int iBatchCount)
{
if (strFile.isEmpty())
return;
m_vecWavePoint.clear();
ParseWave(strFile, m_vecWavePoint, iBatchCount);
}
void FitCurveDialog::InitReportFile(const QString& strFile, int iBatchCount)
{
if (strFile.isEmpty())
return;
m_vecReportPoint.clear();
ParseReport(strFile, m_vecReportPoint, iBatchCount);
}
void FitCurveDialog::ParseWave(const QString& strFile, std::vector<std::vector<std::vector<float>>>& vecWavePoint, int iRowCount)
{
if (strFile.isEmpty())
{
QMessageBox::information(nullptr, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("请检查数据Wave文件路径"));
return;
}
QFile file(strFile);
if (file.open(QIODevice::ReadOnly))
{
std::vector<std::vector<float>> batch;
int iRow = 1;
while (!file.atEnd())
{
QString strLine = file.readLine().simplified();
if (!strLine.isEmpty())
{
QStringList listLine = strLine.split(" ");
std::vector<float> vecLine;
for (size_t i = 0; i < listLine.size(); i++)
{
vecLine.push_back(listLine[i].toFloat());
}
batch.push_back(vecLine);
if (iRow % iRowCount == 0)
{
vecWavePoint.push_back(batch);
batch.clear();
}
}
iRow++;
}
file.close();
}
}
void FitCurveDialog::ParseReport(const QString& strFile, std::vector<std::vector<std::vector<float>>>& vecReportPoint, int iRowCount)
{
if (strFile.isEmpty())
{
QMessageBox::information(nullptr, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("请检查数据Report文件路径"));
return;
}
QFile file(strFile);
if (file.open(QIODevice::ReadOnly))
{
std::vector<std::vector<float>> batch;
int iRow = 1;
while (!file.atEnd())
{
QString strLine = file.readLine().simplified();
if (!strLine.isEmpty())
{
QStringList listLine = strLine.split(" ");
std::vector<float> vecLine;
for (size_t i = 0; i < listLine.size(); i++)
{
if (12 == i || i == 13) // 距离、速度
{
vecLine.push_back(listLine[i].toFloat());
}
}
batch.push_back(vecLine);
if (iRow % iRowCount == 0)
{
vecReportPoint.push_back(batch);
batch.clear();
}
}
iRow++;
}
file.close();
}
slotUpdateTime(1);
}
void FitCurveDialog::OnWorkSpaceChanged(WorkSpace* worksapce) {
if (worksapce == nullptr) {
LOG_ERROR("worksapce is nullptr");
return;
}
connect(worksapce, &WorkSpace::TimestepChanged, this, &FitCurveDialog::OnTimestepChanged);
}
void FitCurveDialog::OnTimestepChanged(Timestep* timestep) {
if (timestep == nullptr) {
LOG_ERROR("timestep is nullptr");
return;
}
connect(timestep, SIGNAL(TimeChanged(double)), this, SLOT(slotUpdateTime(double)));
}
FitCurveDialog::~FitCurveDialog()
{
delete ui;
}
void FitCurveDialog::AttachDock(DockWidget* dockWidget)
{
if (nullptr == dockWidget) {
qDebug() << __FUNCTION__ << "dockwidget is nullptr";
return;
}
dockWidget->SetDockWidgetTitleBar(nullptr);
dockWidget->setWidget(this);
DockTitleBar* dockTitleBar = new DockTitleBar;
if (1 == m_iType)
{
dockTitleBar->SetTitle(tr("2D Curve"));
}
else
{
dockTitleBar->SetTitle(tr("2D(y(lg)) Curve"));
}
dockWidget->SetDockWidgetTitleBar(dockTitleBar);
}