From 21247250512933d80a116274c6c625cdff0d81b3 Mon Sep 17 00:00:00 2001 From: anxinglong <2910824064@qq.com> Date: Mon, 20 Apr 2026 18:02:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BC=B9=E6=A1=86=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E6=8B=9F=E5=90=88=E5=8F=82=E6=95=B0=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E6=94=B9bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AdaptiveSimpsonIntegrate.h | 4 +- src/DataCalcProcess/FindPeaksBySvd.cpp | 2 +- .../EnergyCountPeakFitView.cpp | 357 +++++++++++++++--- .../EnergyCountPeakFitView.h | 30 +- .../PeakFitParamsDialog.cpp | 74 ++++ .../PeakFitParamsDialog.h | 28 ++ src/src.pro | 2 + 7 files changed, 439 insertions(+), 58 deletions(-) create mode 100644 src/EnergyCountPeakFitView/PeakFitParamsDialog.cpp create mode 100644 src/EnergyCountPeakFitView/PeakFitParamsDialog.h diff --git a/src/DataCalcProcess/AdaptiveSimpsonIntegrate.h b/src/DataCalcProcess/AdaptiveSimpsonIntegrate.h index 8eafc54..bbf9b33 100644 --- a/src/DataCalcProcess/AdaptiveSimpsonIntegrate.h +++ b/src/DataCalcProcess/AdaptiveSimpsonIntegrate.h @@ -1,4 +1,4 @@ -#ifndef ADAPTIVESIMPSONINTEGRATE_H +#ifndef ADAPTIVESIMPSONINTEGRATE_H #define ADAPTIVESIMPSONINTEGRATE_H namespace AdaptiveSimpsonIntegrate { @@ -6,7 +6,7 @@ namespace AdaptiveSimpsonIntegrate { #include #include #include -#include // 用于格式化输出 +//#include // 定义被积函数:y = A*exp(-(x-P)²/(2*delt²)) + H/(1+exp((x-P)/W)) + C struct FitFunction { diff --git a/src/DataCalcProcess/FindPeaksBySvd.cpp b/src/DataCalcProcess/FindPeaksBySvd.cpp index f7dd1f9..9e5d617 100644 --- a/src/DataCalcProcess/FindPeaksBySvd.cpp +++ b/src/DataCalcProcess/FindPeaksBySvd.cpp @@ -137,4 +137,4 @@ std::vector FindPeaksBySvd::FindPeaks(const arma::mat throw std::string("Find peaks unkonow exception"); } return peak_info_vec; -} \ No newline at end of file +} diff --git a/src/EnergyCountPeakFitView/EnergyCountPeakFitView.cpp b/src/EnergyCountPeakFitView/EnergyCountPeakFitView.cpp index 682e5cb..284a1b9 100644 --- a/src/EnergyCountPeakFitView/EnergyCountPeakFitView.cpp +++ b/src/EnergyCountPeakFitView/EnergyCountPeakFitView.cpp @@ -1,3 +1,8 @@ +#define NOMINMAX + +//#include +#include +#include #include "EnergyCountPeakFitView.h" #include "CustomQwtPlot.h" #include "csv.h" @@ -16,6 +21,8 @@ #include #include #include "PlotRectItem.h" +#include +#include EnergyCountPeakFitView::EnergyCountPeakFitView(QWidget *parent) @@ -36,6 +43,7 @@ EnergyCountPeakFitView::~EnergyCountPeakFitView() LOG_DEBUG(QStringLiteral(u"%1析构.").arg(this->GetViewName())); delete _rubberBand; clearAllSelectionRects(); + clearFitCurves(); } void EnergyCountPeakFitView::InitViewWorkspace(const QString &project_name) @@ -115,6 +123,12 @@ void EnergyCountPeakFitView::setupMenu() QAction* action_clear_rect = this->_menu->addAction(QStringLiteral(u"清除框选标记")); connect(action_clear_rect, &QAction::triggered, this, &EnergyCountPeakFitView::clearAllSelectionRects); + this->_menu->addSeparator(); + QAction* action_select_algorithm = this->_menu->addAction(QStringLiteral(u"选择拟合算法")); + + QAction* action_clear_fit = this->_menu->addAction(QStringLiteral(u"清除拟合曲线")); + connect(action_clear_fit, &QAction::triggered, this, &EnergyCountPeakFitView::clearFitCurves); + QAction* action_hint = this->_menu->addAction(QStringLiteral(u"框选提示:按住 Ctrl 键并拖动左键")); action_hint->setEnabled(false); } @@ -251,6 +265,7 @@ bool EnergyCountPeakFitView::eventFilter(QObject* watched, QEvent* event) me->button() == Qt::LeftButton && (me->modifiers() & Qt::ControlModifier)) { startSelection(me->pos()); + return true; } // 移动时更新橡皮筋 @@ -262,6 +277,7 @@ bool EnergyCountPeakFitView::eventFilter(QObject* watched, QEvent* event) else if (event->type() == QEvent::MouseButtonRelease && me->button() == Qt::LeftButton && _isSelecting) { finishSelection(); + clearAllSelectionRects(); return true; } } @@ -271,77 +287,132 @@ bool EnergyCountPeakFitView::eventFilter(QObject* watched, QEvent* event) void EnergyCountPeakFitView::startSelection(const QPoint& pos) { _isSelecting = true; - _selectionStart = pos; + _selectionStart = pos; - if (!_rubberBand) { - _rubberBand = new QRubberBand(QRubberBand::Rectangle, _plot->canvas()); - } - _rubberBand->setGeometry(QRect(pos, QSize())); - _rubberBand->show(); + if (!_rubberBand) { + _rubberBand = new QRubberBand(QRubberBand::Rectangle, _plot->canvas()); + } + int canvasHeight = _plot->canvas()->height(); + // 初始矩形:宽度为0,高度为画布全高 + _rubberBand->setGeometry(QRect(_selectionStart.x(), 0, 0, canvasHeight)); + _rubberBand->show(); } void EnergyCountPeakFitView::updateSelection(const QPoint& pos) { if (!_rubberBand) return; - // 计算矩形(归一化) - QRect rect = QRect(_selectionStart, pos).normalized(); - _rubberBand->setGeometry(rect); + int canvasHeight = _plot->canvas()->height(); + int x1 = _selectionStart.x(); + int x2 = pos.x(); + int left = qMin(x1, x2); + int width = qAbs(x1 - x2); + _rubberBand->setGeometry(QRect(left, 0, width, canvasHeight)); } - void EnergyCountPeakFitView::finishSelection() { - if (_rubberBand) { - QRect finalRect = _rubberBand->geometry(); - _rubberBand->hide(); - if (finalRect.width() > 2 && finalRect.height() > 2) { - QRectF plotRect = pixelRectToPlotRect(finalRect); - addSelectionRect(plotRect); // 直接添加,不清除旧的 - // TODO: 在此处添加峰拟合逻辑 - // ========== 新增拟合逻辑 ========== - QList curves = _plot->GetCurveList(); - if (curves.isEmpty()) return; - QwtPlotCurve* originalCurve = curves.first(); + if (_rubberBand) + { + QRect finalRect = _rubberBand->geometry(); + _rubberBand->hide(); - // 获取原始数据 - QVector origX, origY; - for (size_t i = 0; i < originalCurve->dataSize(); ++i) { - origX.push_back(originalCurve->sample(i).x()); - origY.push_back(originalCurve->sample(i).y()); - } + if (finalRect.width() > 2) { + const QwtScaleMap xMap = _plot->canvasMap(QwtPlot::xBottom); + double xMin = xMap.invTransform(finalRect.left()); + double xMax = xMap.invTransform(finalRect.right()); + if (xMin > xMax) std::swap(xMin, xMax); - // 提取框内的点 - QVector roiX, roiY; - for (int i = 0; i < origX.size(); ++i) { - if (origX[i] >= plotRect.left() && origX[i] <= plotRect.right() && - origY[i] >= plotRect.bottom() && origY[i] <= plotRect.top()) { - roiX.push_back(origX[i]); - roiY.push_back(origY[i]); - } - } + double yMin = _plot->axisScaleDiv(QwtPlot::yLeft).lowerBound(); + double yMax = _plot->axisScaleDiv(QwtPlot::yLeft).upperBound(); + QRectF plotRect(xMin, yMin, xMax - xMin, yMax - yMin); + addSelectionRect(plotRect); - if (roiX.size() >= 3) - { + // 获取曲线数据 + QList curves = _plot->GetCurveList(); + if (!curves.isEmpty()) { + QwtPlotCurve* originalCurve = curves.first(); + QVector origX, origY; + for (size_t i = 0; i < originalCurve->dataSize(); ++i) { + origX.push_back(originalCurve->sample(i).x()); + origY.push_back(originalCurve->sample(i).y()); + } + QVector roiX, roiY; + for (int i = 0; i < origX.size(); ++i) { + if (origX[i] >= xMin && origX[i] <= xMax) { + roiX.push_back(origX[i]); + roiY.push_back(origY[i]); + } + } + if (roiX.size() >= 3) { + QStringList algorithms; + algorithms << QStringLiteral(u"光子峰拟合算法"); + bool ok = false; + QString selected = QInputDialog::getItem(this, + QStringLiteral(u"选择拟合算法"), + QStringLiteral(u"请选择用于当前框选区域的峰拟合算法:"), + algorithms, + 0, + false, + &ok); + if (ok && selected == QStringLiteral(u"光子峰拟合算法")) { + arma::vec armaRoiX(roiX.size()), armaRoiY(roiY.size()); + for (int i = 0; i < roiX.size(); ++i) { + armaRoiX(i) = roiX[i]; + armaRoiY(i) = roiY[i]; + } + // 1. 自动估算默认初始参数(基于整个 ROI,或取第一个峰) + arma::vec defaultP0; + bool defaultValid = false; + try { + defaultP0 = EstimatePhotonPeakModelInitialParams(armaRoiX, armaRoiY); + defaultValid = true; + } catch (...) { + // 估算失败,使用硬编码默认值 + } + if (!defaultValid) { + double midEnergy = (roiX.first() + roiX.last()) / 2.0; + defaultP0 = {100.0, 2.0, 50.0, 2.0, 10.0, midEnergy}; + } + + // 2. 弹出参数设置对话框 + FitParams defaultParams; + defaultParams.A = defaultP0(0); + defaultParams.delt = defaultP0(1); + defaultParams.H = defaultP0(2); + defaultParams.W = defaultP0(3); + defaultParams.C = defaultP0(4); + defaultParams.P = defaultP0(5); + PeakFitParamsDialog paramDlg(defaultParams, this); + if (paramDlg.exec() != QDialog::Accepted) + return; // 用户取消 + + FitParams userParams = paramDlg.getParams(); + arma::vec userP0 = {userParams.A, userParams.delt, userParams.H, + userParams.W, userParams.C, userParams.P}; + + // 3. 执行拟合(曲线自动添加到主图) + QList results = performPeakFitting(roiX, roiY, &userP0); + if (results.isEmpty()) { + qDebug()<< QStringLiteral(u"拟合结果:") << QStringLiteral(u"未检测到有效峰或拟合失败。"); + } else { +// QMessageBox::information(this, "拟合结果", QString("成功拟合 %1 个峰,曲线已显示在图区。").arg(results.size())); + for (const auto& r : results) { + qDebug() << QStringLiteral(u"峰中心= %1 keV, FWHM= %2, 面积= %3") + .arg(r.center).arg(r.fwhm).arg(r.area); + } + } + } + } else { + qDebug() << QStringLiteral(u"框选区域内数据点不足3个,无法拟合。"); + } + } } } - } - - _isSelecting = false; + _isSelecting = false; } -QRectF EnergyCountPeakFitView::pixelRectToPlotRect(const QRect& pixelRect) const -{ - const QwtScaleMap xMap = _plot->canvasMap(QwtPlot::xBottom); - const QwtScaleMap yMap = _plot->canvasMap(QwtPlot::yLeft); - double x1 = xMap.invTransform(pixelRect.left()); - double x2 = xMap.invTransform(pixelRect.right()); - double y1 = yMap.invTransform(pixelRect.top()); - double y2 = yMap.invTransform(pixelRect.bottom()); - - return QRectF(QPointF(x1, y1), QPointF(x2, y2)).normalized(); -} void EnergyCountPeakFitView::addSelectionRect(const QRectF &plotRect) { @@ -352,3 +423,185 @@ void EnergyCountPeakFitView::addSelectionRect(const QRectF &plotRect) _selectionRectItems.append(rectItem); _plot->replot(); } + +QList EnergyCountPeakFitView::performPeakFitting(const QVector &x, const QVector &y, const arma::vec* userP0) +{ + // 清除之前的拟合曲线(每次拟合全新绘制) +// clearFitCurves(); + + QList results; + if (x.size() != y.size() || x.size() < 3) + { + qDebug() << QStringLiteral(u"数据长度不足"); + return results; + } + + // 转换为 Armadillo 向量 + arma::vec armaX(x.size()), armaY(y.size()); + for (int i = 0; i < x.size(); ++i) { + armaX(i) = x[i]; + armaY(i) = y[i]; + } + + const int step_w = 20; + if (armaY.n_elem < step_w) { + qDebug() << QStringLiteral(u"ROI 数据点太少,跳过寻峰。"); + return results; + } + if (arma::all(armaY == 0)) { + qDebug() << QStringLiteral(u"ROI 计数全为零,跳过寻峰。"); + return results; + } + + // 构建两列矩阵:第一列为能量,第二列为计数 + arma::mat spec_data(armaX.n_rows, 2); + spec_data.col(0) = armaX; + spec_data.col(1) = armaY; + + FindPeaksBySvd peakFinder; + std::vector peaks; + try { + peaks = peakFinder.FindPeaks(spec_data, step_w); + } catch (const std::string& s) { + qDebug() << QStringLiteral(u"FindPeaks 异常:") << s.c_str(); + return results; + } catch (const std::exception& e) { + qDebug() << QStringLiteral(u"FindPeaks 异常:") << e.what(); + return results; + } catch (...) { + qDebug() << QStringLiteral(u"FindPeaks 未知异常"); + return results; + } + + if (peaks.empty()) { + qDebug() << QStringLiteral(u"未检测到峰。"); + return results; + } + + const double fitRangeEnergy = 15.0; + for (const auto& pinfo : peaks) { + int idxCenter = pinfo.pos; + if (idxCenter < 0 || idxCenter >= x.size()) + continue; + double centerEnergy = armaX(idxCenter); + + // 提取局部数据 + QVector localX, localY; + for (int i = 0; i < x.size(); ++i) { + if (x[i] >= centerEnergy - fitRangeEnergy && x[i] <= centerEnergy + fitRangeEnergy) { + localX.push_back(x[i]); + localY.push_back(y[i]); + } + } + if (localX.size() < 5) { + qDebug() << QStringLiteral(u"局部数据点不足5,跳过峰 at") << centerEnergy; + continue; + } + + arma::vec xLocal(localX.size()), yLocal(localY.size()); + for (int i = 0; i < localX.size(); ++i) { + xLocal(i) = localX[i]; + yLocal(i) = localY[i]; + } + + arma::vec p0; + if (userP0 && userP0->n_elem == 6) { + p0 = *userP0; // 使用用户提供的初值 + } else { + try { + p0 = EstimatePhotonPeakModelInitialParams(xLocal, yLocal); + } catch (...) { + qDebug() << QStringLiteral(u"初始参数估算失败,跳过峰 at") << centerEnergy; + continue; + } + } + + arma::vec p_fit; + try { + p_fit = NolinearLeastSquaresCurveFit::Lsqcurvefit(PhotonPeakModel, xLocal, yLocal, p0); + } catch (const std::exception& e) { + qDebug() << QStringLiteral(u"拟合失败:") << e.what(); + continue; + } + + double sigma = p_fit(1); + double fwhm = sigma * 2.355; + + AdaptiveSimpsonIntegrate::FitFunction fitFunc; + fitFunc.A = p_fit(0); + fitFunc.delt= p_fit(1); + fitFunc.H = p_fit(2); + fitFunc.W = p_fit(3); + fitFunc.C = p_fit(4); + fitFunc.P = p_fit(5); + double lower = fitFunc.P - 3.0 * fitFunc.delt; + double upper = fitFunc.P + 3.0 * fitFunc.delt; + double area = AdaptiveSimpsonIntegrate::integrate(fitFunc, lower, upper); + + PeakFitResult result; + result.center = p_fit(5); + result.amplitude = p_fit(0); + result.sigma = p_fit(1); + result.fwhm = fwhm; + result.area = area; + result.baseline = p_fit(4); + result.sigmoidH = p_fit(2); + result.sigmoidW = p_fit(3); +// result.localX = localX; // 保存局部数据,备用 +// result.localY = localY; + + // 绘制拟合曲线到主图 + double xMinPlot = result.center - 3 * result.sigma; + double xMaxPlot = result.center + 3 * result.sigma; + xMinPlot = qMax(xMinPlot, localX.first()); + xMaxPlot = qMin(xMaxPlot, localX.last()); + QString curveName = QStringLiteral(u"Fit_%1keV").arg(result.center, 0, 'f', 2); + QwtPlotCurve* fitCurve = createFitCurve(result, xMinPlot, xMaxPlot, curveName); + _fitCurves.append(fitCurve); + + results.append(result); + } + + _plot->replot(); // 刷新显示拟合曲线 + return results; +} + +QwtPlotCurve *EnergyCountPeakFitView::createFitCurve(const PeakFitResult &result, double xMin, double xMax, const QString &name) +{ + const int numPoints = 200; + QVector xs, ys; + double step = (xMax - xMin) / (numPoints - 1); + + arma::vec p(6); + p(0) = result.amplitude; + p(1) = result.sigma; + p(2) = result.sigmoidH; + p(3) = result.sigmoidW; + p(4) = result.baseline; + p(5) = result.center; + + for (int i = 0; i < numPoints; ++i) { + double x = xMin + i * step; + xs.append(x); + double y = PhotonPeakModel(x, p); + ys.append(y); + } + + QwtPlotCurve* curve = new QwtPlotCurve(name); + curve->setSamples(xs, ys); + curve->setPen(QPen(Qt::blue, 2)); + curve->attach(_plot); + return curve; +} + + +void EnergyCountPeakFitView::clearFitCurves() +{ + for (QwtPlotCurve* curve : _fitCurves) { + curve->detach(); + delete curve; + } + _fitCurves.clear(); + _plot->replot(); +} + diff --git a/src/EnergyCountPeakFitView/EnergyCountPeakFitView.h b/src/EnergyCountPeakFitView/EnergyCountPeakFitView.h index b98df50..c9d9c40 100644 --- a/src/EnergyCountPeakFitView/EnergyCountPeakFitView.h +++ b/src/EnergyCountPeakFitView/EnergyCountPeakFitView.h @@ -1,6 +1,5 @@ #ifndef ENERGYCOUNTPEAKFITVIEW_H #define ENERGYCOUNTPEAKFITVIEW_H - #include #include #include @@ -8,6 +7,25 @@ #include #include // 新增 #include // 新增 +#include "PeakFitParamsDialog.h" + +#include "DataCalcProcess/FindPeaksBySvd.h" +#include "DataCalcProcess/NolinearLeastSquaresCurveFit.h" +#include "DataCalcProcess/AdaptiveSimpsonIntegrate.h" +#include "DataCalcProcess/MathModelDefine.h" + +struct PeakFitResult +{ + double center; // 峰中心能量 (keV) + double amplitude; // 高斯振幅 + double sigma; // 高斯宽度 + double fwhm; // 半高宽 = sigma * 2.355 + double area; // 峰面积(积分) + double baseline; // 常数项 C + double sigmoidH; // Sigmoid 项高度 H + double sigmoidW; // Sigmoid 项宽度 W + }; + class PlotRectItem; // 前向声明 class QMenu; @@ -15,6 +33,7 @@ class CustomQwtPlot; class CustomQwtPlotXaxisSelector; class QwtPlotPicker; class QwtPlotCurve; + class EnergyCountPeakFitView : public MeasureAnalysisView { Q_OBJECT @@ -34,6 +53,8 @@ private slots: void onActionCurveShowSetting(); void onActionPlotConfigure(); void clearAllSelectionRects(); + void clearFitCurves(); // 清除所有拟合曲线 + protected: bool eventFilter(QObject* watched, QEvent* event) override; // 事件过滤器 @@ -41,10 +62,11 @@ private: void startSelection(const QPoint& pos); void updateSelection(const QPoint& pos); void finishSelection(); - QRectF pixelRectToPlotRect(const QRect& pixelRect) const; void addSelectionRect(const QRectF& plotRect); - +QList performPeakFitting(const QVector& x, const QVector& y, const arma::vec* userP0 = nullptr); + // 根据拟合参数生成曲线 + QwtPlotCurve* createFitCurve(const PeakFitResult& result, double xMin, double xMax, const QString& name); private: CustomQwtPlot* _plot = nullptr; QMenu* _menu = nullptr; @@ -58,6 +80,8 @@ private: QwtPlotCurve* _fittedCurve = nullptr; // 显示拟合结果的曲线 + // 存储当前显示的拟合曲线,用于清除 + QList _fitCurves; }; diff --git a/src/EnergyCountPeakFitView/PeakFitParamsDialog.cpp b/src/EnergyCountPeakFitView/PeakFitParamsDialog.cpp new file mode 100644 index 0000000..942cae6 --- /dev/null +++ b/src/EnergyCountPeakFitView/PeakFitParamsDialog.cpp @@ -0,0 +1,74 @@ +#include "PeakFitParamsDialog.h" + +PeakFitParamsDialog::PeakFitParamsDialog(const FitParams& defaultParams, QWidget* parent) + : QDialog(parent) +{ + setWindowTitle(QStringLiteral(u"光子峰拟合参数设置")); + setModal(true); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + QFormLayout* form = new QFormLayout(); + + m_A = new QDoubleSpinBox(); + m_A->setRange(0, 1e9); + m_A->setDecimals(2); + m_A->setValue(defaultParams.A); + m_A->setToolTip(QStringLiteral(u"高斯峰幅度")); + + m_delt = new QDoubleSpinBox(); + m_delt->setRange(0.01, 100); + m_delt->setDecimals(3); + m_delt->setValue(defaultParams.delt); + m_delt->setToolTip(QStringLiteral(u"高斯宽度 (sigma)")); + + m_H = new QDoubleSpinBox(); + m_H->setRange(-1e9, 1e9); + m_H->setDecimals(2); + m_H->setValue(defaultParams.H); + m_H->setToolTip(QStringLiteral(u"Sigmoid 函数幅度")); + + m_W = new QDoubleSpinBox(); + m_W->setRange(0.01, 100); + m_W->setDecimals(3); + m_W->setValue(defaultParams.W); + m_W->setToolTip(QStringLiteral(u"Sigmoid 宽度")); + + m_C = new QDoubleSpinBox(); + m_C->setRange(-1e9, 1e9); + m_C->setDecimals(2); + m_C->setValue(defaultParams.C); + m_C->setToolTip(QStringLiteral(u"基线常数")); + + m_P = new QDoubleSpinBox(); + m_P->setRange(-1e9, 1e9); + m_P->setDecimals(3); + m_P->setValue(defaultParams.P); + m_P->setToolTip(QStringLiteral(u"峰中心能量")); + + form->addRow(QStringLiteral(u"A (振幅):"), m_A); + form->addRow(QStringLiteral(u"delt (高斯宽度):"), m_delt); + form->addRow(QStringLiteral(u"H (Sigmoid 幅度):"), m_H); + form->addRow(QStringLiteral(u"W (Sigmoid 宽度):"), m_W); + form->addRow(QStringLiteral(u"C (基线):"), m_C); + form->addRow(QStringLiteral(u"P (峰中心):"), m_P); + + mainLayout->addLayout(form); + + QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + mainLayout->addWidget(buttonBox); +} + +FitParams PeakFitParamsDialog::getParams() const +{ + FitParams p; + p.accepted = (result() == QDialog::Accepted); + p.A = m_A->value(); + p.delt = m_delt->value(); + p.H = m_H->value(); + p.W = m_W->value(); + p.C = m_C->value(); + p.P = m_P->value(); + return p; +} diff --git a/src/EnergyCountPeakFitView/PeakFitParamsDialog.h b/src/EnergyCountPeakFitView/PeakFitParamsDialog.h new file mode 100644 index 0000000..8ac66dc --- /dev/null +++ b/src/EnergyCountPeakFitView/PeakFitParamsDialog.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct FitParams { + bool accepted = false; + double A; // 振幅 + double delt; // sigma + double H; // sigmoid 幅度 + double W; // sigmoid 宽度 + double C; // 基线 + double P; // 峰中心 +}; + +class PeakFitParamsDialog : public QDialog +{ + Q_OBJECT +public: + explicit PeakFitParamsDialog(const FitParams& defaultParams, QWidget* parent = nullptr); + FitParams getParams() const; + +private: + QDoubleSpinBox *m_A, *m_delt, *m_H, *m_W, *m_C, *m_P; +}; diff --git a/src/src.pro b/src/src.pro index c50b172..0b39fa0 100644 --- a/src/src.pro +++ b/src/src.pro @@ -74,6 +74,7 @@ SOURCES += \ DeviceParameterConfig/DeviceParameterConfig.cpp \ DeviceParameterConfig/DeviceParameterConfigList.cpp \ EnergyCountPeakFitView/EnergyCountPeakFitView.cpp \ + EnergyCountPeakFitView/PeakFitParamsDialog.cpp \ EnergyCountPeakFitView/PlotRectItem.cpp \ EnergyCountPlotView/EnergyCountPlotView.cpp \ EnergyScaleDataModel.cpp \ @@ -120,6 +121,7 @@ HEADERS += \ DeviceParameterConfig/DeviceParameterConfig.h \ DeviceParameterConfig/DeviceParameterConfigList.h \ EnergyCountPeakFitView/EnergyCountPeakFitView.h \ + EnergyCountPeakFitView/PeakFitParamsDialog.h \ EnergyCountPeakFitView/PlotRectItem.h \ EnergyCountPlotView/EnergyCountPlotView.h \ EnergyScaleDataModel.h \