2025-03-26T17:30:00

This commit is contained in:
yuntang 2025-03-26 17:30:02 +08:00
parent 6c19e0fc79
commit 86dbbe706d
7 changed files with 347 additions and 40 deletions

View File

@ -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 \

View File

@ -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
View 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
View 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

View File

@ -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);

View File

@ -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

View File

@ -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);