2025-03-26T17:30:00
This commit is contained in:
parent
6c19e0fc79
commit
86dbbe706d
@ -17,6 +17,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
SOURCES += \
|
||||
data/filemanager.cpp \
|
||||
data/peakpoint.cpp \
|
||||
global.cpp \
|
||||
rightwidget.cpp \
|
||||
ui/analysissettingform.cpp \
|
||||
@ -33,6 +34,7 @@ SOURCES += \
|
||||
|
||||
HEADERS += \
|
||||
data/filemanager.h \
|
||||
data/peakpoint.h \
|
||||
defines.h \
|
||||
global.h \
|
||||
rightwidget.h \
|
||||
|
@ -17,6 +17,7 @@ struct ExperimentData {
|
||||
float sampleTemp;
|
||||
float dsc;
|
||||
};
|
||||
|
||||
struct ExpeInfo
|
||||
{
|
||||
QString sampleName;
|
||||
@ -24,6 +25,7 @@ struct ExpeInfo
|
||||
QString date;
|
||||
QString userName;
|
||||
};
|
||||
|
||||
extern ExpeInfo _expeInfo;
|
||||
extern QFile _expeFile;
|
||||
|
||||
|
232
src/data/peakpoint.cpp
Normal file
232
src/data/peakpoint.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
#include <QDebug>
|
||||
|
||||
#include "peakpoint.h"
|
||||
|
||||
QVector<FileManager::ExperimentData>PeakPoint:: _dataVtr;
|
||||
double PeakPoint::_leftPointX,PeakPoint::_rightPointX;
|
||||
QPointF PeakPoint::_peakPoint;
|
||||
QPointF PeakPoint::_leftSelectedPoint,PeakPoint::_rightSelectedPoint;
|
||||
|
||||
void PeakPoint::setExperimentData(const QVector<FileManager::ExperimentData> &dataVtr)
|
||||
{
|
||||
_dataVtr = dataVtr;
|
||||
}
|
||||
|
||||
QPointF PeakPoint::findPeakPoint(){
|
||||
int n = _dataVtr.size();
|
||||
|
||||
if (n < 3) {
|
||||
return QPointF(); // 至少需要三个点才能找到波峰
|
||||
}
|
||||
|
||||
QPointF uniquePeak;
|
||||
// 初始化为 double 类型能表示的最小负数,确保能处理负数 y 值
|
||||
double maxY = 0.0;
|
||||
bool findFlag = false;
|
||||
|
||||
// 直接在遍历过程中找出最大波峰
|
||||
for (int i = 1; i < n - 1; ++i) {
|
||||
const double currentX = _dataVtr.at(i).sampleTemp;
|
||||
const double currentY = _dataVtr.at(i).dsc;
|
||||
|
||||
if(currentX < _leftPointX){
|
||||
continue;
|
||||
}
|
||||
if(currentX > _rightPointX){
|
||||
break;
|
||||
}
|
||||
|
||||
const double preY = _dataVtr.at(i - 1).dsc;
|
||||
const double lastY = _dataVtr.at(i + 1).dsc;
|
||||
|
||||
findFlag = false;
|
||||
if(currentY >= preY && currentY >= lastY){
|
||||
findFlag = true;
|
||||
}else if(currentY <= preY && currentY <= lastY){
|
||||
findFlag = true;
|
||||
}
|
||||
|
||||
if(findFlag){
|
||||
double absY = std::abs(currentY);
|
||||
if(absY >= maxY){
|
||||
maxY = absY;
|
||||
uniquePeak = QPointF(currentX,currentY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_peakPoint = uniquePeak;
|
||||
|
||||
return uniquePeak;
|
||||
}
|
||||
|
||||
|
||||
QPair<QPointF, QPointF> PeakPoint::calculateMaxDiffPointDetail(
|
||||
const PeakPoint::MaxDiffPointDetailType type)
|
||||
{
|
||||
#if 1
|
||||
double maxDiff = std::numeric_limits<double>::min();
|
||||
QPointF currentPoint,lastPoint;
|
||||
|
||||
for (int i = 0; i < _dataVtr.size() - 1; ++i) {
|
||||
const double currentX = _dataVtr.at(i).sampleTemp;
|
||||
const double currentY = _dataVtr.at(i).dsc;
|
||||
|
||||
if(type == MaxDiffPointDetailType::Left){
|
||||
if(currentX <= _leftPointX){
|
||||
continue;
|
||||
}
|
||||
if(currentX >= _peakPoint.x()){
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
if(currentX <= _peakPoint.x()){
|
||||
continue;
|
||||
}
|
||||
if(currentX >= _rightPointX){
|
||||
break;
|
||||
}
|
||||
}
|
||||
//
|
||||
const double lastX = _dataVtr.at(i + 1).sampleTemp;
|
||||
const double lastY = _dataVtr.at(i + 1).dsc;
|
||||
double diff = std::abs(currentY - lastY);
|
||||
if(diff > maxDiff){
|
||||
maxDiff = diff;
|
||||
|
||||
currentPoint.setX(currentX);
|
||||
currentPoint.setY(currentY);
|
||||
lastPoint.setX(lastX);
|
||||
lastPoint.setY(lastY);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return qMakePair(currentPoint,lastPoint);
|
||||
}
|
||||
|
||||
|
||||
QPair<QPointF, QPointF> PeakPoint::calculateMaxDiffPointLeft()
|
||||
{
|
||||
return calculateMaxDiffPointDetail(MaxDiffPointDetailType::Left);
|
||||
}
|
||||
|
||||
QPair<QPointF, QPointF> PeakPoint::calculateMaxDiffPointRight()
|
||||
{
|
||||
return calculateMaxDiffPointDetail(MaxDiffPointDetailType::Right);
|
||||
}
|
||||
|
||||
double PeakPoint::findClosestY(double targetX)
|
||||
{
|
||||
double minDiff = std::numeric_limits<double>::max();
|
||||
double closestY = 0.0;
|
||||
|
||||
for(FileManager::ExperimentData &ed:_dataVtr){
|
||||
// 计算当前 x 与目标 x 的差值的绝对值
|
||||
double diff = std::abs(ed.sampleTemp - targetX);
|
||||
// 更新最小差值和对应的 y 值
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
closestY = ed.dsc;
|
||||
}
|
||||
}
|
||||
|
||||
return closestY;
|
||||
}
|
||||
|
||||
QPointF PeakPoint::findClosestPointByX(double x) {
|
||||
int left = 0;
|
||||
int right = _dataVtr.size() - 1;
|
||||
|
||||
QPointF targetPoint;
|
||||
targetPoint.setX(_dataVtr.value(0).sampleTemp);
|
||||
targetPoint.setY(_dataVtr.value(0).dsc);
|
||||
|
||||
while (left <= right) {
|
||||
int mid = left + (right - left) / 2;
|
||||
FileManager::ExperimentData& ed = _dataVtr[mid];
|
||||
|
||||
if (std::abs(ed.sampleTemp - x) < std::abs(targetPoint.x() - x)) {
|
||||
targetPoint.setX(ed.sampleTemp);
|
||||
targetPoint.setY(ed.dsc);
|
||||
}
|
||||
|
||||
if(ed.sampleTemp < x){
|
||||
left = mid + 1;
|
||||
} else {
|
||||
right = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return targetPoint;
|
||||
}
|
||||
|
||||
void PeakPoint::setRegionPointX(const double left, const double right)
|
||||
{
|
||||
_leftPointX = left;
|
||||
_rightPointX = right;
|
||||
|
||||
_leftSelectedPoint = findClosestPointByX(_leftPointX);
|
||||
_rightSelectedPoint = findClosestPointByX(_rightPointX);
|
||||
|
||||
qDebug()<<"left,right point:"<<_leftSelectedPoint
|
||||
<<","<<_rightSelectedPoint;
|
||||
}
|
||||
|
||||
QString PeakPoint::textFormat(const double enthalpyValue,
|
||||
const double peakValue,
|
||||
const double startPoint,
|
||||
const double endPoint)
|
||||
{
|
||||
return QString("峰的综合信息:\n"
|
||||
"焓值:%1 J/g \n"
|
||||
"峰值:%2℃ \n"
|
||||
"起始点:%3℃ \n"
|
||||
"终止点:%4℃"
|
||||
).arg(QString::number(enthalpyValue, 'f', 2))
|
||||
.arg(QString::number(peakValue, 'f', 2))
|
||||
.arg(QString::number(startPoint, 'f', 2))
|
||||
.arg(QString::number(endPoint, 'f', 2));
|
||||
}
|
||||
|
||||
// 计算两条直线的交点
|
||||
QPointF PeakPoint::calculateIntersection(const QPointF p1,const QPointF p2,
|
||||
const QPointF p3, const QPointF p4){
|
||||
// 直线的一般式: A1x + B1y + C1 = 0 和 A2x + B2y + C2 = 0
|
||||
double A1 = p2.y() - p1.y();
|
||||
double B1 = p1.x() - p2.x();
|
||||
double C1 = A1 * p1.x() + B1 * p1.y();
|
||||
|
||||
double A2 = p4.y() - p3.y();
|
||||
double B2 = p3.x() - p4.x();
|
||||
double C2 = A2 * p3.x() + B2 * p3.y();
|
||||
|
||||
double determinant = A1 * B2 - A2 * B1;
|
||||
|
||||
if (determinant == 0) {
|
||||
// 两条直线平行或重合,无交点或无限交点
|
||||
return {0, 0};
|
||||
} else {
|
||||
double x = (B2 * C1 - B1 * C2) / determinant;
|
||||
double y = (A1 * C2 - A2 * C1) / determinant;
|
||||
return {x, y};
|
||||
}
|
||||
}
|
||||
|
||||
double PeakPoint::calculateArea(const std::vector<QPointF> &points) {
|
||||
double integral = 0.0;
|
||||
size_t n = points.size();
|
||||
|
||||
if (n < 2) {
|
||||
return integral; // 至少需要两个点才能计算积分
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < n; ++i) {
|
||||
double dx = points[i].x() - points[i - 1].x();
|
||||
double avg_y = (points[i].y() + points[i - 1].y()) / 2.0;
|
||||
integral += dx * avg_y;
|
||||
}
|
||||
|
||||
return integral;
|
||||
}
|
||||
|
40
src/data/peakpoint.h
Normal file
40
src/data/peakpoint.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef PEAKPOINT_H
|
||||
#define PEAKPOINT_H
|
||||
|
||||
#include <QPointF>
|
||||
|
||||
#include "filemanager.h"
|
||||
|
||||
|
||||
namespace PeakPoint{
|
||||
void setExperimentData(const QVector<FileManager::ExperimentData>&);
|
||||
void setRegionPointX(const double,const double);
|
||||
double findClosestY(double targetX);
|
||||
QPointF findPeakPoint();
|
||||
QString textFormat(const double enthalpyValue,
|
||||
const double peakValue,
|
||||
const double startPoint,
|
||||
const double endPoint);
|
||||
|
||||
|
||||
//private
|
||||
QPair<QPointF,QPointF> calculateMaxDiffPointLeft();
|
||||
QPair<QPointF,QPointF> calculateMaxDiffPointRight();
|
||||
enum MaxDiffPointDetailType{
|
||||
Left,
|
||||
Right
|
||||
};
|
||||
QPair<QPointF,QPointF> calculateMaxDiffPointDetail(const MaxDiffPointDetailType type);
|
||||
|
||||
QPointF calculateIntersection(const QPointF p1,const QPointF p2,
|
||||
const QPointF p3, const QPointF p4);
|
||||
double calculateArea(const std::vector<QPointF> &points);
|
||||
QPointF findClosestPointByX(double target);
|
||||
|
||||
extern QVector<FileManager::ExperimentData> _dataVtr;
|
||||
extern double _leftPointX,_rightPointX;
|
||||
extern QPointF _peakPoint;
|
||||
extern QPointF _leftSelectedPoint,_rightSelectedPoint;
|
||||
}
|
||||
|
||||
#endif // PEAKPOINT_H
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "centralwidget.h"
|
||||
#include "filemanager.h"
|
||||
#include "peakpoint.h"
|
||||
|
||||
CentralWidget::CentralWidget(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
@ -132,10 +133,11 @@ void CentralWidget::slotRecvAnalysisFileName(const QString &fileName)
|
||||
{
|
||||
qDebug() << "slotRecvAnalysisFileName" << fileName;
|
||||
|
||||
QVector<FileManager::ExperimentData> dataVtr;
|
||||
FileManager::readExperimentFile(fileName, dataVtr);
|
||||
// QVector<FileManager::ExperimentData> dataVtr;
|
||||
_dataVtr.clear();
|
||||
FileManager::readExperimentFile(fileName, _dataVtr);
|
||||
|
||||
if (dataVtr.size() < 0)
|
||||
if (_dataVtr.size() < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -148,6 +150,8 @@ void CentralWidget::slotRecvAnalysisFileName(const QString &fileName)
|
||||
_customPlot->addGraph();
|
||||
#endif
|
||||
|
||||
PeakPoint::setExperimentData(_dataVtr);
|
||||
|
||||
// 设置坐标轴标签
|
||||
_customPlot->yAxis->setLabel("DSC/mW");
|
||||
_customPlot->xAxis->setLabel("Temp/℃");
|
||||
@ -156,7 +160,7 @@ void CentralWidget::slotRecvAnalysisFileName(const QString &fileName)
|
||||
_customPlot->yAxis->setRange(-20, 20);
|
||||
|
||||
QVector<double> xVtr, yVtr;
|
||||
for (FileManager::ExperimentData &ed : dataVtr)
|
||||
for (FileManager::ExperimentData &ed : _dataVtr)
|
||||
{
|
||||
xVtr.push_back(ed.sampleTemp);
|
||||
yVtr.push_back(ed.dsc);
|
||||
@ -178,8 +182,7 @@ void CentralWidget::slotAnalysisSettingApply()
|
||||
switch (_nanlysisMode) {
|
||||
case AnalysisMode::NumericalLabel:
|
||||
{
|
||||
double x = _line1->point1->coords().x();
|
||||
drawText(x,"11111");
|
||||
drawText(_line1->point1->coords(),"11111");
|
||||
break;
|
||||
}
|
||||
case AnalysisMode::PeakSynthesisAnalysis:
|
||||
@ -188,8 +191,28 @@ void CentralWidget::slotAnalysisSettingApply()
|
||||
double x2 = _line2->point1->coords().x();
|
||||
|
||||
fillGraph(x1,x2);
|
||||
//
|
||||
break;
|
||||
|
||||
PeakPoint::setRegionPointX(x1,x2);
|
||||
|
||||
QPointF point = PeakPoint::findPeakPoint();
|
||||
qDebug()<<"peak point:"<<point;
|
||||
|
||||
double enthalpyValue = 1.1;
|
||||
double peakValue = 3.1;
|
||||
double startPoint = 4.1;
|
||||
double endPoint = 5.1;
|
||||
|
||||
drawText(point,PeakPoint::textFormat(
|
||||
enthalpyValue,peakValue,startPoint,endPoint));
|
||||
|
||||
#if 0
|
||||
QString PeakPoint::textFormat(const double enthalpyValue,
|
||||
const double peakValue,
|
||||
const double startPoint,
|
||||
const double endPoint)
|
||||
#endif
|
||||
//
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
@ -261,8 +284,30 @@ void CentralWidget::setEventHandlerEnable(const bool flag)
|
||||
}
|
||||
_customPlot->replot();
|
||||
}
|
||||
void CentralWidget::drawText(const double x, const QString text)
|
||||
void CentralWidget::drawText(const QPointF point, const QString text)
|
||||
{
|
||||
// double y = PeakPoint::findClosestY(x);
|
||||
// 创建标注文字(QCPItemText)
|
||||
QCPItemText *textLabel = new QCPItemText(_customPlot);
|
||||
textLabel->setPositionAlignment(Qt::AlignBottom | Qt::AlignHCenter); // 对齐方式
|
||||
textLabel->position->setType(QCPItemPosition::ptPlotCoords); // 使用数据坐标
|
||||
textLabel->position->setCoords(point.x() + 20, point.y()); // 设置文本位置在指定点上方
|
||||
|
||||
textLabel->setText(text); // 设置文本内容
|
||||
// textLabel->setFont(QFont("Arial", 10));
|
||||
textLabel->setPen(QPen(Qt::lightGray)); // 文字边框
|
||||
textLabel->setBrush(Qt::white); // 文字背景
|
||||
|
||||
// 创建指向点的线段(QCPItemLine)
|
||||
QCPItemLine *arrow = new QCPItemLine(_customPlot);
|
||||
arrow->start->setParentAnchor(textLabel->left); // 线段起点绑定到标注文字底部
|
||||
arrow->end->setCoords(point.x(),point.y()); // 线段终点设置为指定的点
|
||||
arrow->setHead(QCPLineEnding::esSpikeArrow); // 添加箭头
|
||||
arrow->setPen(QPen(Qt::red, 1));
|
||||
|
||||
// 重绘图表以显示文本标签和箭头
|
||||
_customPlot->replot();
|
||||
|
||||
#if 0
|
||||
// 创建标注文字(QCPItemText)
|
||||
QCPItemText *textLabel = new QCPItemText(_customPlot);
|
||||
@ -311,32 +356,13 @@ void CentralWidget::drawText(const double x, const QString text)
|
||||
}
|
||||
#endif
|
||||
|
||||
double y = findClosestY(x);
|
||||
// 创建标注文字(QCPItemText)
|
||||
QCPItemText *textLabel = new QCPItemText(_customPlot);
|
||||
textLabel->setPositionAlignment(Qt::AlignBottom | Qt::AlignHCenter); // 对齐方式
|
||||
textLabel->position->setType(QCPItemPosition::ptPlotCoords); // 使用数据坐标
|
||||
textLabel->position->setCoords(x, y + 10); // 设置文本位置在指定点上方
|
||||
|
||||
textLabel->setText(text); // 设置文本内容
|
||||
textLabel->setFont(QFont("Arial", 10));
|
||||
textLabel->setPen(QPen(Qt::black)); // 文字边框
|
||||
textLabel->setBrush(Qt::white); // 文字背景
|
||||
|
||||
// 创建指向点的线段(QCPItemLine)
|
||||
QCPItemLine *arrow = new QCPItemLine(_customPlot);
|
||||
arrow->start->setParentAnchor(textLabel->bottom); // 线段起点绑定到标注文字底部
|
||||
arrow->end->setCoords(x, y); // 线段终点设置为指定的点
|
||||
arrow->setHead(QCPLineEnding::esSpikeArrow); // 添加箭头
|
||||
arrow->setPen(QPen(Qt::red, 2));
|
||||
|
||||
// 重绘图表以显示文本标签和箭头
|
||||
_customPlot->replot();
|
||||
}
|
||||
|
||||
#if 0
|
||||
double CentralWidget::findClosestY(double targetX) {
|
||||
// 获取曲线数据容器
|
||||
QSharedPointer<QCPDataContainer<QCPGraphData>> dataContainer = _graph->data();
|
||||
QSharedPointer<QCPDataContainer<QCPGraphData>> dataContainer =
|
||||
_graph->data();
|
||||
// 初始化最小差值和对应的 y 值
|
||||
double minDiff = std::numeric_limits<double>::max();
|
||||
double closestY = 0.0;
|
||||
@ -358,12 +384,14 @@ double CentralWidget::findClosestY(double targetX) {
|
||||
|
||||
return closestY;
|
||||
}
|
||||
#endif
|
||||
|
||||
void CentralWidget::fillGraph(const double x1, const double x2)
|
||||
{
|
||||
//未寻找x1\x2之间最大值。
|
||||
double y1 = findClosestY(x1);
|
||||
double y2 = findClosestY(x2);
|
||||
//todo.未寻找x1\x2之间最大值。
|
||||
|
||||
double y1 = PeakPoint::findClosestY(x1);
|
||||
double y2 = PeakPoint::findClosestY(x2);
|
||||
|
||||
QVector<double> xVtr,yVtr;
|
||||
xVtr.push_back(x1);
|
||||
@ -386,6 +414,7 @@ void CentralWidget::fillGraph(const double x1, const double x2)
|
||||
_customPlot->replot();
|
||||
}
|
||||
|
||||
|
||||
void CentralWidget::clearAllData()
|
||||
{
|
||||
|
||||
@ -418,6 +447,7 @@ void CentralWidget::clearAllData()
|
||||
}
|
||||
_customPlot->replot();
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
_line1->setVisible(false);
|
||||
_line2->setVisible(false);
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "protocol.h"
|
||||
#include "global.h"
|
||||
#include "draglinehandler.h"
|
||||
#include "filemanager.h"
|
||||
|
||||
class CentralWidget:public QWidget
|
||||
{
|
||||
@ -43,16 +44,16 @@ protected:
|
||||
void contextMenuEvent(QContextMenuEvent *event);
|
||||
private:
|
||||
void setEventHandlerEnable(const bool);
|
||||
void drawText(const double,const QString);
|
||||
double findClosestY(double targetX);
|
||||
void drawText(const QPointF,const QString);
|
||||
// double findClosestY(double targetX);
|
||||
void fillGraph(const double x1,const double x2);
|
||||
void findPeakPoint();
|
||||
private:
|
||||
AnalysisMode _nanlysisMode;
|
||||
QCustomPlot *_customPlot;
|
||||
QCPGraph* _graph;
|
||||
DragLineHandler* _eventHandler;
|
||||
QCPItemStraightLine *_line1,*_line2;
|
||||
QVector<FileManager::ExperimentData> _dataVtr;
|
||||
};
|
||||
|
||||
#endif // CENTRALWIDGET_H
|
||||
|
@ -16,7 +16,7 @@ DragLineHandler::~DragLineHandler()
|
||||
bool DragLineHandler::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if(!_enableFlag){
|
||||
qDebug()<<"_enableFlag false.";
|
||||
// qDebug()<<"_enableFlag false.";
|
||||
#if 0
|
||||
if(mPlot){
|
||||
mPlot->setCursor(Qt::ArrowCursor);
|
||||
@ -29,11 +29,11 @@ bool DragLineHandler::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::MouseMove)
|
||||
{
|
||||
qDebug()<<"mouse move...";
|
||||
// qDebug()<<"mouse move...";
|
||||
|
||||
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
|
||||
QPoint mousePos = mouseEvent->pos();
|
||||
qDebug()<<"x:"<<mousePos.x();
|
||||
// qDebug()<<"x:"<<mousePos.x();
|
||||
|
||||
bool nearLine1 = isNearLine(_line1, mousePos);
|
||||
bool nearLine2 = isNearLine(_line2, mousePos);
|
||||
|
Loading…
Reference in New Issue
Block a user