新增效率刻度功能,增加图标配置功能

This commit is contained in:
anxinglong 2026-07-24 17:50:23 +08:00
parent 29a746d873
commit 8ee6c4387c
14 changed files with 2239 additions and 41 deletions

View File

@ -15,6 +15,16 @@
#include <cstdint> #include <cstdint>
#include <map> #include <map>
#include <vector> #include <vector>
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QColorDialog>
#include <QMap>
#include <QPushButton>
#include <QwtLegend>
//反符合能谱 //反符合能谱
AntiConformEnergySpectrumView::AntiConformEnergySpectrumView(QWidget* parent) AntiConformEnergySpectrumView::AntiConformEnergySpectrumView(QWidget* parent)
: MeasureAnalysisView(parent) : MeasureAnalysisView(parent)
@ -155,6 +165,133 @@ void AntiConformEnergySpectrumView::clearAllRoiMarkers()
void AntiConformEnergySpectrumView::onActionPlotConfigure() void AntiConformEnergySpectrumView::onActionPlotConfigure()
{ {
QDialog dlg(this);
dlg.setWindowTitle(QStringLiteral(u"图表配置"));
dlg.setMinimumWidth(320);
QVBoxLayout* layout = new QVBoxLayout(&dlg);
// X轴标题
QHBoxLayout* xTitleLayout = new QHBoxLayout();
QLabel* xTitleLabel = new QLabel(QStringLiteral(u"X轴标题"), &dlg);
QLineEdit* xTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::xBottom).text(), &dlg);
xTitleLayout->addWidget(xTitleLabel);
xTitleLayout->addWidget(xTitleEdit);
layout->addLayout(xTitleLayout);
// Y轴标题
QHBoxLayout* yTitleLayout = new QHBoxLayout();
QLabel* yTitleLabel = new QLabel(QStringLiteral(u"Y轴标题"), &dlg);
QLineEdit* yTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::yLeft).text(), &dlg);
yTitleLayout->addWidget(yTitleLabel);
yTitleLayout->addWidget(yTitleEdit);
layout->addLayout(yTitleLayout);
layout->addSpacing(8);
// 显示网格
QCheckBox* gridCheck = new QCheckBox(QStringLiteral(u"显示网格线"), &dlg);
gridCheck->setChecked(_plot->axisEnabled(QwtPlot::xBottom) && _plot->axisEnabled(QwtPlot::yLeft));
layout->addWidget(gridCheck);
// 显示图例
QCheckBox* legendCheck = new QCheckBox(QStringLiteral(u"显示图例"), &dlg);
legendCheck->setChecked(_plot->legend() != nullptr);
layout->addWidget(legendCheck);
layout->addSpacing(8);
// 曲线颜色
QLabel* curveColorLabel = new QLabel(QStringLiteral(u"曲线颜色:"), &dlg);
layout->addWidget(curveColorLabel);
// 获取所有曲线
QList<QwtPlotCurve*> curveList;
QMap<QwtPlotCurve*, QColor> curveColorMap;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
curveList.append(curve);
curveColorMap.insert(curve, curve->pen().color());
}
}
// 为每条曲线创建颜色按钮
QList<QPushButton*> colorBtnList;
for (int i = 0; i < curveList.size(); ++i) {
QwtPlotCurve* curve = curveList.at(i);
QHBoxLayout* btnLayout = new QHBoxLayout();
QLabel* nameLabel = new QLabel(QStringLiteral(u"曲线%1").arg(i + 1), &dlg);
QPushButton* colorBtn = new QPushButton(&dlg);
colorBtn->setFixedSize(60, 24);
QColor color = curveColorMap.value(curve);
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(color.name()));
btnLayout->addWidget(nameLabel);
btnLayout->addWidget(colorBtn);
btnLayout->addStretch();
layout->addLayout(btnLayout);
colorBtnList.append(colorBtn);
connect(colorBtn, &QPushButton::clicked, [&dlg, curve, &curveColorMap, colorBtn]() {
QColor current = curveColorMap.value(curve);
QColor selected = QColorDialog::getColor(current, &dlg, QStringLiteral(u"选择曲线颜色"));
if (selected.isValid()) {
curveColorMap[curve] = selected;
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(selected.name()));
}
});
}
layout->addStretch();
// 确定取消按钮
QDialogButtonBox* buttonBox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dlg);
layout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
if (dlg.exec() == QDialog::Accepted) {
// 应用X轴标题
_plot->setAxisTitle(QwtPlot::xBottom, xTitleEdit->text());
// 应用Y轴标题
_plot->setAxisTitle(QwtPlot::yLeft, yTitleEdit->text());
// 应用网格显示
_plot->enableAxis(QwtPlot::xBottom, gridCheck->isChecked());
_plot->enableAxis(QwtPlot::yLeft, gridCheck->isChecked());
// 应用图例显示
if (legendCheck->isChecked() && _plot->legend() == nullptr) {
QwtLegend* legend = new QwtLegend();
legend->setDefaultItemMode(QwtLegendData::ReadOnly);
_plot->insertLegend(legend, QwtPlot::RightLegend);
// 给未命名曲线设置默认标题,保证图例显示名称
int idx = 1;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
if (curve->title().isEmpty()) {
curve->setTitle(QStringLiteral(u"曲线%1").arg(idx));
}
++idx;
}
}
} else if (!legendCheck->isChecked() && _plot->legend() != nullptr) {
_plot->insertLegend(nullptr);
}
// 应用曲线颜色
for (QwtPlotCurve* curve : curveList) {
QPen pen = curve->pen();
pen.setColor(curveColorMap.value(curve));
curve->setPen(pen);
}
// 刷新绘图
_plot->replot();
}
} }
void AntiConformEnergySpectrumView::onActionRoiTriggered() void AntiConformEnergySpectrumView::onActionRoiTriggered()

View File

@ -14,6 +14,15 @@
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
#include <QPen> #include <QPen>
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QColorDialog>
#include <QMap>
#include <QPushButton>
#include <QwtLegend>
struct SpectrumData struct SpectrumData
{ {
int eventId;//事件ID int eventId;//事件ID
@ -209,7 +218,133 @@ void CoincidenceEventTimeView::setupMenu()
void CoincidenceEventTimeView::onActionPlotConfigure() void CoincidenceEventTimeView::onActionPlotConfigure()
{ {
QDialog dlg(this);
dlg.setWindowTitle(QStringLiteral(u"图表配置"));
dlg.setMinimumWidth(320);
QVBoxLayout* layout = new QVBoxLayout(&dlg);
// X轴标题
QHBoxLayout* xTitleLayout = new QHBoxLayout();
QLabel* xTitleLabel = new QLabel(QStringLiteral(u"X轴标题"), &dlg);
QLineEdit* xTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::xBottom).text(), &dlg);
xTitleLayout->addWidget(xTitleLabel);
xTitleLayout->addWidget(xTitleEdit);
layout->addLayout(xTitleLayout);
// Y轴标题
QHBoxLayout* yTitleLayout = new QHBoxLayout();
QLabel* yTitleLabel = new QLabel(QStringLiteral(u"Y轴标题"), &dlg);
QLineEdit* yTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::yLeft).text(), &dlg);
yTitleLayout->addWidget(yTitleLabel);
yTitleLayout->addWidget(yTitleEdit);
layout->addLayout(yTitleLayout);
layout->addSpacing(8);
// 显示网格
QCheckBox* gridCheck = new QCheckBox(QStringLiteral(u"显示网格线"), &dlg);
gridCheck->setChecked(_plot->axisEnabled(QwtPlot::xBottom) && _plot->axisEnabled(QwtPlot::yLeft));
layout->addWidget(gridCheck);
// 显示图例
QCheckBox* legendCheck = new QCheckBox(QStringLiteral(u"显示图例"), &dlg);
legendCheck->setChecked(_plot->legend() != nullptr);
layout->addWidget(legendCheck);
layout->addSpacing(8);
// 曲线颜色
QLabel* curveColorLabel = new QLabel(QStringLiteral(u"曲线颜色:"), &dlg);
layout->addWidget(curveColorLabel);
// 获取所有曲线
QList<QwtPlotCurve*> curveList;
QMap<QwtPlotCurve*, QColor> curveColorMap;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
curveList.append(curve);
curveColorMap.insert(curve, curve->pen().color());
}
}
// 为每条曲线创建颜色按钮
QList<QPushButton*> colorBtnList;
for (int i = 0; i < curveList.size(); ++i) {
QwtPlotCurve* curve = curveList.at(i);
QHBoxLayout* btnLayout = new QHBoxLayout();
QLabel* nameLabel = new QLabel(QStringLiteral(u"曲线%1").arg(i + 1), &dlg);
QPushButton* colorBtn = new QPushButton(&dlg);
colorBtn->setFixedSize(60, 24);
QColor color = curveColorMap.value(curve);
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(color.name()));
btnLayout->addWidget(nameLabel);
btnLayout->addWidget(colorBtn);
btnLayout->addStretch();
layout->addLayout(btnLayout);
colorBtnList.append(colorBtn);
connect(colorBtn, &QPushButton::clicked, [&dlg, curve, &curveColorMap, colorBtn]() {
QColor current = curveColorMap.value(curve);
QColor selected = QColorDialog::getColor(current, &dlg, QStringLiteral(u"选择曲线颜色"));
if (selected.isValid()) {
curveColorMap[curve] = selected;
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(selected.name()));
}
});
}
layout->addStretch();
// 确定取消按钮
QDialogButtonBox* buttonBox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dlg);
layout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
if (dlg.exec() == QDialog::Accepted) {
// 应用X轴标题
_plot->setAxisTitle(QwtPlot::xBottom, xTitleEdit->text());
// 应用Y轴标题
_plot->setAxisTitle(QwtPlot::yLeft, yTitleEdit->text());
// 应用网格显示
_plot->enableAxis(QwtPlot::xBottom, gridCheck->isChecked());
_plot->enableAxis(QwtPlot::yLeft, gridCheck->isChecked());
// 应用图例显示
if (legendCheck->isChecked() && _plot->legend() == nullptr) {
QwtLegend* legend = new QwtLegend();
legend->setDefaultItemMode(QwtLegendData::ReadOnly);
_plot->insertLegend(legend, QwtPlot::RightLegend);
// 给未命名曲线设置默认标题,保证图例显示名称
int idx = 1;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
if (curve->title().isEmpty()) {
curve->setTitle(QStringLiteral(u"曲线%1").arg(idx));
}
++idx;
}
}
} else if (!legendCheck->isChecked() && _plot->legend() != nullptr) {
_plot->insertLegend(nullptr);
}
// 应用曲线颜色
for (QwtPlotCurve* curve : curveList) {
QPen pen = curve->pen();
pen.setColor(curveColorMap.value(curve));
curve->setPen(pen);
}
// 刷新绘图
_plot->replot();
}
} }

View File

@ -15,6 +15,16 @@
#include <cstdint> #include <cstdint>
#include <QPen> #include <QPen>
#include <QMenu> #include <QMenu>
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QColorDialog>
#include <QMap>
#include <QPushButton>
#include <QwtLegend>
//符合能谱 //符合能谱
struct SpectrumData struct SpectrumData
{ {
@ -194,7 +204,133 @@ void ConformToTheEnergySpectrum::clearAllRoiMarkers()
void ConformToTheEnergySpectrum::onActionPlotConfigure() void ConformToTheEnergySpectrum::onActionPlotConfigure()
{ {
QDialog dlg(this);
dlg.setWindowTitle(QStringLiteral(u"图表配置"));
dlg.setMinimumWidth(320);
QVBoxLayout* layout = new QVBoxLayout(&dlg);
// X轴标题
QHBoxLayout* xTitleLayout = new QHBoxLayout();
QLabel* xTitleLabel = new QLabel(QStringLiteral(u"X轴标题"), &dlg);
QLineEdit* xTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::xBottom).text(), &dlg);
xTitleLayout->addWidget(xTitleLabel);
xTitleLayout->addWidget(xTitleEdit);
layout->addLayout(xTitleLayout);
// Y轴标题
QHBoxLayout* yTitleLayout = new QHBoxLayout();
QLabel* yTitleLabel = new QLabel(QStringLiteral(u"Y轴标题"), &dlg);
QLineEdit* yTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::yLeft).text(), &dlg);
yTitleLayout->addWidget(yTitleLabel);
yTitleLayout->addWidget(yTitleEdit);
layout->addLayout(yTitleLayout);
layout->addSpacing(8);
// 显示网格
QCheckBox* gridCheck = new QCheckBox(QStringLiteral(u"显示网格线"), &dlg);
gridCheck->setChecked(_plot->axisEnabled(QwtPlot::xBottom) && _plot->axisEnabled(QwtPlot::yLeft));
layout->addWidget(gridCheck);
// 显示图例
QCheckBox* legendCheck = new QCheckBox(QStringLiteral(u"显示图例"), &dlg);
legendCheck->setChecked(_plot->legend() != nullptr);
layout->addWidget(legendCheck);
layout->addSpacing(8);
// 曲线颜色
QLabel* curveColorLabel = new QLabel(QStringLiteral(u"曲线颜色:"), &dlg);
layout->addWidget(curveColorLabel);
// 获取所有曲线
QList<QwtPlotCurve*> curveList;
QMap<QwtPlotCurve*, QColor> curveColorMap;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
curveList.append(curve);
curveColorMap.insert(curve, curve->pen().color());
}
}
// 为每条曲线创建颜色按钮
QList<QPushButton*> colorBtnList;
for (int i = 0; i < curveList.size(); ++i) {
QwtPlotCurve* curve = curveList.at(i);
QHBoxLayout* btnLayout = new QHBoxLayout();
QLabel* nameLabel = new QLabel(QStringLiteral(u"曲线%1").arg(i + 1), &dlg);
QPushButton* colorBtn = new QPushButton(&dlg);
colorBtn->setFixedSize(60, 24);
QColor color = curveColorMap.value(curve);
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(color.name()));
btnLayout->addWidget(nameLabel);
btnLayout->addWidget(colorBtn);
btnLayout->addStretch();
layout->addLayout(btnLayout);
colorBtnList.append(colorBtn);
connect(colorBtn, &QPushButton::clicked, [&dlg, curve, &curveColorMap, colorBtn]() {
QColor current = curveColorMap.value(curve);
QColor selected = QColorDialog::getColor(current, &dlg, QStringLiteral(u"选择曲线颜色"));
if (selected.isValid()) {
curveColorMap[curve] = selected;
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(selected.name()));
}
});
}
layout->addStretch();
// 确定取消按钮
QDialogButtonBox* buttonBox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dlg);
layout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
if (dlg.exec() == QDialog::Accepted) {
// 应用X轴标题
_plot->setAxisTitle(QwtPlot::xBottom, xTitleEdit->text());
// 应用Y轴标题
_plot->setAxisTitle(QwtPlot::yLeft, yTitleEdit->text());
// 应用网格显示
_plot->enableAxis(QwtPlot::xBottom, gridCheck->isChecked());
_plot->enableAxis(QwtPlot::yLeft, gridCheck->isChecked());
// 应用图例显示
if (legendCheck->isChecked() && _plot->legend() == nullptr) {
QwtLegend* legend = new QwtLegend();
legend->setDefaultItemMode(QwtLegendData::ReadOnly);
_plot->insertLegend(legend, QwtPlot::RightLegend);
// 给未命名曲线设置默认标题,保证图例显示名称
int idx = 1;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
if (curve->title().isEmpty()) {
curve->setTitle(QStringLiteral(u"曲线%1").arg(idx));
}
++idx;
}
}
} else if (!legendCheck->isChecked() && _plot->legend() != nullptr) {
_plot->insertLegend(nullptr);
}
// 应用曲线颜色
for (QwtPlotCurve* curve : curveList) {
QPen pen = curve->pen();
pen.setColor(curveColorMap.value(curve));
curve->setPen(pen);
}
// 刷新绘图
_plot->replot();
}
} }
void ConformToTheEnergySpectrum::slot_roi_data(ROIItem &item) void ConformToTheEnergySpectrum::slot_roi_data(ROIItem &item)

View File

@ -1,8 +1,16 @@
#include "CountRateAnalysisView.h" #include "CountRateAnalysisView.h"
#include "ui_CountRateAnalysisView.h" #include "ui_CountRateAnalysisView.h"
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton> #include <QPushButton>
#include <QMessageBox> #include <QMessageBox>
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QColorDialog>
#include <QMap>
#include <QwtPlotCurve> #include <QwtPlotCurve>
#include <QwtPlotMarker> #include <QwtPlotMarker>
#include <QwtPlotZoneItem> #include <QwtPlotZoneItem>
@ -198,5 +206,131 @@ void CountRateAnalysisView::setupMenu()
void CountRateAnalysisView::onActionPlotConfigure() void CountRateAnalysisView::onActionPlotConfigure()
{ {
QDialog dlg(this);
dlg.setWindowTitle(QStringLiteral(u"图表配置"));
dlg.setMinimumWidth(320);
QVBoxLayout* layout = new QVBoxLayout(&dlg);
// X轴标题
QHBoxLayout* xTitleLayout = new QHBoxLayout();
QLabel* xTitleLabel = new QLabel(QStringLiteral(u"X轴标题"), &dlg);
QLineEdit* xTitleEdit = new QLineEdit(plot->axisTitle(QwtPlot::xBottom).text(), &dlg);
xTitleLayout->addWidget(xTitleLabel);
xTitleLayout->addWidget(xTitleEdit);
layout->addLayout(xTitleLayout);
// Y轴标题
QHBoxLayout* yTitleLayout = new QHBoxLayout();
QLabel* yTitleLabel = new QLabel(QStringLiteral(u"Y轴标题"), &dlg);
QLineEdit* yTitleEdit = new QLineEdit(plot->axisTitle(QwtPlot::yLeft).text(), &dlg);
yTitleLayout->addWidget(yTitleLabel);
yTitleLayout->addWidget(yTitleEdit);
layout->addLayout(yTitleLayout);
layout->addSpacing(8);
// 显示网格
QCheckBox* gridCheck = new QCheckBox(QStringLiteral(u"显示网格线"), &dlg);
gridCheck->setChecked(plot->axisEnabled(QwtPlot::xBottom) && plot->axisEnabled(QwtPlot::yLeft));
layout->addWidget(gridCheck);
// 显示图例
QCheckBox* legendCheck = new QCheckBox(QStringLiteral(u"显示图例"), &dlg);
legendCheck->setChecked(plot->legend() != nullptr);
layout->addWidget(legendCheck);
layout->addSpacing(8);
// 曲线颜色
QLabel* curveColorLabel = new QLabel(QStringLiteral(u"曲线颜色:"), &dlg);
layout->addWidget(curveColorLabel);
// 获取所有曲线
QList<QwtPlotCurve*> curveList;
QMap<QwtPlotCurve*, QColor> curveColorMap;
const QwtPlotItemList& items = plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
curveList.append(curve);
curveColorMap.insert(curve, curve->pen().color());
}
}
// 为每条曲线创建颜色按钮
QList<QPushButton*> colorBtnList;
for (int i = 0; i < curveList.size(); ++i) {
QwtPlotCurve* curve = curveList.at(i);
QHBoxLayout* btnLayout = new QHBoxLayout();
QLabel* nameLabel = new QLabel(QStringLiteral(u"曲线%1").arg(i + 1), &dlg);
QPushButton* colorBtn = new QPushButton(&dlg);
colorBtn->setFixedSize(60, 24);
QColor color = curveColorMap.value(curve);
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(color.name()));
btnLayout->addWidget(nameLabel);
btnLayout->addWidget(colorBtn);
btnLayout->addStretch();
layout->addLayout(btnLayout);
colorBtnList.append(colorBtn);
connect(colorBtn, &QPushButton::clicked, [&dlg, curve, &curveColorMap, colorBtn]() {
QColor current = curveColorMap.value(curve);
QColor selected = QColorDialog::getColor(current, &dlg, QStringLiteral(u"选择曲线颜色"));
if (selected.isValid()) {
curveColorMap[curve] = selected;
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(selected.name()));
}
});
}
layout->addStretch();
// 确定取消按钮
QDialogButtonBox* buttonBox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dlg);
layout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
if (dlg.exec() == QDialog::Accepted) {
// 应用X轴标题
plot->setAxisTitle(QwtPlot::xBottom, xTitleEdit->text());
// 应用Y轴标题
plot->setAxisTitle(QwtPlot::yLeft, yTitleEdit->text());
// 应用网格显示
plot->enableAxis(QwtPlot::xBottom, gridCheck->isChecked());
plot->enableAxis(QwtPlot::yLeft, gridCheck->isChecked());
// 应用图例显示
if (legendCheck->isChecked() && plot->legend() == nullptr) {
QwtLegend* legend = new QwtLegend();
legend->setDefaultItemMode(QwtLegendData::ReadOnly);
plot->insertLegend(legend, QwtPlot::RightLegend);
// 给未命名曲线设置默认标题,保证图例显示名称
int idx = 1;
const QwtPlotItemList& items = plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
if (curve->title().isEmpty()) {
curve->setTitle(QStringLiteral(u"曲线%1").arg(idx));
}
++idx;
}
}
} else if (!legendCheck->isChecked() && plot->legend() != nullptr) {
plot->insertLegend(nullptr);
}
// 应用曲线颜色
for (QwtPlotCurve* curve : curveList) {
QPen pen = curve->pen();
pen.setColor(curveColorMap.value(curve));
curve->setPen(pen);
}
// 刷新绘图
plot->replot();
}
} }

View File

@ -1,14 +1,858 @@
#include "EfficiencyScale.h" #include "EfficiencyScale.h"
#include "ui_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) EfficiencyScale::EfficiencyScale(QWidget *parent)
: QDialog(parent) : QDialog(parent)
, ui(new Ui::EfficiencyScale) , ui(new Ui::EfficiencyScale)
{ {
ui->setupUi(this); 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() EfficiencyScale::~EfficiencyScale()
{ {
delete ui; 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));
}
}

View File

@ -2,11 +2,61 @@
#define EFFICIENCYSCALE_H #define EFFICIENCYSCALE_H
#include <QDialog> #include <QDialog>
#include <QListWidgetItem>
#include <QTableWidgetItem>
#include <QwtPlotCurve>
#include <QMap>
#include <QVector>
#include <QPointF>
namespace Ui { namespace Ui {
class EfficiencyScale; class EfficiencyScale;
} }
class CustomQwtPlot;
// 单个效率刻度点数据结构
struct EfficiencyPoint
{
double energy; // 能量(keV)
double netCount; // 净计数 N
double liveTime; // 测量活时间 t (s)
double activity; // 放射源活度 A (Bq)
double branchRatio; // 分支比 Pγ
double countRate; // 净计数率 N/t (cps) —— 计算值
double efficiency; // 探测效率 ε —— 计算值
double fitEfficiency; // 拟合效率 —— 拟合后计算值
double error; // 误差(%) —— 拟合后计算值
QString scaleDate; // 刻度日期
EfficiencyPoint()
: energy(0.0), netCount(0.0), liveTime(0.0), activity(0.0)
, branchRatio(1.0), countRate(0.0), efficiency(0.0)
, fitEfficiency(0.0), error(0.0) {}
};
// 多项式拟合结果
struct FitResult
{
bool valid;
int degree; // 拟合次数1=一次2=二次)
QVector<double> coeffs; // 多项式系数coeffs[0]为常数项coeffs[1]一次项...
double inflectionEnergy; // 拐点能量(keV),分段拟合时使用
FitResult() : valid(false), degree(1), inflectionEnergy(0.0) {}
};
// 单通道效率刻度数据
struct ChannelEfficiencyData
{
QVector<EfficiencyPoint> points; // 刻度点列表
FitResult highFit; // 拐点以上拟合结果
FitResult lowFit; // 拐点以下拟合结果
double inflectionEnergy; // 拐点能量(keV)
ChannelEfficiencyData() : inflectionEnergy(0.0) {}
};
class EfficiencyScale : public QDialog class EfficiencyScale : public QDialog
{ {
Q_OBJECT Q_OBJECT
@ -15,8 +65,88 @@ public:
explicit EfficiencyScale(QWidget *parent = nullptr); explicit EfficiencyScale(QWidget *parent = nullptr);
~EfficiencyScale(); ~EfficiencyScale();
private:
void setupPlot();
//加载能量刻度文件夹下所有的文件
void loadAllFilesInTheFolder();
//初始化通道数
void initComboBoxUi();
void clearPlot();
// 计算计数率countRate = netCount / liveTime (cps)
double calculateCountRate(double netCount, double liveTime) const;
// 计算单个能量点的探测效率:ε = N / (t * A * Pγ)
double calculateEfficiency(double netCount, double liveTime,
double activity, double branchRatio) const;
// 重新计算所有点的计数率和探测效率
void recalculateAllPoints(QVector<EfficiencyPoint>& points) const;
// 多项式拟合最小二乘法对ln(E)-ln(ε)做拟合
// degree: 1~5分别对应一次到五次拟合
FitResult polynomialFit(const QVector<EfficiencyPoint>& points,
int degree, double minEnergy, double maxEnergy) const;
// 根据拟合公式计算指定能量的效率
double calculateFitEfficiency(double energy, const FitResult& fit) const;
// 计算谱仪总效率(多通道计数率求和后计算效率)
double calculateTotalEfficiency(double energy, double totalNetCount,
double liveTime, double activity,
double branchRatio) const;
// 刷新表格数据
void refreshTable();
// 刷新绘图(原始散点 + 拟合曲线)
void refreshPlot();
// 生成拟合曲线上的采样点
QVector<QPointF> generateFitCurvePoints(const FitResult& highFit,
const FitResult& lowFit,
double inflectionEnergy,
int sampleCount = 200) const;
// 保存当前刻度数据到JSON文件
bool saveToJsonFile(const QString& filePath) const;
// 从JSON文件加载刻度数据
bool loadFromJsonFile(const QString& filePath);
// 获取当前选中通道的数据引用
ChannelEfficiencyData& currentChannelData();
const ChannelEfficiencyData& currentChannelData() const;
private slots:
void onItemDoubleClicked(QListWidgetItem *item);
void on_pBtn_Save_clicked();
void on_pBtn_SaveAs_clicked();
void on_pBtn_Add_clicked();
void on_pBtn_Delete_clicked();
void on_pBtn_Add_File_clicked();
void on_pBtn_Delete_File_clicked();
void on_pBtn_fitting_4_clicked(); // 拐点以上拟合
void on_pBtn_fitting_3_clicked(); // 拐点以下拟合
// 通道切换
void on_comboBox_channel_currentIndexChanged(int index);
// 表格单元格编辑后重算
void on_table_scale_data_itemChanged(QTableWidgetItem* item);
private: private:
Ui::EfficiencyScale *ui; Ui::EfficiencyScale *ui;
CustomQwtPlot* _plot = nullptr;
QwtPlotCurve *_rawDataCurve; // 原始数据散点曲线
QwtPlotCurve *_fitCurve; // 拟合曲线
QString m_currentFilePath;
QMap<QString, ChannelEfficiencyData> m_channelDataMap; // 各通道数据
QString m_currentChannel; // 当前选中通道名
QString m_scaleName; // 效率刻度名称
QString m_description; // 备注说明
}; };
#endif // EFFICIENCYSCALE_H #endif // EFFICIENCYSCALE_H

View File

@ -33,19 +33,19 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QListWidget" name="listWidget_3"/> <widget class="QListWidget" name="listWidget" />
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_21"> <layout class="QHBoxLayout" name="horizontalLayout_21">
<item> <item>
<widget class="QPushButton" name="pBtn_Add_File_3"> <widget class="QPushButton" name="pBtn_Add_File">
<property name="text"> <property name="text">
<string>添加</string> <string>添加</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="pBtn_Delete_File_3"> <widget class="QPushButton" name="pBtn_Delete_File">
<property name="text"> <property name="text">
<string>删除</string> <string>删除</string>
</property> </property>
@ -87,7 +87,7 @@
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_name_3"/> <widget class="QLineEdit" name="lineEdit_name" />
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_12"> <widget class="QLabel" name="label_12">
@ -132,7 +132,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="comboBox_channel_3"/> <widget class="QComboBox" name="comboBox_channel" />
</item> </item>
</layout> </layout>
</item> </item>
@ -155,14 +155,14 @@
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_18"> <layout class="QHBoxLayout" name="horizontalLayout_18">
<item> <item>
<widget class="QPushButton" name="pBtn_Save_3"> <widget class="QPushButton" name="pBtn_Save">
<property name="text"> <property name="text">
<string>保存</string> <string>保存</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="pBtn_SaveAs_3"> <widget class="QPushButton" name="pBtn_SaveAs">
<property name="text"> <property name="text">
<string>另存为</string> <string>另存为</string>
</property> </property>
@ -188,7 +188,7 @@
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_23"> <layout class="QHBoxLayout" name="horizontalLayout_23">
<item> <item>
<widget class="QComboBox" name="comboBox_fit_type_4"> <widget class="QComboBox" name="comboBox_fit_type">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -216,7 +216,7 @@
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_19"> <layout class="QHBoxLayout" name="horizontalLayout_19">
<item> <item>
<widget class="QComboBox" name="comboBox_fit_type_3"> <widget class="QComboBox" name="comboBox_fit_type_1">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -254,14 +254,14 @@
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_20"> <layout class="QHBoxLayout" name="horizontalLayout_20">
<item> <item>
<widget class="QPushButton" name="pBtn_Add_3"> <widget class="QPushButton" name="pBtn_Add">
<property name="text"> <property name="text">
<string>新增</string> <string>新增</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="pBtn_Delete_3"> <widget class="QPushButton" name="pBtn_Delete">
<property name="text"> <property name="text">
<string>删除</string> <string>删除</string>
</property> </property>
@ -287,7 +287,7 @@
<item> <item>
<widget class="QWidget" name="widget_3" native="true"> <widget class="QWidget" name="widget_3" native="true">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true"/> <string notr="true" />
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_17"> <layout class="QVBoxLayout" name="verticalLayout_17">
<property name="spacing"> <property name="spacing">
@ -306,7 +306,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<layout class="QVBoxLayout" name="layout_fittingCurve_3"> <layout class="QVBoxLayout" name="layout_fittingCurve">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>0</number>
</property> </property>
@ -318,7 +318,7 @@
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QTableWidget" name="tablew_scale_data_3"> <widget class="QTableWidget" name="table_scale_data">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>0</width> <width>0</width>
@ -330,7 +330,37 @@
</attribute> </attribute>
<column> <column>
<property name="text"> <property name="text">
<string>能量</string> <string>序号</string>
</property>
</column>
<column>
<property name="text">
<string>能量(keV)</string>
</property>
</column>
<column>
<property name="text">
<string>净计数(N)</string>
</property>
</column>
<column>
<property name="text">
<string>活时间(s)</string>
</property>
</column>
<column>
<property name="text">
<string>活度(Bq)</string>
</property>
</column>
<column>
<property name="text">
<string>分支比</string>
</property>
</column>
<column>
<property name="text">
<string>净计数率(cps)</string>
</property> </property>
</column> </column>
<column> <column>
@ -345,7 +375,7 @@
</column> </column>
<column> <column>
<property name="text"> <property name="text">
<string>误差</string> <string>误差(%)</string>
</property> </property>
</column> </column>
<column> <column>
@ -359,6 +389,6 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<resources/> <resources />
<connections/> <connections />
</ui> </ui>

View File

@ -406,6 +406,7 @@ void EnergyCountPeakFitView::onActionCurveShowSetting()
void EnergyCountPeakFitView::onActionPlotConfigure() void EnergyCountPeakFitView::onActionPlotConfigure()
{ {
} }
void EnergyCountPeakFitView::clearAllSelectionRects() void EnergyCountPeakFitView::clearAllSelectionRects()

View File

@ -15,6 +15,13 @@
#include <QCheckBox> #include <QCheckBox>
#include <QwtScaleDiv> #include <QwtScaleDiv>
#include <QPen> #include <QPen>
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QColorDialog>
#include <QMap>
//能量计数谱 //能量计数谱
EnergyCountPlotView::EnergyCountPlotView(QWidget *parent) EnergyCountPlotView::EnergyCountPlotView(QWidget *parent)
: MeasureAnalysisView { parent } : MeasureAnalysisView { parent }
@ -285,7 +292,166 @@ void EnergyCountPlotView::onActionCurveShowSetting()
void EnergyCountPlotView::onActionPlotConfigure() void EnergyCountPlotView::onActionPlotConfigure()
{ {
QDialog dlg(this);
dlg.setWindowTitle(QStringLiteral(u"图表配置"));
dlg.setMinimumWidth(520);
QVBoxLayout* layout = new QVBoxLayout(&dlg);
// X轴标题
QHBoxLayout* xTitleLayout = new QHBoxLayout();
QLabel* xTitleLabel = new QLabel(QStringLiteral(u"X轴标题"), &dlg);
QLineEdit* xTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::xBottom).text(), &dlg);
xTitleLayout->addWidget(xTitleLabel);
xTitleLayout->addWidget(xTitleEdit);
layout->addLayout(xTitleLayout);
// Y轴标题
QHBoxLayout* yTitleLayout = new QHBoxLayout();
QLabel* yTitleLabel = new QLabel(QStringLiteral(u"Y轴标题"), &dlg);
QLineEdit* yTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::yLeft).text(), &dlg);
yTitleLayout->addWidget(yTitleLabel);
yTitleLayout->addWidget(yTitleEdit);
layout->addLayout(yTitleLayout);
layout->addSpacing(8);
// 显示网格
QCheckBox* gridCheck = new QCheckBox(QStringLiteral(u"显示网格线"), &dlg);
gridCheck->setChecked(_plot->axisEnabled(QwtPlot::xBottom) && _plot->axisEnabled(QwtPlot::yLeft));
layout->addWidget(gridCheck);
// 显示图例
QCheckBox* legendCheck = new QCheckBox(QStringLiteral(u"显示图例"), &dlg);
legendCheck->setChecked(_plot->legend() != nullptr);
layout->addWidget(legendCheck);
layout->addSpacing(8);
// 曲线颜色
QLabel* curveColorLabel = new QLabel(QStringLiteral(u"曲线颜色:"), &dlg);
layout->addWidget(curveColorLabel);
// 获取所有曲线并按通道号排序(与图例显示顺序一致)
QList<QwtPlotCurve*> curveList;
QMap<QwtPlotCurve*, QColor> curveColorMap;
QStringList chNameList;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
curveList.append(curve);
curveColorMap.insert(curve, curve->pen().color());
chNameList.append(curve->title().text());
}
}
// 按通道号排序
std::sort(chNameList.begin(), chNameList.end(), [](const QString& a, const QString& b) {
int num_a = ExtractNumberFromString(a);
int num_b = ExtractNumberFromString(b);
return num_a < num_b;
});
// 按排序后的顺序重建曲线列表
QList<QwtPlotCurve*> sortedCurveList;
for (const QString& chName : chNameList) {
for (QwtPlotCurve* curve : curveList) {
if (curve->title().text() == chName) {
sortedCurveList.append(curve);
break;
}
}
}
curveList = sortedCurveList;
// 多列网格布局4列降低对话框高度
const int numColumns = 4;
QGridLayout* gridLayout = new QGridLayout();
gridLayout->setHorizontalSpacing(12);
gridLayout->setVerticalSpacing(4);
QList<QPushButton*> colorBtnList;
for (int i = 0; i < curveList.size(); ++i) {
QwtPlotCurve* curve = curveList.at(i);
int row = i / numColumns;
int col = i % numColumns;
// 每行单元:颜色按钮 + 通道名
QHBoxLayout* cellLayout = new QHBoxLayout();
cellLayout->setSpacing(6);
QPushButton* colorBtn = new QPushButton(&dlg);
colorBtn->setFixedSize(36, 20);
QColor color = curveColorMap.value(curve);
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(color.name()));
QLabel* nameLabel = new QLabel(curve->title().text(), &dlg);
cellLayout->addWidget(colorBtn);
cellLayout->addWidget(nameLabel);
cellLayout->addStretch();
gridLayout->addLayout(cellLayout, row, col);
colorBtnList.append(colorBtn);
connect(colorBtn, &QPushButton::clicked, [&dlg, curve, &curveColorMap, colorBtn]() {
QColor current = curveColorMap.value(curve);
QColor selected = QColorDialog::getColor(current, &dlg, QStringLiteral(u"选择曲线颜色"));
if (selected.isValid()) {
curveColorMap[curve] = selected;
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(selected.name()));
}
});
}
layout->addLayout(gridLayout);
layout->addStretch();
// 确定取消按钮
QDialogButtonBox* buttonBox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dlg);
layout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
if (dlg.exec() == QDialog::Accepted) {
// 应用X轴标题
_plot->setAxisTitle(QwtPlot::xBottom, xTitleEdit->text());
// 应用Y轴标题
_plot->setAxisTitle(QwtPlot::yLeft, yTitleEdit->text());
// 应用网格显示
_plot->enableAxis(QwtPlot::xBottom, gridCheck->isChecked());
_plot->enableAxis(QwtPlot::yLeft, gridCheck->isChecked());
// 应用图例显示
if (legendCheck->isChecked() && _plot->legend() == nullptr) {
QwtLegend* legend = new QwtLegend();
legend->setDefaultItemMode(QwtLegendData::ReadOnly);
_plot->insertLegend(legend, QwtPlot::RightLegend);
int idx = 1;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
if (curve->title().isEmpty()) {
curve->setTitle(QStringLiteral(u"曲线%1").arg(idx));
}
++idx;
}
}
} else if (!legendCheck->isChecked() && _plot->legend() != nullptr) {
_plot->insertLegend(nullptr);
}
// 应用曲线颜色
for (QwtPlotCurve* curve : curveList) {
QPen pen = curve->pen();
pen.setColor(curveColorMap.value(curve));
curve->setPen(pen);
}
// 刷新绘图
_plot->replot();
}
} }
void EnergyCountPlotView::onActionRoiTriggered() void EnergyCountPlotView::onActionRoiTriggered()

View File

@ -38,6 +38,7 @@
#include "ParticleCountPlotView.h" #include "ParticleCountPlotView.h"
#include "reportwidget.h" #include "reportwidget.h"
#include "RegionOfInterest.h" #include "RegionOfInterest.h"
#include "EfficiencyScale.h"
#include <array> #include <array>
#include "csv.h" #include "csv.h"
#include <fstream> #include <fstream>
@ -159,6 +160,7 @@ MainWindow::~MainWindow()
void MainWindow::onMeasureStarted(bool success, const QString& message) void MainWindow::onMeasureStarted(bool success, const QString& message)
{ {
LOG_INFO(message);
// Q_UNUSED(message); // Q_UNUSED(message);
// if (success) { // if (success) {
// ui->action_start_measure->setEnabled(false); // ui->action_start_measure->setEnabled(false);
@ -174,6 +176,7 @@ void MainWindow::onMeasureStopped(bool success, const QString& message)
// ui->action_stop_measure->setEnabled(false); // ui->action_stop_measure->setEnabled(false);
m_AddressCountTimer->stop(); m_AddressCountTimer->stop();
m_EnergyCountTimer->stop(); m_EnergyCountTimer->stop();
LOG_INFO(message);
// // 更新树节点状态:把"测量中"改回"已停止" // // 更新树节点状态:把"测量中"改回"已停止"
// if (!nodeMap.isEmpty()) { // if (!nodeMap.isEmpty()) {
// MeasureAnalysisProjectModel* models = ProjectList::Instance()->GetCurrentProjectModel(); // MeasureAnalysisProjectModel* models = ProjectList::Instance()->GetCurrentProjectModel();
@ -189,7 +192,8 @@ void MainWindow::onMeasureStopped(bool success, const QString& message)
void MainWindow::onDeviceStatusMessage(const QString& msg) void MainWindow::onDeviceStatusMessage(const QString& msg)
{ {
ShowStatusBarMsg(msg); // ShowStatusBarMsg(msg);
LOG_INFO(msg);
} }
void MainWindow::initMainWindow() void MainWindow::initMainWindow()
@ -540,7 +544,7 @@ void MainWindow::onUpdateParticleDataTreeStatus()
nodeMap = project_node_items[models->GetProjectName()]; nodeMap = project_node_items[models->GetProjectName()];
QStandardItem *nodeItem = nodeMap[models->GetProjectName()]; QStandardItem *nodeItem = nodeMap[models->GetProjectName()];
ProjectList::Instance()->SetNodeStatus(nodeItem,"测量中",true); ProjectList::Instance()->SetNodeStatus(nodeItem,"测量中",true);
LOG_INFO("测量中");
QString dir = ProjectList::Instance()->GetCurrentProjectModel()->GetProjectDir(); QString dir = ProjectList::Instance()->GetCurrentProjectModel()->GetProjectDir();
QString csvPath = QStringLiteral(u"%1/%2").arg(dir).arg("粒子数据.csv"); QString csvPath = QStringLiteral(u"%1/%2").arg(dir).arg("粒子数据.csv");
models->SetAllChannelParticleDataFilename(csvPath); models->SetAllChannelParticleDataFilename(csvPath);
@ -888,7 +892,6 @@ void MainWindow::on_action_analysisReport_triggered()
widget->show(); widget->show();
} }
#include "EfficiencyScale.h"
void MainWindow::on_action_efficiency_scale_mrg_triggered() void MainWindow::on_action_efficiency_scale_mrg_triggered()
{ {
// QDialog energy_scale_mrg_dlg; // QDialog energy_scale_mrg_dlg;

View File

@ -16,6 +16,17 @@
#include <QMessageBox> #include <QMessageBox>
#include <QHeaderView> #include <QHeaderView>
#include "AddNuclideDialog.h" #include "AddNuclideDialog.h"
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QColorDialog>
#include <QMap>
#include <QwtLegend>
//核素分析
// 更新选择框显示 // 更新选择框显示
static void updateSelectionMarkers(QwtPlot* plot, static void updateSelectionMarkers(QwtPlot* plot,
QwtPlotMarker*& leftMarker, QwtPlotMarker*& leftMarker,
@ -275,7 +286,133 @@ void NuclideAnalysisView::loadDataFromFile(const QString& data_name, const QStri
void NuclideAnalysisView::onActionPlotConfigure() void NuclideAnalysisView::onActionPlotConfigure()
{ {
QDialog dlg(this);
dlg.setWindowTitle(QStringLiteral(u"图表配置"));
dlg.setMinimumWidth(320);
QVBoxLayout* layout = new QVBoxLayout(&dlg);
// X轴标题
QHBoxLayout* xTitleLayout = new QHBoxLayout();
QLabel* xTitleLabel = new QLabel(QStringLiteral(u"X轴标题"), &dlg);
QLineEdit* xTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::xBottom).text(), &dlg);
xTitleLayout->addWidget(xTitleLabel);
xTitleLayout->addWidget(xTitleEdit);
layout->addLayout(xTitleLayout);
// Y轴标题
QHBoxLayout* yTitleLayout = new QHBoxLayout();
QLabel* yTitleLabel = new QLabel(QStringLiteral(u"Y轴标题"), &dlg);
QLineEdit* yTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::yLeft).text(), &dlg);
yTitleLayout->addWidget(yTitleLabel);
yTitleLayout->addWidget(yTitleEdit);
layout->addLayout(yTitleLayout);
layout->addSpacing(8);
// 显示网格
QCheckBox* gridCheck = new QCheckBox(QStringLiteral(u"显示网格线"), &dlg);
gridCheck->setChecked(_plot->axisEnabled(QwtPlot::xBottom) && _plot->axisEnabled(QwtPlot::yLeft));
layout->addWidget(gridCheck);
// 显示图例
QCheckBox* legendCheck = new QCheckBox(QStringLiteral(u"显示图例"), &dlg);
legendCheck->setChecked(_plot->legend() != nullptr);
layout->addWidget(legendCheck);
layout->addSpacing(8);
// 曲线颜色
QLabel* curveColorLabel = new QLabel(QStringLiteral(u"曲线颜色:"), &dlg);
layout->addWidget(curveColorLabel);
// 获取所有曲线
QList<QwtPlotCurve*> curveList;
QMap<QwtPlotCurve*, QColor> curveColorMap;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
curveList.append(curve);
curveColorMap.insert(curve, curve->pen().color());
}
}
// 为每条曲线创建颜色按钮
QList<QPushButton*> colorBtnList;
for (int i = 0; i < curveList.size(); ++i) {
QwtPlotCurve* curve = curveList.at(i);
QHBoxLayout* btnLayout = new QHBoxLayout();
QLabel* nameLabel = new QLabel(QStringLiteral(u"曲线%1").arg(i + 1), &dlg);
QPushButton* colorBtn = new QPushButton(&dlg);
colorBtn->setFixedSize(60, 24);
QColor color = curveColorMap.value(curve);
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(color.name()));
btnLayout->addWidget(nameLabel);
btnLayout->addWidget(colorBtn);
btnLayout->addStretch();
layout->addLayout(btnLayout);
colorBtnList.append(colorBtn);
connect(colorBtn, &QPushButton::clicked, [&dlg, curve, &curveColorMap, colorBtn]() {
QColor current = curveColorMap.value(curve);
QColor selected = QColorDialog::getColor(current, &dlg, QStringLiteral(u"选择曲线颜色"));
if (selected.isValid()) {
curveColorMap[curve] = selected;
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(selected.name()));
}
});
}
layout->addStretch();
// 确定取消按钮
QDialogButtonBox* buttonBox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dlg);
layout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
if (dlg.exec() == QDialog::Accepted) {
// 应用X轴标题
_plot->setAxisTitle(QwtPlot::xBottom, xTitleEdit->text());
// 应用Y轴标题
_plot->setAxisTitle(QwtPlot::yLeft, yTitleEdit->text());
// 应用网格显示
_plot->enableAxis(QwtPlot::xBottom, gridCheck->isChecked());
_plot->enableAxis(QwtPlot::yLeft, gridCheck->isChecked());
// 应用图例显示
if (legendCheck->isChecked() && _plot->legend() == nullptr) {
QwtLegend* legend = new QwtLegend();
legend->setDefaultItemMode(QwtLegendData::ReadOnly);
_plot->insertLegend(legend, QwtPlot::RightLegend);
// 给未命名曲线设置默认标题,保证图例显示名称
int idx = 1;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
if (curve->title().isEmpty()) {
curve->setTitle(QStringLiteral(u"曲线%1").arg(idx));
}
++idx;
}
}
} else if (!legendCheck->isChecked() && _plot->legend() != nullptr) {
_plot->insertLegend(nullptr);
}
// 应用曲线颜色
for (QwtPlotCurve* curve : curveList) {
QPen pen = curve->pen();
pen.setColor(curveColorMap.value(curve));
curve->setPen(pen);
}
// 刷新绘图
_plot->replot();
}
} }
void NuclideAnalysisView::onActionPlotSelect() void NuclideAnalysisView::onActionPlotSelect()

View File

@ -28,6 +28,14 @@
#include "BatchEnergyScaleDialog.h" #include "BatchEnergyScaleDialog.h"
#include "FindPeaksResultDialog.h" #include "FindPeaksResultDialog.h"
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QColorDialog>
#include <QMap>
//道址计数谱
ParticleCountPlotView::ParticleCountPlotView(QWidget* parent) ParticleCountPlotView::ParticleCountPlotView(QWidget* parent)
: MeasureAnalysisView { parent } : MeasureAnalysisView { parent }
{ {
@ -120,7 +128,7 @@ void ParticleCountPlotView::updateView(const QMap<QString, QVariant>& data_files
// 清除现有所有曲线 // 清除现有所有曲线
for (QwtPlotCurve* curve : this->_plot->GetCurveList()) { for (QwtPlotCurve* curve : this->_plot->GetCurveList()) {
curve->detach(); curve->detach();
delete curve; // delete curve;
} }
_plot->clearCurve(); _plot->clearCurve();
@ -230,33 +238,74 @@ void ParticleCountPlotView::setupEnergyScaleDlg()
void ParticleCountPlotView::loadDataFromFile(const QString& data_name, const QString& filename) void ParticleCountPlotView::loadDataFromFile(const QString& data_name, const QString& filename)
{ {
std::string address_str = QString(QStringLiteral(u"道址")).toStdString(); // std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
std::string count_str = QString(QStringLiteral(u"计数")).toStdString(); // std::string count_str = QString(QStringLiteral(u"计数")).toStdString();
io::CSVReader< // io::CSVReader<
2, // 2,
io::trim_chars<' ', '\t'>, // io::trim_chars<' ', '\t'>,
io::double_quote_escape<',', '"'>, // io::double_quote_escape<',', '"'>,
io::throw_on_overflow, // io::throw_on_overflow,
io::empty_line_comment> // io::empty_line_comment
reader(QStrToSysPath(filename)); // >
reader.read_header(io::ignore_extra_column, address_str, count_str); // reader(QStrToSysPath(filename));
// reader.read_header(io::ignore_extra_column, address_str, count_str);
// int address;
// unsigned long long particle_count;
// QVector<double> x, y;
// while (reader.read_row(address, particle_count)) {
// x.push_back(address);
// y.push_back(particle_count);
// if ( _x_max < address )
// _x_max = address;
// if ( _y_max < particle_count )
// _y_max = particle_count;
// }
// _plot->SetAxisInitRange(QwtPlot::xBottom, 0.0f, _x_max);
// _plot->SetAxisInitRange(QwtPlot::yLeft, 0.0f, _y_max);
// // 绘制曲线
// QwtPlotCurve* curve = new QwtPlotCurve(data_name);
// curve->setSamples(x, y);
// _plot->AddCurve(curve);
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
LOG_WARN(QStringLiteral(u"无法打开文件: %1").arg(filename));
return;
}
QTextStream in(&file);
in.setCodec("UTF-8");
// 跳过表头(道址,计数)
QString header = in.readLine();
Q_UNUSED(header);
QVector<double> x, y;
int address; int address;
unsigned long long particle_count; unsigned long long particle_count;
QVector<double> x, y;
while (reader.read_row(address, particle_count)) { while (!in.atEnd()) {
x.push_back(address); QString line = in.readLine();
y.push_back(particle_count); QStringList fields = line.split(',');
if ( _x_max < address ) if (fields.size() >= 2) {
_x_max = address; address = fields[0].trimmed().toInt();
if ( _y_max < particle_count ) particle_count = fields[1].trimmed().toULongLong();
_y_max = particle_count; x.push_back(address);
y.push_back(particle_count);
if (_x_max < address)
_x_max = address;
if (_y_max < particle_count)
_y_max = particle_count;
}
} }
file.close();
_plot->SetAxisInitRange(QwtPlot::xBottom, 0.0f, _x_max); _plot->SetAxisInitRange(QwtPlot::xBottom, 0.0f, _x_max);
_plot->SetAxisInitRange(QwtPlot::yLeft, 0.0f, _y_max); _plot->SetAxisInitRange(QwtPlot::yLeft, 0.0f, _y_max);
// 绘制曲线
QwtPlotCurve* curve = new QwtPlotCurve(data_name); QwtPlotCurve* curve = new QwtPlotCurve(data_name);
curve->setSamples(x, y); curve->setSamples(x, y);
_plot->AddCurve(curve); _plot->AddCurve(curve);
@ -535,5 +584,164 @@ void ParticleCountPlotView::onActionEnergyScale()
void ParticleCountPlotView::onActionPlotConfigure() void ParticleCountPlotView::onActionPlotConfigure()
{ {
QDialog dlg(this);
dlg.setWindowTitle(QStringLiteral(u"图表配置"));
dlg.setMinimumWidth(520);
QVBoxLayout* layout = new QVBoxLayout(&dlg);
// X轴标题
QHBoxLayout* xTitleLayout = new QHBoxLayout();
QLabel* xTitleLabel = new QLabel(QStringLiteral(u"X轴标题"), &dlg);
QLineEdit* xTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::xBottom).text(), &dlg);
xTitleLayout->addWidget(xTitleLabel);
xTitleLayout->addWidget(xTitleEdit);
layout->addLayout(xTitleLayout);
// Y轴标题
QHBoxLayout* yTitleLayout = new QHBoxLayout();
QLabel* yTitleLabel = new QLabel(QStringLiteral(u"Y轴标题"), &dlg);
QLineEdit* yTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::yLeft).text(), &dlg);
yTitleLayout->addWidget(yTitleLabel);
yTitleLayout->addWidget(yTitleEdit);
layout->addLayout(yTitleLayout);
layout->addSpacing(8);
// 显示网格
QCheckBox* gridCheck = new QCheckBox(QStringLiteral(u"显示网格线"), &dlg);
gridCheck->setChecked(_plot->axisEnabled(QwtPlot::xBottom) && _plot->axisEnabled(QwtPlot::yLeft));
layout->addWidget(gridCheck);
// 显示图例
QCheckBox* legendCheck = new QCheckBox(QStringLiteral(u"显示图例"), &dlg);
legendCheck->setChecked(_plot->legend() != nullptr);
layout->addWidget(legendCheck);
layout->addSpacing(8);
// 曲线颜色
QLabel* curveColorLabel = new QLabel(QStringLiteral(u"曲线颜色:"), &dlg);
layout->addWidget(curveColorLabel);
// 获取所有曲线并按通道号排序(与图例显示顺序一致)
QList<QwtPlotCurve*> curveList;
QMap<QwtPlotCurve*, QColor> curveColorMap;
QStringList chNameList;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
curveList.append(curve);
curveColorMap.insert(curve, curve->pen().color());
chNameList.append(curve->title().text());
}
}
// 按通道号排序
std::sort(chNameList.begin(), chNameList.end(), [](const QString& a, const QString& b) {
int num_a = ExtractNumberFromString(a);
int num_b = ExtractNumberFromString(b);
return num_a < num_b;
});
// 按排序后的顺序重建曲线列表
QList<QwtPlotCurve*> sortedCurveList;
for (const QString& chName : chNameList) {
for (QwtPlotCurve* curve : curveList) {
if (curve->title().text() == chName) {
sortedCurveList.append(curve);
break;
}
}
}
curveList = sortedCurveList;
// 多列网格布局4列降低对话框高度
const int numColumns = 4;
QGridLayout* gridLayout = new QGridLayout();
gridLayout->setHorizontalSpacing(12);
gridLayout->setVerticalSpacing(4);
QList<QPushButton*> colorBtnList;
for (int i = 0; i < curveList.size(); ++i) {
QwtPlotCurve* curve = curveList.at(i);
int row = i / numColumns;
int col = i % numColumns;
// 每行单元:颜色按钮 + 通道名
QHBoxLayout* cellLayout = new QHBoxLayout();
cellLayout->setSpacing(6);
QPushButton* colorBtn = new QPushButton(&dlg);
colorBtn->setFixedSize(36, 20);
QColor color = curveColorMap.value(curve);
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(color.name()));
QLabel* nameLabel = new QLabel(curve->title().text(), &dlg);
cellLayout->addWidget(colorBtn);
cellLayout->addWidget(nameLabel);
cellLayout->addStretch();
gridLayout->addLayout(cellLayout, row, col);
colorBtnList.append(colorBtn);
connect(colorBtn, &QPushButton::clicked, [&dlg, curve, &curveColorMap, colorBtn]() {
QColor current = curveColorMap.value(curve);
QColor selected = QColorDialog::getColor(current, &dlg, QStringLiteral(u"选择曲线颜色"));
if (selected.isValid()) {
curveColorMap[curve] = selected;
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(selected.name()));
}
});
}
layout->addLayout(gridLayout);
layout->addStretch();
// 确定取消按钮
QDialogButtonBox* buttonBox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dlg);
layout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
if (dlg.exec() == QDialog::Accepted) {
// 应用X轴标题
_plot->setAxisTitle(QwtPlot::xBottom, xTitleEdit->text());
// 应用Y轴标题
_plot->setAxisTitle(QwtPlot::yLeft, yTitleEdit->text());
// 应用网格显示
_plot->enableAxis(QwtPlot::xBottom, gridCheck->isChecked());
_plot->enableAxis(QwtPlot::yLeft, gridCheck->isChecked());
// 应用图例显示
if (legendCheck->isChecked() && _plot->legend() == nullptr) {
QwtLegend* legend = new QwtLegend();
legend->setDefaultItemMode(QwtLegendData::ReadOnly);
_plot->insertLegend(legend, QwtPlot::RightLegend);
int idx = 1;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
if (curve->title().isEmpty()) {
curve->setTitle(QStringLiteral(u"曲线%1").arg(idx));
}
++idx;
}
}
} else if (!legendCheck->isChecked() && _plot->legend() != nullptr) {
_plot->insertLegend(nullptr);
}
// 应用曲线颜色
for (QwtPlotCurve* curve : curveList) {
QPen pen = curve->pen();
pen.setColor(curveColorMap.value(curve));
curve->setPen(pen);
}
// 刷新绘图
_plot->replot();
}
} }

View File

@ -19,6 +19,16 @@
#include <QMenu> #include <QMenu>
#include <QAction> #include <QAction>
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QColorDialog>
#include <QMap>
#include <QwtLegend>
//粒子入射时间分析
ParticleInjectTimeAnalysisView::ParticleInjectTimeAnalysisView(QWidget *parent) : ParticleInjectTimeAnalysisView::ParticleInjectTimeAnalysisView(QWidget *parent) :
MeasureAnalysisView(parent) MeasureAnalysisView(parent)
{ {
@ -153,7 +163,133 @@ void ParticleInjectTimeAnalysisView::updateData(bool b_init_update)
void ParticleInjectTimeAnalysisView::onActionPlotConfigure() void ParticleInjectTimeAnalysisView::onActionPlotConfigure()
{ {
QDialog dlg(this);
dlg.setWindowTitle(QStringLiteral(u"图表配置"));
dlg.setMinimumWidth(320);
QVBoxLayout* layout = new QVBoxLayout(&dlg);
// X轴标题
QHBoxLayout* xTitleLayout = new QHBoxLayout();
QLabel* xTitleLabel = new QLabel(QStringLiteral(u"X轴标题"), &dlg);
QLineEdit* xTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::xBottom).text(), &dlg);
xTitleLayout->addWidget(xTitleLabel);
xTitleLayout->addWidget(xTitleEdit);
layout->addLayout(xTitleLayout);
// Y轴标题
QHBoxLayout* yTitleLayout = new QHBoxLayout();
QLabel* yTitleLabel = new QLabel(QStringLiteral(u"Y轴标题"), &dlg);
QLineEdit* yTitleEdit = new QLineEdit(_plot->axisTitle(QwtPlot::yLeft).text(), &dlg);
yTitleLayout->addWidget(yTitleLabel);
yTitleLayout->addWidget(yTitleEdit);
layout->addLayout(yTitleLayout);
layout->addSpacing(8);
// 显示网格
QCheckBox* gridCheck = new QCheckBox(QStringLiteral(u"显示网格线"), &dlg);
gridCheck->setChecked(_plot->axisEnabled(QwtPlot::xBottom) && _plot->axisEnabled(QwtPlot::yLeft));
layout->addWidget(gridCheck);
// 显示图例
QCheckBox* legendCheck = new QCheckBox(QStringLiteral(u"显示图例"), &dlg);
legendCheck->setChecked(_plot->legend() != nullptr);
layout->addWidget(legendCheck);
layout->addSpacing(8);
// 曲线颜色
QLabel* curveColorLabel = new QLabel(QStringLiteral(u"曲线颜色:"), &dlg);
layout->addWidget(curveColorLabel);
// 获取所有曲线
QList<QwtPlotCurve*> curveList;
QMap<QwtPlotCurve*, QColor> curveColorMap;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
curveList.append(curve);
curveColorMap.insert(curve, curve->pen().color());
}
}
// 为每条曲线创建颜色按钮
QList<QPushButton*> colorBtnList;
for (int i = 0; i < curveList.size(); ++i) {
QwtPlotCurve* curve = curveList.at(i);
QHBoxLayout* btnLayout = new QHBoxLayout();
QLabel* nameLabel = new QLabel(QStringLiteral(u"曲线%1").arg(i + 1), &dlg);
QPushButton* colorBtn = new QPushButton(&dlg);
colorBtn->setFixedSize(60, 24);
QColor color = curveColorMap.value(curve);
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(color.name()));
btnLayout->addWidget(nameLabel);
btnLayout->addWidget(colorBtn);
btnLayout->addStretch();
layout->addLayout(btnLayout);
colorBtnList.append(colorBtn);
connect(colorBtn, &QPushButton::clicked, [&dlg, curve, &curveColorMap, colorBtn]() {
QColor current = curveColorMap.value(curve);
QColor selected = QColorDialog::getColor(current, &dlg, QStringLiteral(u"选择曲线颜色"));
if (selected.isValid()) {
curveColorMap[curve] = selected;
colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #999;")
.arg(selected.name()));
}
});
}
layout->addStretch();
// 确定取消按钮
QDialogButtonBox* buttonBox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dlg);
layout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
if (dlg.exec() == QDialog::Accepted) {
// 应用X轴标题
_plot->setAxisTitle(QwtPlot::xBottom, xTitleEdit->text());
// 应用Y轴标题
_plot->setAxisTitle(QwtPlot::yLeft, yTitleEdit->text());
// 应用网格显示
_plot->enableAxis(QwtPlot::xBottom, gridCheck->isChecked());
_plot->enableAxis(QwtPlot::yLeft, gridCheck->isChecked());
// 应用图例显示
if (legendCheck->isChecked() && _plot->legend() == nullptr) {
QwtLegend* legend = new QwtLegend();
legend->setDefaultItemMode(QwtLegendData::ReadOnly);
_plot->insertLegend(legend, QwtPlot::RightLegend);
// 给未命名曲线设置默认标题,保证图例显示名称
int idx = 1;
const QwtPlotItemList& items = _plot->itemList();
for (QwtPlotItem* item : items) {
if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) {
QwtPlotCurve* curve = static_cast<QwtPlotCurve*>(item);
if (curve->title().isEmpty()) {
curve->setTitle(QStringLiteral(u"曲线%1").arg(idx));
}
++idx;
}
}
} else if (!legendCheck->isChecked() && _plot->legend() != nullptr) {
_plot->insertLegend(nullptr);
}
// 应用曲线颜色
for (QwtPlotCurve* curve : curveList) {
QPen pen = curve->pen();
pen.setColor(curveColorMap.value(curve));
curve->setPen(pen);
}
// 刷新绘图
_plot->replot();
}
} }
void ParticleInjectTimeAnalysisView::showEvent(QShowEvent *e) void ParticleInjectTimeAnalysisView::showEvent(QShowEvent *e)

View File

@ -175,6 +175,7 @@ void ParticleTimeDifferenceView::loadDataFromFile()
void ParticleTimeDifferenceView::onActionPlotConfigure() void ParticleTimeDifferenceView::onActionPlotConfigure()
{ {
} }
void ParticleTimeDifferenceView::showEvent(QShowEvent *e) void ParticleTimeDifferenceView::showEvent(QShowEvent *e)