修改射线列表下拉框,修改核素分析为框选区域,修改核素区域框选完成以后弹出能量范围的核素

This commit is contained in:
anxinglong 2026-06-23 18:00:10 +08:00
parent 03daa073cc
commit 1c7784702f
7 changed files with 730 additions and 1 deletions

View File

@ -125,6 +125,8 @@ std::vector<FindPeaksBySvd::PeakInfo> FindPeaksBySvd::FindPeaks(const arma::mat
peak_info.pos = spec_data_col0.at(pos);
peak_info.fwhm = fwhm;
peak_info.area = area;
peak_info.leftBoundary = left_half;
peak_info.rightBoundary = right_half;
peak_info_vec.push_back(peak_info);
}
}

View File

@ -15,6 +15,8 @@ public:
double pos = 0.0f; // 峰所在位置
double fwhm = 0.0f; // 峰的半高宽
double area = 0.0f; // 峰的面积
double leftBoundary = 0.0f;//左边界
double rightBoundary = 0.0f;//右边界
} PeakInfo;
std::vector<FindPeaksBySvd::PeakInfo> FindPeaks(const arma::mat& spec_data, int step_w = 7) throw(std::string);

View File

@ -1286,3 +1286,9 @@ void MainWindow::on_action_stop_measure_triggered()
_measure_client->stopMeasure(device_guid);
m_AddressCountTimer->stop();
}
void MainWindow::on_action_device_connect_cfg_triggered()
{
}

View File

@ -100,6 +100,9 @@ private slots:
void on_AddressCountTimer();
void on_EnergyCountTimer();
//设备连接管理
void on_action_device_connect_cfg_triggered();
private:
QMutex _mutex_info_output;
QPlainTextEdit* _plain_edit_info_output;

View File

@ -10,6 +10,53 @@
#include <QwtPlotCanvas>
#include <QwtText>
#include <QPen>
// 更新选择框显示
static void updateSelectionMarkers(QwtPlot* plot,
QwtPlotMarker*& leftMarker,
QwtPlotMarker*& rightMarker,
double x1, double x2)
{
double x_left = qMin(x1, x2);
double x_right = qMax(x1, x2);
// 左边界
if (!leftMarker) {
leftMarker = new QwtPlotMarker();
leftMarker->setLineStyle(QwtPlotMarker::VLine);
leftMarker->setLinePen(QPen(Qt::blue, 1, Qt::DashLine));
leftMarker->attach(plot);
}
leftMarker->setValue(x_left, 0);
// 右边界
if (!rightMarker) {
rightMarker = new QwtPlotMarker();
rightMarker->setLineStyle(QwtPlotMarker::VLine);
rightMarker->setLinePen(QPen(Qt::blue, 1, Qt::DashLine));
rightMarker->attach(plot);
}
rightMarker->setValue(x_right, 0);
plot->replot();
}
// 清除选择框
static void clearSelectionMarkers(QwtPlotMarker*& leftMarker,
QwtPlotMarker*& rightMarker)
{
if (leftMarker) {
leftMarker->detach();
delete leftMarker;
leftMarker = nullptr;
}
if (rightMarker) {
rightMarker->detach();
delete rightMarker;
rightMarker = nullptr;
}
}
NuclideAnalysisView::NuclideAnalysisView(QWidget* parent)
: MeasureAnalysisView { parent }
{
@ -48,8 +95,75 @@ void NuclideAnalysisView::SetAnalyzeDataFilename(const QMap<QString, QVariant>&
const QString& data_filename = data_files_set.first().toString();
if (QFileInfo(data_filename).exists()) {
loadDataFromFile(data_name, data_filename);
}
}
}
bool NuclideAnalysisView::eventFilter(QObject *obj, QEvent *event)
{
if (obj == _plot->canvas()) {
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
// 鼠标按下:记录起始位置
if (event->type() == QEvent::MouseButtonPress) {
if (mouseEvent->button() == Qt::LeftButton) {
m_bSelecting = true;
m_selectStartX = _plot->invTransform(QwtPlot::xBottom, mouseEvent->pos().x());
m_selectStartPos = mouseEvent->pos();
updateSelectionMarkers(_plot, m_selectLeftMarker, m_selectRightMarker,
m_selectStartX, m_selectStartX);
return true;
}
}
// 鼠标移动:更新选择框
if (event->type() == QEvent::MouseMove) {
if (m_bSelecting) {
double xValue = _plot->invTransform(QwtPlot::xBottom, mouseEvent->pos().x());
updateSelectionMarkers(_plot, m_selectLeftMarker, m_selectRightMarker,
m_selectStartX, xValue);
return true;
}
}
// 鼠标释放
if (event->type() == QEvent::MouseButtonRelease) {
if (mouseEvent->button() == Qt::LeftButton && m_bSelecting) {
m_bSelecting = false;
double xEnd = _plot->invTransform(QwtPlot::xBottom, mouseEvent->pos().x());
int dx = mouseEvent->pos().x() - m_selectStartPos.x();
int dy = mouseEvent->pos().y() - m_selectStartPos.y();
double moveDistance = sqrt(dx*dx + dy*dy);
// 清除选择框
clearSelectionMarkers(m_selectLeftMarker, m_selectRightMarker);
_plot->replot();
if (moveDistance > 10)
{
double x1 = qMin(m_selectStartX, xEnd);
double x2 = qMax(m_selectStartX, xEnd);
if (x2 - x1 > 1.0 && HasSpectrumData())
{
PeakFitResultNuclide result = FitPeakByRange(x1, x2, m_spectrumX, m_spectrumY);
if (result.success)
{
// 追加到累积结果
m_allFitResults.append(result);
DrawPeaks(m_allFitResults);
queryNuclidesForPeak(result);
}
}
}
return true;
}
}
}
return MeasureAnalysisView::eventFilter(obj, event);
}
void NuclideAnalysisView::setupPlot()
@ -73,6 +187,9 @@ void NuclideAnalysisView::setupPlot()
_plot->SetAxisDragScale(QwtPlot::xBottom, true);
// 启用鼠标追踪
_plot->canvas()->setMouseTracking(true);
// 给canvas安装事件过滤器捕获鼠标点击
_plot->canvas()->installEventFilter(this);
}
void NuclideAnalysisView::setupMenu()
@ -92,6 +209,18 @@ void NuclideAnalysisView::setupMenu()
connect(action_plot_config, &QAction::triggered, this, &NuclideAnalysisView::onActionPlotConfigure);
this->_menu->addSeparator();
QAction* action_plot_select = this->_menu->addAction(QStringLiteral(u"在谱线上选择"));
action_plot_select->setObjectName("plot_select");
connect(action_plot_select, &QAction::triggered, this, &NuclideAnalysisView::onActionPlotSelect);
this->_menu->addSeparator();
QAction* action_plot_clear = this->_menu->addAction(QStringLiteral(u"清除"));
action_plot_clear->setObjectName("plot_clear");
connect(action_plot_clear, &QAction::triggered, this, &NuclideAnalysisView::onActionPlotClear);
this->_menu->addSeparator();
setupNuclideDialog();
}
void NuclideAnalysisView::loadDataFromFile(const QString& data_name, const QString& filename)
@ -130,9 +259,516 @@ void NuclideAnalysisView::loadDataFromFile(const QString& data_name, const QStri
curve->setPen(QPen(Qt::gray, 2)); // 原始数据统一灰色
curve->setSamples(x, y);
_plot->AddCurve(curve,false);
m_spectrumX = x;
m_spectrumY = y;
// QVector<PeakFitResultNuclide> results =FitThreePeaksFromSpectrum(x,y);
// DrawPeaks(results,y);
}
void NuclideAnalysisView::onActionPlotConfigure()
{
}
void NuclideAnalysisView::onActionPlotSelect()
{
m_bSelectHS = true;
}
void NuclideAnalysisView::onActionPlotClear()
{
if (!_plot) {
return;
}
for (int i = 0; i < m_peakMarkers.size(); ++i) {
if (m_peakMarkers[i]) {
m_peakMarkers[i]->detach();
delete m_peakMarkers[i];
}
}
m_peakMarkers.clear();
if (_vLineMarker) {
_vLineMarker->detach();
delete _vLineMarker;
_vLineMarker = nullptr;
}
if (m_selectLeftMarker) {
m_selectLeftMarker->detach();
delete m_selectLeftMarker;
m_selectLeftMarker = nullptr;
}
if (m_selectRightMarker) {
m_selectRightMarker->detach();
delete m_selectRightMarker;
m_selectRightMarker = nullptr;
}
m_allFitResults.clear();
m_bSelecting = false;
m_bSelectHS = false;
_plot->replot();
}
void NuclideAnalysisView::addVerticalLine(double xValue, const QColor& color)
{
if(!m_bSelectHS)
return;
m_bSelectHS = false;
if (!_vLineMarker) {
_vLineMarker = new QwtPlotMarker();
_vLineMarker->setLineStyle(QwtPlotMarker::VLine);
_vLineMarker->setLinePen(QPen(color, 1, Qt::SolidLine));
_vLineMarker->attach(_plot);
} else {
_vLineMarker->setLinePen(QPen(color, 1, Qt::SolidLine));
}
_vLineMarker->setValue(xValue, 0);
_plot->replot();
}
// 用光子峰模型拟合并计算单个峰的半高宽
PeakFitResultNuclide NuclideAnalysisView::FitPhotonPeakCalcFwhm(const QVector<double>& x, const QVector<double>& y)
{
PeakFitResultNuclide result;
result.success = false;
result.center = 0.0;
result.amplitude = 0.0;
result.sigma = 0.0;
result.fwhm = 0.0;
result.area = 0.0;
result.baseline = 0.0;
result.sigmoidH = 0.0;
result.sigmoidW = 0.0;
// 参数校验
if (x.size() < 6 || y.size() < 6 || x.size() != y.size()) {
return result;
}
int n = x.size();
arma::vec arma_x(n);
arma::vec arma_y(n);
for (int i = 0; i < n; ++i) {
arma_x(i) = x[i];
arma_y(i) = y[i];
}
arma::vec p0 = EstimatePhotonPeakModelInitialParams(arma_x, arma_y);
arma::vec p_fit = NolinearLeastSquaresCurveFit::Lsqcurvefit(PhotonPeakModel, arma_x, arma_y, p0);
if (p_fit.n_elem >= 6) {
double A = p_fit(0); // 高斯振幅
double delt = p_fit(1); // 高斯宽度sigma
double H = p_fit(2); // Sigmoid 高度
double W = p_fit(3); // Sigmoid 宽度
double C = p_fit(4); // 常数本底
double P = p_fit(5); // 峰位
double fwhm = delt * 2.355;
double area = A * delt * 2.5066; // sqrt(2π) ≈ 2.5066
result.success = true;
result.center = P;
result.amplitude = A;
result.sigma = delt;
result.fwhm = fwhm;
result.area = area;
result.baseline = C;
result.sigmoidH = H;
result.sigmoidW = W;
}
return result;
}
// 根据x范围截取数据并拟合计FWHM
PeakFitResultNuclide NuclideAnalysisView::FitPeakByRange(double x_start, double x_end,
const QVector<double>& x, const QVector<double>& y)
{
PeakFitResultNuclide result;
result.success = false;
if (x.size() != y.size() || x_start >= x_end) {
return result;
}
// 截取指定范围内的数据
QVector<double> x_peak, y_peak;
for (int i = 0; i < x.size(); ++i) {
if (x[i] >= x_start && x[i] <= x_end) {
x_peak.append(x[i]);
y_peak.append(y[i]);
}
}
// 调用拟合函数
result = FitPhotonPeakCalcFwhm(x_peak, y_peak);
return result;
}
// 从整张谱中自动寻找三个最强的峰并计算FWHMSVD寻峰
QVector<PeakFitResultNuclide> NuclideAnalysisView::FitThreePeaksFromSpectrum(
const QVector<double>& x,
const QVector<double>& y,
double height_threshold,
int min_distance)
{
QVector<PeakFitResultNuclide> results;
if (x.size() < 10 || y.size() < 10 || x.size() != y.size()) {
return results;
}
// 将 QVector 转换为 arma::matFindPeaksBySvd 需要矩阵格式)
int n = x.size();
arma::mat spec_data(n, 2);
for (int i = 0; i < n; ++i) {
spec_data(i, 0) = x[i]; // 第一列x能量/道址)
spec_data(i, 1) = y[i]; // 第二列y计数
}
FindPeaksBySvd peakFinder;
std::vector<FindPeaksBySvd::PeakInfo> peaks;
try {
peaks = peakFinder.FindPeaks(spec_data, 40); // step_w = 7
} catch (const std::string& e) {
return results;
}
if (peaks.empty()) {
return results;
}
// std::sort(peaks.begin(), peaks.end(),
// [](const FindPeaksBySvd::PeakInfo& a, const FindPeaksBySvd::PeakInfo& b) {
// return a.height > b.height;
// });
// if (peaks.size() > 3) {
// peaks.resize(3);
// }
std::sort(peaks.begin(), peaks.end(),
[](const FindPeaksBySvd::PeakInfo& a, const FindPeaksBySvd::PeakInfo& b) {
return a.pos < b.pos;
});
for (size_t i = 0; i < peaks.size(); ++i) {
const FindPeaksBySvd::PeakInfo& peak = peaks[i];
double peak_pos = peak.pos;
double window = peak.width;
if (window < 10.0) window = 10.0;
double x_start = peak_pos - window;
double x_end = peak_pos + window;
if (x_start < x.first()) x_start = x.first();
if (x_end > x.last()) x_end = x.last();
QVector<double> x_peak, y_peak;
for (int j = 0; j < x.size(); ++j) {
if (x[j] >= x_start && x[j] <= x_end) {
x_peak.append(x[j]);
y_peak.append(y[j]);
}
}
PeakFitResultNuclide result = FitPhotonPeakCalcFwhm(x_peak, y_peak);
results.append(result);
}
return results;
}
// 绘制检测到的峰(矩形表示峰位和峰宽)
void NuclideAnalysisView::DrawPeaks(const QVector<PeakFitResultNuclide>& peaks)
{
if (!_plot || peaks.isEmpty()) {
return;
}
// 先清除之前的标记
ClearPeakMarkers();
// 获取Y轴最大值用于放置标签
double y_max = 0;
if (!m_spectrumY.isEmpty()) {
y_max = *std::max_element(m_spectrumY.begin(), m_spectrumY.end());
}
// 为每个峰创建矩形标记
for (int i = 0; i < peaks.size(); ++i) {
if (!peaks[i].success) {
continue;
}
double center = peaks[i].center;
double fwhm = peaks[i].fwhm;
double amplitude = peaks[i].amplitude;
double baseline = peaks[i].baseline;
QColor color = Qt::red;
// 半透明填充色
QColor fillColor = color;
fillColor.setAlpha(5); // 透明度 0-255
// 1. 矩形(表示峰位和峰宽)
// 矩形范围x = [center - fwhm/2, center + fwhm/2]
// y = [baseline, baseline + amplitude]
double x_left = center - fwhm / 2.0;
double x_right = center + fwhm / 2.0;
double y_bottom = 0.0;
double y_top = y_max;
// 用 QwtPlotCurve 绘制闭合矩形
QPolygonF rectPoints;
rectPoints << QPointF(x_left, y_bottom);
rectPoints << QPointF(x_right, y_bottom);
rectPoints << QPointF(x_right, y_top);
rectPoints << QPointF(x_left, y_top);
rectPoints << QPointF(x_left, y_bottom); // 闭合
QwtPlotCurve* rectCurve = new QwtPlotCurve();
rectCurve->setSamples(rectPoints);
rectCurve->setPen(QPen(color, 2, Qt::SolidLine));
rectCurve->setBrush(QBrush(fillColor));
rectCurve->attach(_plot);
m_peakMarkers.append(static_cast<QwtPlotItem*>(rectCurve));
// 2. 峰位竖线(中心线,虚线)
QwtPlotMarker* centerLine = new QwtPlotMarker();
centerLine->setLineStyle(QwtPlotMarker::VLine);
centerLine->setLinePen(QPen(color, 1, Qt::DotLine));
centerLine->setValue(center, 0);
centerLine->attach(_plot);
m_peakMarkers.append(centerLine);
// 3. 文本标签
QString labelText = QString("位置: %1 keV\nFWHM: %2 keV\n峰面积: %3")
.arg(center, 0, 'f', 1)
.arg(fwhm, 0, 'f', 2)
.arg(peaks[i].area, 0, 'f', 0);
QwtPlotMarker* textLabel = new QwtPlotMarker();
textLabel->setLabel(QwtText(labelText));
textLabel->setLabelAlignment(Qt::AlignLeft | Qt::AlignTop);
textLabel->setLabelOrientation(Qt::Horizontal);
double label_y = y_top + y_max * 0.02;
if (label_y > y_max * 0.9) {
label_y = y_top - y_max * 0.15;
textLabel->setLabelAlignment(Qt::AlignLeft | Qt::AlignTop);
}
textLabel->setValue(x_left, label_y);
textLabel->attach(_plot);
m_peakMarkers.append(textLabel);
}
_plot->replot();
}
// 清除所有峰标记
void NuclideAnalysisView::ClearPeakMarkers()
{
if (!_plot) {
return;
}
// 分离并删除所有峰标记
for (int i = 0; i < m_peakMarkers.size(); ++i) {
if (m_peakMarkers[i]) {
m_peakMarkers[i]->detach();
delete m_peakMarkers[i];
}
}
m_peakMarkers.clear();
_plot->replot();
}
bool NuclideAnalysisView::HasSpectrumData() const
{
return !m_spectrumX.isEmpty();
}
std::vector<FindPeaksBySvd::PeakInfo> NuclideAnalysisView::GetSvdPeaks(const QVector<double> &x, const QVector<double> &y, int step_w)
{
std::vector<FindPeaksBySvd::PeakInfo> peaks;
if (x.size() < 10 || y.size() < 10 || x.size() != y.size()) {
return peaks;
}
int n = x.size();
arma::mat spec_data(n, 2);
for (int i = 0; i < n; ++i) {
spec_data(i, 0) = x[i];
spec_data(i, 1) = y[i];
}
FindPeaksBySvd peakFinder;
try {
peaks = peakFinder.FindPeaks(spec_data, step_w);
} catch (const std::string& e) {
}
return peaks;
}
void NuclideAnalysisView::setupNuclideDialog()
{
m_nuclideDialog = new QDialog(this);
m_nuclideDialog->setWindowTitle(QStringLiteral(u"匹配核素射线"));
m_nuclideDialog->resize(520, 400);
QVBoxLayout* layout = new QVBoxLayout(m_nuclideDialog);
layout->setContentsMargins(6, 6, 6, 6);
m_nuclideTable = new QTableWidget(m_nuclideDialog);
QStringList headers = { "核素", "射线类型", "能量(keV)","操作" };
m_nuclideTable->setColumnCount(headers.size());
m_nuclideTable->setHorizontalHeaderLabels(headers);
// m_nuclideTable->horizontalHeader()->setStretchLastSection(true);
m_nuclideTable->setSelectionBehavior(QAbstractItemView::SelectRows);
m_nuclideTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_nuclideTable->setAlternatingRowColors(true);
layout->addWidget(m_nuclideTable);
}
void NuclideAnalysisView::queryNuclidesForPeak(const PeakFitResultNuclide& peak)
{
if (!peak.success) return;
if (!openNuclideDatabase()) {
m_nuclideTable->setRowCount(1);
m_nuclideTable->setItem(0, 0, new QTableWidgetItem(QStringLiteral(u"数据库未打开")));
m_nuclideDialog->show();
return;
}
double eMin = peak.center - peak.fwhm;
double eMax = peak.center + peak.fwhm;
double center = peak.center;
auto& db = SqliteManager::instance();
QString sql = "SELECT ni.NUCLIDE_NAME, nri.RAY_TYPE, nri.RAY_MEV, "
"nri.RAY_BRANCH_RATIO, nri.RAY_MEV_UNCERTAINTY "
"FROM nuclideRayInfo nri "
"INNER JOIN nuclideLib ni ON nri.NUCLIDE_ID = ni.ID;";
auto allRows = db.selectRows(sql);
struct MatchItem {
QString nuclideName;
QString rayType;
QString energyDisplay;
double sortValue;
};
QVector<MatchItem> matches;
for (const auto& row : allRows) {
QString mevStr = row["RAY_MEV"].toString().trimmed();
if (mevStr.isEmpty()) continue;
QStringList energyList = mevStr.split(',');
for (const QString& eStr : energyList) {
QString trimmed = eStr.trimmed();
if (trimmed.isEmpty()) continue;
bool matched = false;
QString displayStr;
double sortVal = 0.0;
if (trimmed.contains('-')) {
// 范围形式:如 "11.386-12.032"
QStringList rangeParts = trimmed.split('-');
if (rangeParts.size() == 2) {
bool ok1 = false, ok2 = false;
double low = rangeParts[0].trimmed().toDouble(&ok1);
double high = rangeParts[1].trimmed().toDouble(&ok2);
if (ok1 && ok2 && low <= high) {
// 范围与搜索窗口有重叠就算匹配
if (high >= eMin && low <= eMax) {
matched = true;
displayStr = trimmed;
// 取范围内最接近峰位的值用于排序
if (center < low) sortVal = low;
else if (center > high) sortVal = high;
else sortVal = center;
}
}
}
} else {
// 单值形式
bool ok = false;
double energy = trimmed.toDouble(&ok);
if (ok && energy >= eMin && energy <= eMax) {
matched = true;
displayStr = trimmed;
sortVal = energy;
}
}
if (matched) {
MatchItem item;
item.nuclideName = row["NUCLIDE_NAME"].toString();
item.rayType = row["RAY_TYPE"].toString();
item.energyDisplay = displayStr;
item.sortValue = sortVal;
matches.append(item);
}
}
}
// 按与峰位的接近程度排序
std::sort(matches.begin(), matches.end(),
[center](const MatchItem& a, const MatchItem& b) {
return qAbs(a.sortValue - center) < qAbs(b.sortValue - center);
});
m_nuclideTable->setRowCount(matches.size());
for (int i = 0; i < matches.size(); ++i) {
const auto& m = matches[i];
m_nuclideTable->setItem(i, 0, new QTableWidgetItem(m.nuclideName));
m_nuclideTable->setItem(i, 1, new QTableWidgetItem(m.rayType));
m_nuclideTable->setItem(i, 2, new QTableWidgetItem(m.energyDisplay));
}
if (matches.isEmpty()) {
m_nuclideTable->setRowCount(1);
m_nuclideTable->setItem(0, 0, new QTableWidgetItem(QStringLiteral(u"未找到匹配核素")));
}
m_nuclideDialog->show();
m_nuclideDialog->raise();
m_nuclideDialog->activateWindow();
}
bool NuclideAnalysisView::openNuclideDatabase()
{
auto& db = SqliteManager::instance();
if (db.isOpen()) {
return true;
}
QString dbPath = QCoreApplication::applicationDirPath() + "/nuclideLib.db";
if (dbPath.isEmpty()) {
qWarning() << "核素数据库路径未设置";
return false;
}
return db.openDatabase(dbPath, "nuclide_conn");
}

View File

@ -4,24 +4,86 @@
#include <QWidget>
#include <MeasureAnalysisView.h>
#include <QwtPlotCurve>
#include <QwtPlotMarker>
#include <QMenu>
#include <QwtPlotShapeItem>
#include <QPainterPath>
#include <QwtSymbol>
#include <QDialog>
#include <QTableWidget>
#include <QVBoxLayout>
#include "sqlitemanager.h"
#include "DataCalcProcess/AdaptiveSimpsonIntegrate.h"
#include "DataCalcProcess/FindPeaksBySvd.h"
#include "DataCalcProcess/MathModelDefine.h"
#include "DataCalcProcess/NolinearLeastSquaresCurveFit.h"
#include "DataCalcProcess/Poly2FindPeaks.h"
class CustomQwtPlot;
struct PeakFitResultNuclide {
bool success; // 拟合是否成功
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 NuclideAnalysisView : public MeasureAnalysisView
{
Q_OBJECT
public:
NuclideAnalysisView(QWidget *parent = nullptr);
virtual ~NuclideAnalysisView();
virtual void InitViewWorkspace(const QString& project_name) override final;
virtual void SetAnalyzeDataFilename(const QMap<QString, QVariant>& data_files_set);
void addVerticalLine(double xValue, const QColor& color = Qt::red);
// 用光子峰模型拟合并计算单个峰的半高宽
PeakFitResultNuclide FitPhotonPeakCalcFwhm(const QVector<double>& x, const QVector<double>& y);
// 根据x范围截取数据并拟合计FWHM
PeakFitResultNuclide FitPeakByRange(double x_start, double x_end,
const QVector<double>& x, const QVector<double>& y);
// 从整张谱中自动寻找三个最强的峰并计算FWHM
QVector<PeakFitResultNuclide> FitThreePeaksFromSpectrum(
const QVector<double>& x,
const QVector<double>& y,
double height_threshold = 50.0,
int min_distance = 20);
void DrawPeaks(const QVector<PeakFitResultNuclide>& peaks);
void ClearPeakMarkers();
bool HasSpectrumData() const;
std::vector<FindPeaksBySvd::PeakInfo> GetSvdPeaks(const QVector<double>& x,const QVector<double>& y,int step_w);
protected:
virtual bool eventFilter(QObject* obj, QEvent* event) override;
private:
void setupPlot();
void setupMenu();
void loadDataFromFile(const QString &data_name, const QString& filename);
void setupNuclideDialog();
void queryNuclidesForPeak(const PeakFitResultNuclide& peak);
bool openNuclideDatabase();
private slots:
void onActionPlotConfigure();
//在谱线上选择
void onActionPlotSelect();
void onActionPlotClear();
private:
CustomQwtPlot* _plot = nullptr;
QString _workspace;
@ -29,5 +91,23 @@ private:
int _x_max = 0;
int _y_max = 0;
bool m_bSelectHS = false;
QwtPlotMarker* _vLineMarker = nullptr; // 竖线标记
QList<QwtPlotItem*> m_peakMarkers; // 峰标记列表
QVector<double> m_spectrumX; // 谱数据X能量/道址)
QVector<double> m_spectrumY; // 谱
QVector<PeakFitResultNuclide> m_allFitResults;
bool m_bSelecting = false; // 是否正在拖动选择
double m_selectStartX = 0.0; // 选择起始X坐标
QPoint m_selectStartPos; // 选择起始像素位置(判断点击还是拖动)
QwtPlotMarker* m_selectLeftMarker = nullptr; // 选择左边界线
QwtPlotMarker* m_selectRightMarker = nullptr; // 选择右边界线
QDialog* m_nuclideDialog = nullptr;
QTableWidget* m_nuclideTable = nullptr;
};
#endif // NUCLIDEANALYSISVIEW_H

View File

@ -30,7 +30,7 @@ void NuclideRayDialog::initUI()
// 射线类型下拉框(常用核素射线类型)
m_cmbRayType = new QComboBox(this);
m_cmbRayType->addItems({QStringLiteral(u"γ"), QStringLiteral(u"β⁻"), QStringLiteral(u"β⁺"), QStringLiteral(u"α"), QStringLiteral(u"X射线"), QStringLiteral(u"中子"), QStringLiteral(u"电子俘获")});
m_cmbRayType->addItems({QStringLiteral(u"γ"), QStringLiteral(u"β⁻"), QStringLiteral(u"β⁺"), QStringLiteral(u"α"), QStringLiteral(u"X射线")});
formLayout->addRow(QStringLiteral(u"射线类型:"), m_cmbRayType);
m_leRayMev = new QLineEdit(this);