EnergySpectrumAnalyer/src/EfficiencyScale.cpp

857 lines
30 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 "EfficiencyScale.h"
#include "ui_EfficiencyScale.h"
#include <QDir>
#include <QwtPlotCanvas>
#include <QwtText>
#include <QwtSymbol>
#include <QMessageBox>
#include "CustomQwtPlot.h"
#include <QFileDialog>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDateTime>
#include <QHeaderView>
#include <cmath>
#include <stdexcept>
#include <armadillo>
#include "DataCalcProcess/EnergyEfficiencyFit.h"
EfficiencyScale::EfficiencyScale(QWidget *parent)
: QDialog(parent)
, ui(new Ui::EfficiencyScale)
{
ui->setupUi(this);
QHBoxLayout* inflectionLayout = new QHBoxLayout();
QLabel* inflectionLabel = new QLabel(QStringLiteral(u"拐点能量(keV):"), this);
QLineEdit* inflectionEdit = new QLineEdit(this);
inflectionEdit->setObjectName("lineEdit_inflection_energy");
inflectionEdit->setPlaceholderText("0 = 全段统一拟合");
inflectionLayout->addWidget(inflectionLabel);
inflectionLayout->addWidget(inflectionEdit);
ui->verticalLayout_16->insertLayout(2, inflectionLayout);
this->_plot = new CustomQwtPlot(this);
ui->layout_fittingCurve->addWidget(this->_plot);
setupPlot();
_rawDataCurve = new QwtPlotCurve(QStringLiteral(u"原始数据"));
_rawDataCurve->setStyle(QwtPlotCurve::NoCurve);
_rawDataCurve->setSymbol(new QwtSymbol(QwtSymbol::Ellipse,
QBrush(Qt::red), QPen(Qt::red), QSize(8, 8)));
_rawDataCurve->attach(_plot);
_fitCurve = new QwtPlotCurve(QStringLiteral(u"拟合曲线"));
_fitCurve->setPen(QPen(Qt::blue, 2));
_fitCurve->attach(_plot);
ui->table_scale_data->verticalHeader()->hide();
initComboBoxUi();
// 初始化32个通道数据
for (int i = 1; i <= 32; ++i) {
QString channelName = QStringLiteral(u"通道%1").arg(i);
m_channelDataMap.insert(channelName, ChannelEfficiencyData());
}
m_currentChannel = QStringLiteral(u"通道1");
// 连接通道切换信号
connect(ui->comboBox_channel, SIGNAL(currentIndexChanged(int)),
this, SLOT(on_comboBox_channel_currentIndexChanged(int)));
// 表格设置
ui->table_scale_data->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ui->table_scale_data->setEditTriggers(QAbstractItemView::DoubleClicked
| QAbstractItemView::SelectedClicked);
// 连接表格编辑信号
connect(ui->table_scale_data, SIGNAL(itemChanged(QTableWidgetItem*)),
this, SLOT(on_table_scale_data_itemChanged(QTableWidgetItem*)));
// 初始化显示
refreshTable();
refreshPlot();
// 加载文件列表
loadAllFilesInTheFolder();
}
EfficiencyScale::~EfficiencyScale()
{
delete ui;
}
void EfficiencyScale::setupPlot()
{
_plot->setCanvasBackground(Qt::white);
QwtPlotCanvas* canvas = qobject_cast<QwtPlotCanvas*>(_plot->canvas());
canvas->setFrameStyle(QFrame::NoFrame);
QFont font = this->font();
font.setBold(false);
QwtText energy_label = QStringLiteral(u"能量");
energy_label.setFont(font);
QwtText count_label = QStringLiteral(u"探测效率");
count_label.setFont(font);
_plot->setAxisTitle(QwtPlot::xBottom, energy_label);
_plot->setAxisTitle(QwtPlot::yLeft, count_label);
// 设置轴自动缩放
_plot->setAxisAutoScale(QwtPlot::xBottom, true);
_plot->setAxisAutoScale(QwtPlot::yLeft, true);
_plot->enableAxis(QwtPlot::xBottom);
_plot->enableAxis(QwtPlot::yLeft);
_plot->SetAxisDragScale(QwtPlot::xBottom, true);
// 启用鼠标追踪
_plot->canvas()->setMouseTracking(true);
}
void EfficiencyScale::loadAllFilesInTheFolder()
{
const QString& energy_scale_dir_path = QDir(qApp->applicationDirPath()).filePath("configure/EfficiencyScale");
QDir dir(energy_scale_dir_path);
// 确保目录存在
if (!dir.exists()) {
dir.mkpath(energy_scale_dir_path);
}
QFileInfoList infoList = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
ui->listWidget->clear();
for (const QFileInfo &info : infoList) {
QString displayName;
if (info.isFile()) {
displayName = info.baseName();
} else {
displayName = info.fileName();
}
QListWidgetItem *item = new QListWidgetItem(displayName);
item->setData(Qt::UserRole, info.absoluteFilePath());
ui->listWidget->addItem(item);
}
connect(ui->listWidget, &QListWidget::itemDoubleClicked, this, &EfficiencyScale::onItemDoubleClicked);
}
void EfficiencyScale::initComboBoxUi()
{
ui->comboBox_fit_type->addItem(QStringLiteral(u"一次拟合"));
ui->comboBox_fit_type->addItem(QStringLiteral(u"二次拟合"));
ui->comboBox_fit_type_1->addItem(QStringLiteral(u"一次拟合"));
ui->comboBox_fit_type_1->addItem(QStringLiteral(u"二次拟合"));
for(int i = 1;i <= 32; ++i)
{
QString channelName = QStringLiteral(u"通道%1").arg(i);
ui->comboBox_channel->addItem(channelName);
}
}
void EfficiencyScale::clearPlot()
{
_rawDataCurve->detach();
_fitCurve->detach();
_plot->detachItems(QwtPlotItem::Rtti_PlotCurve, false);
_plot->replot();
}
double EfficiencyScale::calculateCountRate(double netCount, double liveTime) const
{
if (liveTime <= 0.0) return 0.0;
return netCount / liveTime; // cps: 每秒计数个数
}
double EfficiencyScale::calculateEfficiency(double netCount, double liveTime,
double activity, double branchRatio) const
{
// 探测效率 ε = N / (t * A * Pγ)
// N: 净计数, t: 活时间, A: 活度, Pγ: 分支比
double denominator = liveTime * activity * branchRatio;
if (denominator <= 0.0) return 0.0;
return netCount / denominator;
}
void EfficiencyScale::recalculateAllPoints(QVector<EfficiencyPoint>& points) const
{
for (auto& pt : points) {
pt.countRate = calculateCountRate(pt.netCount, pt.liveTime);
pt.efficiency = calculateEfficiency(pt.netCount, pt.liveTime,
pt.activity, pt.branchRatio);
}
}
FitResult EfficiencyScale::polynomialFit(const QVector<EfficiencyPoint>& points,
int degree, double minEnergy, double maxEnergy) const
{
FitResult result;
result.degree = degree;
// 筛选能量范围内的有效点
QVector<double> eData, epsData;
for (const auto& pt : points) {
if (pt.energy >= minEnergy && pt.energy <= maxEnergy
&& pt.efficiency > 0.0 && pt.energy > 0.0) {
eData.append(pt.energy);
epsData.append(pt.efficiency);
}
}
int n = eData.size();
if (n <= degree) {
result.valid = false;
return result;
}
// 转换为 Armadillo 向量
arma::vec E(n), eps(n);
for (int i = 0; i < n; ++i) {
E(i) = eData[i];
eps(i) = epsData[i];
}
// 调用半经验拟合
try {
arma::vec coeffs = EnergyEfficiencyFit::SemiEmpiricalFit(E, eps, degree);
// 只取有效阶数的系数0~degree共degree+1个
result.coeffs.clear();
for (int i = 0; i <= degree; ++i) {
result.coeffs.append(coeffs(i));
}
result.valid = true;
} catch (...) {
result.valid = false;
}
return result;
}
double EfficiencyScale::calculateFitEfficiency(double energy, const FitResult& fit) const
{
if (!fit.valid || energy <= 0.0) return 0.0;
double lnE = std::log(energy);
double lnEff = 0.0;
double xPow = 1.0;
for (int i = 0; i < fit.coeffs.size(); ++i) {
lnEff += fit.coeffs[i] * xPow;
xPow *= lnE;
}
return std::exp(lnEff);
}
double EfficiencyScale::calculateTotalEfficiency(double energy, double totalNetCount,
double liveTime, double activity,
double branchRatio) const
{
// 谱仪总效率:各通道能峰计数率相加后,按相同公式计算
Q_UNUSED(energy);
return calculateEfficiency(totalNetCount, liveTime, activity, branchRatio);
}
ChannelEfficiencyData& EfficiencyScale::currentChannelData()
{
return m_channelDataMap[m_currentChannel];
}
const ChannelEfficiencyData& EfficiencyScale::currentChannelData() const
{
return m_channelDataMap[m_currentChannel];
}
void EfficiencyScale::refreshTable()
{
const ChannelEfficiencyData& chData = currentChannelData();
const QVector<EfficiencyPoint>& points = chData.points;
// 临时断开信号避免触发itemChanged
disconnect(ui->table_scale_data, SIGNAL(itemChanged(QTableWidgetItem*)),
this, SLOT(on_table_scale_data_itemChanged(QTableWidgetItem*)));
ui->table_scale_data->setRowCount(points.size());
for (int row = 0; row < points.size(); ++row) {
const EfficiencyPoint& pt = points[row];
// 0: 序号(只读)
QTableWidgetItem* item0 = new QTableWidgetItem(QString::number(row + 1));
item0->setFlags(item0->flags() & ~Qt::ItemIsEditable);
ui->table_scale_data->setItem(row, 0, item0);
// 1: 能量(keV) —— 可编辑输入
ui->table_scale_data->setItem(row, 1, new QTableWidgetItem(QString::number(pt.energy, 'f', 2)));
// 2: 净计数N —— 可编辑输入
ui->table_scale_data->setItem(row, 2, new QTableWidgetItem(QString::number(pt.netCount, 'f', 0)));
// 3: 活时间t(s) —— 可编辑输入
ui->table_scale_data->setItem(row, 3, new QTableWidgetItem(QString::number(pt.liveTime, 'f', 2)));
// 4: 活度A(Bq) —— 可编辑输入
ui->table_scale_data->setItem(row, 4, new QTableWidgetItem(QString::number(pt.activity, 'f', 2)));
// 5: 分支比Pγ —— 可编辑输入
ui->table_scale_data->setItem(row, 5, new QTableWidgetItem(QString::number(pt.branchRatio, 'f', 4)));
// 6: 净计数率(cps) —— 计算值
QTableWidgetItem* item6 = new QTableWidgetItem(QString::number(pt.countRate, 'f', 4));
item6->setFlags(item6->flags() & ~Qt::ItemIsEditable);
ui->table_scale_data->setItem(row, 6, item6);
// 7: 探测效率ε —— 计算值
QTableWidgetItem* item7 = new QTableWidgetItem(QString::number(pt.efficiency, 'f', 8));
item7->setFlags(item7->flags() & ~Qt::ItemIsEditable);
ui->table_scale_data->setItem(row, 7, item7);
// 8: 拟合效率 —— 拟合后计算
QTableWidgetItem* item8;
if (pt.fitEfficiency > 0.0) {
item8 = new QTableWidgetItem(QString::number(pt.fitEfficiency, 'f', 8));
} else {
item8 = new QTableWidgetItem("-");
}
item8->setFlags(item8->flags() & ~Qt::ItemIsEditable);
ui->table_scale_data->setItem(row, 8, item8);
// 9: 误差(%) —— 拟合后计算
QTableWidgetItem* item9;
if (pt.error > 0.0) {
item9 = new QTableWidgetItem(QString::number(pt.error, 'f', 4) + "%");
} else {
item9 = new QTableWidgetItem("-");
}
item9->setFlags(item9->flags() & ~Qt::ItemIsEditable);
ui->table_scale_data->setItem(row, 9, item9);
// 10: 刻度日期 —— 可编辑
ui->table_scale_data->setItem(row, 10, new QTableWidgetItem(pt.scaleDate));
}
// 重新连接信号
connect(ui->table_scale_data, SIGNAL(itemChanged(QTableWidgetItem*)),
this, SLOT(on_table_scale_data_itemChanged(QTableWidgetItem*)));
}
// 表格单元格编辑后更新数据并重算
void EfficiencyScale::on_table_scale_data_itemChanged(QTableWidgetItem* item)
{
int row = item->row();
int col = item->column();
ChannelEfficiencyData& chData = currentChannelData();
if (row >= chData.points.size()) return;
EfficiencyPoint& pt = chData.points[row];
bool ok = false;
double value = item->text().toDouble(&ok);
switch (col) {
case 1: // 能量
if (ok) pt.energy = value;
break;
case 2: // 净计数N
if (ok) pt.netCount = value;
break;
case 3: // 活时间t
if (ok) pt.liveTime = value;
break;
case 4: // 活度A
if (ok) pt.activity = value;
break;
case 5: // 分支比Pγ
if (ok) pt.branchRatio = value;
break;
case 10: // 刻度日期
pt.scaleDate = item->text();
return; // 日期不需要重算
default:
return; // 其他列是只读计算列,不处理
}
// 修改了输入参数,重新计算计数率和效率
recalculateAllPoints(chData.points);
// 刷新表格的计算列(避免递归,直接更新指定单元格)
disconnect(ui->table_scale_data, SIGNAL(itemChanged(QTableWidgetItem*)),
this, SLOT(on_table_scale_data_itemChanged(QTableWidgetItem*)));
ui->table_scale_data->item(row, 6)->setText(QString::number(pt.countRate, 'f', 4));
ui->table_scale_data->item(row, 7)->setText(QString::number(pt.efficiency, 'e', 6));
connect(ui->table_scale_data, SIGNAL(itemChanged(QTableWidgetItem*)),
this, SLOT(on_table_scale_data_itemChanged(QTableWidgetItem*)));
refreshPlot();
}
QVector<QPointF> EfficiencyScale::generateFitCurvePoints(const FitResult& highFit,
const FitResult& lowFit,
double inflectionEnergy,
int sampleCount) const
{
QVector<QPointF> curvePoints;
const ChannelEfficiencyData& chData = currentChannelData();
if (chData.points.isEmpty()) return curvePoints;
// 找能量范围
double minE = chData.points.first().energy;
double maxE = chData.points.first().energy;
for (const auto& pt : chData.points) {
minE = qMin(minE, pt.energy);
maxE = qMax(maxE, pt.energy);
}
if (minE <= 0 || maxE <= 0) return curvePoints;
// 采样
double logMin = std::log(minE);
double logMax = std::log(maxE);
double step = (logMax - logMin) / sampleCount;
for (int i = 0; i <= sampleCount; ++i) {
double logE = logMin + i * step;
double energy = std::exp(logE);
double eff = 0.0;
if (inflectionEnergy > 0 && energy < inflectionEnergy) {
// 拐点以下用低能拟合
if (lowFit.valid) {
eff = calculateFitEfficiency(energy, lowFit);
}
} else {
// 拐点以上用高能拟合
if (highFit.valid) {
eff = calculateFitEfficiency(energy, highFit);
} else if (lowFit.valid) {
eff = calculateFitEfficiency(energy, lowFit);
}
}
if (eff > 0.0) {
curvePoints.append(QPointF(energy, eff));
}
}
return curvePoints;
}
void EfficiencyScale::refreshPlot()
{
if (!_plot || !_rawDataCurve || !_fitCurve) return;
const ChannelEfficiencyData& chData = currentChannelData();
// 原始数据散点
QVector<QPointF> rawPoints;
for (const auto& pt : chData.points) {
if (pt.efficiency > 0.0) {
rawPoints.append(QPointF(pt.energy, pt.efficiency));
}
}
_rawDataCurve->setSamples(rawPoints);
// 拟合曲线
QVector<QPointF> fitPoints = generateFitCurvePoints(
chData.highFit, chData.lowFit, chData.inflectionEnergy);
_fitCurve->setSamples(fitPoints);
_plot->replot();
}
void EfficiencyScale::on_pBtn_Add_clicked()
{
ChannelEfficiencyData& chData = currentChannelData();
EfficiencyPoint newPoint;
newPoint.scaleDate = QDateTime::currentDateTime().toString("yyyy-MM-dd");
// 默认值
newPoint.energy = 0.0;
newPoint.netCount = 0.0;
newPoint.liveTime = 1.0;
newPoint.activity = 1.0;
newPoint.branchRatio = 1.0;
chData.points.append(newPoint);
recalculateAllPoints(chData.points);
refreshTable();
refreshPlot();
}
void EfficiencyScale::on_pBtn_Delete_clicked()
{
int currentRow = ui->table_scale_data->currentRow();
if (currentRow < 0) {
QMessageBox::warning(this, QStringLiteral(u"提示"),
QStringLiteral(u"请先选中要删除的刻度点"));
return;
}
ChannelEfficiencyData& chData = currentChannelData();
if (currentRow >= chData.points.size()) return;
chData.points.removeAt(currentRow);
recalculateAllPoints(chData.points);
refreshTable();
refreshPlot();
}
void EfficiencyScale::on_pBtn_fitting_4_clicked()
{
// 拐点以上拟合
ChannelEfficiencyData& chData = currentChannelData();
QLineEdit* inflectionEdit = findChild<QLineEdit*>("lineEdit_inflection_energy");
if (inflectionEdit) {
bool ok = false;
double val = inflectionEdit->text().toDouble(&ok);
chData.inflectionEnergy = (ok && val > 0.0) ? val : 0.0;
}
if (chData.points.size() < 2) {
QMessageBox::warning(this, QStringLiteral(u"提示"),
QStringLiteral(u"至少需要2个刻度点才能进行拟合"));
return;
}
int degree = ui->comboBox_fit_type->currentIndex() + 1; // 1或2
double inflectionE = chData.inflectionEnergy;
// 如果没有设置拐点,默认全部用高能拟合
double minE = inflectionE > 0 ? inflectionE : 0.0;
double maxE = 1e9; // 足够大
chData.highFit = polynomialFit(chData.points, degree, minE, maxE);
if (!chData.highFit.valid) {
QMessageBox::critical(this, QStringLiteral(u"错误"),
QStringLiteral(u"拟合失败,请检查数据"));
return;
}
// 更新各点的拟合效率和误差
for (auto& pt : chData.points) {
if (inflectionE <= 0 || pt.energy >= inflectionE) {
pt.fitEfficiency = calculateFitEfficiency(pt.energy, chData.highFit);
if (pt.efficiency > 0.0) {
pt.error = std::abs(pt.fitEfficiency - pt.efficiency) / pt.efficiency * 100.0;
}
}
}
refreshTable();
refreshPlot();
}
void EfficiencyScale::on_pBtn_fitting_3_clicked()
{
// 拐点以下拟合
ChannelEfficiencyData& chData = currentChannelData();
QLineEdit* inflectionEdit = findChild<QLineEdit*>("lineEdit_inflection_energy");
if (inflectionEdit) {
bool ok = false;
double val = inflectionEdit->text().toDouble(&ok);
chData.inflectionEnergy = (ok && val > 0.0) ? val : 0.0;
}
if (chData.points.size() < 2) {
QMessageBox::warning(this, QStringLiteral(u"提示"),
QStringLiteral(u"至少需要2个刻度点才能进行拟合"));
return;
}
int degree = ui->comboBox_fit_type_1->currentIndex() + 1; // 1或2
double inflectionE = chData.inflectionEnergy;
double minE = 0.0;
double maxE = inflectionE > 0 ? inflectionE : 1e9;
chData.lowFit = polynomialFit(chData.points, degree, minE, maxE);
if (!chData.lowFit.valid) {
QMessageBox::critical(this, QStringLiteral(u"错误"),
QStringLiteral(u"拟合失败,请检查数据"));
return;
}
// 更新各点的拟合效率和误差
for (auto& pt : chData.points) {
if (inflectionE <= 0 || pt.energy < inflectionE) {
pt.fitEfficiency = calculateFitEfficiency(pt.energy, chData.lowFit);
if (pt.efficiency > 0.0) {
pt.error = std::abs(pt.fitEfficiency - pt.efficiency) / pt.efficiency * 100.0;
}
}
}
refreshTable();
refreshPlot();
}
void EfficiencyScale::on_comboBox_channel_currentIndexChanged(int index)
{
Q_UNUSED(index);
QLineEdit* inflectionEdit = findChild<QLineEdit*>("lineEdit_inflection_energy");
if (inflectionEdit) {
const ChannelEfficiencyData& chData = currentChannelData();
if (chData.inflectionEnergy > 0.0) {
inflectionEdit->setText(QString::number(chData.inflectionEnergy, 'f', 2));
} else {
inflectionEdit->clear();
}
}
m_currentChannel = ui->comboBox_channel->currentText();
refreshTable();
refreshPlot();
}
bool EfficiencyScale::saveToJsonFile(const QString& filePath) const
{
QJsonObject rootObj;
rootObj["scaleName"] = ui->lineEdit_name->text();
rootObj["description"] = ui->plainTextEdit_description_3->toPlainText();
rootObj["currentChannel"] = m_currentChannel;
QJsonArray channelsArray;
for (auto it = m_channelDataMap.constBegin(); it != m_channelDataMap.constEnd(); ++it) {
QJsonObject chObj;
chObj["channelName"] = it.key();
chObj["inflectionEnergy"] = it.value().inflectionEnergy;
// 刻度点
QJsonArray pointsArray;
for (const auto& pt : it.value().points) {
QJsonObject ptObj;
ptObj["energy"] = pt.energy;
ptObj["netCount"] = pt.netCount;
ptObj["liveTime"] = pt.liveTime;
ptObj["activity"] = pt.activity;
ptObj["branchRatio"] = pt.branchRatio;
ptObj["countRate"] = pt.countRate;
ptObj["efficiency"] = pt.efficiency;
ptObj["fitEfficiency"] = pt.fitEfficiency;
ptObj["error"] = pt.error;
ptObj["scaleDate"] = pt.scaleDate;
pointsArray.append(ptObj);
}
chObj["points"] = pointsArray;
// 高能拟合
QJsonObject highFitObj;
highFitObj["valid"] = it.value().highFit.valid;
highFitObj["degree"] = it.value().highFit.degree;
QJsonArray highCoeffs;
for (double c : it.value().highFit.coeffs) highCoeffs.append(c);
highFitObj["coeffs"] = highCoeffs;
chObj["highFit"] = highFitObj;
// 低能拟合
QJsonObject lowFitObj;
lowFitObj["valid"] = it.value().lowFit.valid;
lowFitObj["degree"] = it.value().lowFit.degree;
QJsonArray lowCoeffs;
for (double c : it.value().lowFit.coeffs) lowCoeffs.append(c);
lowFitObj["coeffs"] = lowCoeffs;
chObj["lowFit"] = lowFitObj;
channelsArray.append(chObj);
}
rootObj["channels"] = channelsArray;
QJsonDocument doc(rootObj);
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return false;
}
file.write(doc.toJson());
file.close();
return true;
}
bool EfficiencyScale::loadFromJsonFile(const QString& filePath)
{
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return false;
}
QByteArray data = file.readAll();
file.close();
QJsonDocument doc = QJsonDocument::fromJson(data);
if (!doc.isObject()) return false;
QJsonObject rootObj = doc.object();
ui->lineEdit_name->setText(rootObj["scaleName"].toString());
ui->plainTextEdit_description_3->setPlainText(rootObj["description"].toString());
QJsonArray channelsArray = rootObj["channels"].toArray();
for (const auto& chVal : channelsArray) {
QJsonObject chObj = chVal.toObject();
QString chName = chObj["channelName"].toString();
if (!m_channelDataMap.contains(chName)) continue;
ChannelEfficiencyData chData;
chData.inflectionEnergy = chObj["inflectionEnergy"].toDouble();
// 刻度点
QJsonArray pointsArray = chObj["points"].toArray();
for (const auto& ptVal : pointsArray) {
QJsonObject ptObj = ptVal.toObject();
EfficiencyPoint pt;
pt.energy = ptObj["energy"].toDouble();
pt.netCount = ptObj["netCount"].toDouble();
pt.liveTime = ptObj["liveTime"].toDouble();
pt.activity = ptObj["activity"].toDouble();
pt.branchRatio = ptObj["branchRatio"].toDouble();
pt.countRate = ptObj["countRate"].toDouble();
pt.efficiency = ptObj["efficiency"].toDouble();
pt.fitEfficiency = ptObj["fitEfficiency"].toDouble();
pt.error = ptObj["error"].toDouble();
pt.scaleDate = ptObj["scaleDate"].toString();
chData.points.append(pt);
}
// 高能拟合
QJsonObject highFitObj = chObj["highFit"].toObject();
chData.highFit.valid = highFitObj["valid"].toBool();
chData.highFit.degree = highFitObj["degree"].toInt();
QJsonArray highCoeffs = highFitObj["coeffs"].toArray();
for (const auto& c : highCoeffs) chData.highFit.coeffs.append(c.toDouble());
// 低能拟合
QJsonObject lowFitObj = chObj["lowFit"].toObject();
chData.lowFit.valid = lowFitObj["valid"].toBool();
chData.lowFit.degree = lowFitObj["degree"].toInt();
QJsonArray lowCoeffs = lowFitObj["coeffs"].toArray();
for (const auto& c : lowCoeffs) chData.lowFit.coeffs.append(c.toDouble());
m_channelDataMap[chName] = chData;
}
m_currentFilePath = filePath;
refreshTable();
refreshPlot();
return true;
}
void EfficiencyScale::onItemDoubleClicked(QListWidgetItem *item)
{
if (!item) return;
QString filePath = item->data(Qt::UserRole).toString();
if (filePath.isEmpty()) return;
QFileInfo fileInfo(filePath);
if (!fileInfo.isFile()) return;
if (loadFromJsonFile(filePath)) {
ui->lineEdit_name->setText(item->text());
QMessageBox::information(this, QStringLiteral(u"成功"),
QStringLiteral(u"加载成功:%1").arg(item->text()));
} else {
QMessageBox::critical(this, QStringLiteral(u"错误"),
QStringLiteral(u"加载失败:文件格式不正确"));
}
}
void EfficiencyScale::on_pBtn_Save_clicked()
{
if (m_currentFilePath.isEmpty()) {
on_pBtn_SaveAs_clicked();
return;
}
if (saveToJsonFile(m_currentFilePath)) {
QMessageBox::information(this, QStringLiteral(u"成功"),
QStringLiteral(u"保存成功"));
} else {
QMessageBox::critical(this, QStringLiteral(u"错误"),
QStringLiteral(u"保存失败"));
}
}
void EfficiencyScale::on_pBtn_SaveAs_clicked()
{
QString defaultDir = QDir(qApp->applicationDirPath()).filePath("configure/EfficiencyScale");
QString fileName = QFileDialog::getSaveFileName(this,
QStringLiteral(u"另存为"), defaultDir,
QStringLiteral(u"效率刻度文件 (*.json)"));
if (fileName.isEmpty()) return;
if (saveToJsonFile(fileName)) {
m_currentFilePath = fileName;
loadAllFilesInTheFolder(); // 刷新列表
QMessageBox::information(this, QStringLiteral(u"成功"),
QStringLiteral(u"保存成功"));
} else {
QMessageBox::critical(this, QStringLiteral(u"错误"),
QStringLiteral(u"保存失败"));
}
}
void EfficiencyScale::on_pBtn_Add_File_clicked()
{
QString defaultDir = QDir(qApp->applicationDirPath()).filePath("configure/EfficiencyScale");
QString fileName = QFileDialog::getSaveFileName(this,
QStringLiteral(u"新建效率刻度文件"), defaultDir,
QStringLiteral(u"效率刻度文件 (*.json)"));
if (fileName.isEmpty()) return;
// 确保后缀
if (!fileName.endsWith(".json", Qt::CaseInsensitive)) {
fileName += ".json";
}
// 清空当前数据,生成空白刻度
m_scaleName = QFileInfo(fileName).completeBaseName();
m_description.clear();
m_currentChannel = QStringLiteral(u"通道1");
for (auto it = m_channelDataMap.begin(); it != m_channelDataMap.end(); ++it) {
it.value().points.clear();
it.value().highFit = FitResult();
it.value().lowFit = FitResult();
it.value().inflectionEnergy = 0.0;
}
// 保存空白文件
if (saveToJsonFile(fileName)) {
m_currentFilePath = fileName;
ui->lineEdit_name->setText(m_scaleName);
ui->plainTextEdit_description_3->setPlainText(m_description);
loadAllFilesInTheFolder();
refreshTable();
refreshPlot();
QMessageBox::information(this, QStringLiteral(u"成功"),
QStringLiteral(u"新建成功"));
} else {
QMessageBox::critical(this, QStringLiteral(u"错误"),
QStringLiteral(u"新建失败"));
}
}
void EfficiencyScale::on_pBtn_Delete_File_clicked()
{
QListWidgetItem* selectedItem = ui->listWidget->currentItem();
if (!selectedItem) {
QMessageBox::warning(this, QStringLiteral(u"提示"),
QStringLiteral(u"请先选中要删除的文件/文件夹"));
return;
}
QString filePath = selectedItem->data(Qt::UserRole).toString();
if (filePath.isEmpty()) {
QMessageBox::warning(this, QStringLiteral(u"错误"),
QStringLiteral(u"无法获取文件路径"));
return;
}
QMessageBox::StandardButton ret = QMessageBox::question(this,
QStringLiteral(u"确认删除"),
QStringLiteral(u"是否确定删除%1\n删除后无法恢复!").arg(selectedItem->text()),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No);
if (ret != QMessageBox::Yes) {
return;
}
bool isDeleted = false;
QFileInfo fileInfo(filePath);
if (fileInfo.isFile()) {
QFile file(filePath);
isDeleted = file.remove();
}
if (isDeleted) {
loadAllFilesInTheFolder();
if (m_currentFilePath == filePath) {
m_currentFilePath.clear();
ui->lineEdit_name->clear();
ui->table_scale_data->setRowCount(0);
clearPlot();
_plot->replot();
}
QMessageBox::information(this, QStringLiteral(u"成功"),
QStringLiteral(u"删除成功"));
} else {
QMessageBox::critical(this, QStringLiteral(u"错误"),
QStringLiteral(u"删除失败:%1").arg(filePath));
}
}