新增效率刻度功能,增加图标配置功能
This commit is contained in:
parent
29a746d873
commit
8ee6c4387c
|
|
@ -15,6 +15,16 @@
|
|||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QColorDialog>
|
||||
#include <QMap>
|
||||
#include <QPushButton>
|
||||
#include <QwtLegend>
|
||||
//反符合能谱
|
||||
AntiConformEnergySpectrumView::AntiConformEnergySpectrumView(QWidget* parent)
|
||||
: MeasureAnalysisView(parent)
|
||||
|
|
@ -155,6 +165,133 @@ void AntiConformEnergySpectrumView::clearAllRoiMarkers()
|
|||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,15 @@
|
|||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <QPen>
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QColorDialog>
|
||||
#include <QMap>
|
||||
#include <QPushButton>
|
||||
#include <QwtLegend>
|
||||
struct SpectrumData
|
||||
{
|
||||
int eventId;//事件ID
|
||||
|
|
@ -209,7 +218,133 @@ void CoincidenceEventTimeView::setupMenu()
|
|||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,16 @@
|
|||
#include <cstdint>
|
||||
#include <QPen>
|
||||
#include <QMenu>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QColorDialog>
|
||||
#include <QMap>
|
||||
#include <QPushButton>
|
||||
#include <QwtLegend>
|
||||
//符合能谱
|
||||
struct SpectrumData
|
||||
{
|
||||
|
|
@ -194,7 +204,133 @@ void ConformToTheEnergySpectrum::clearAllRoiMarkers()
|
|||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,16 @@
|
|||
#include "CountRateAnalysisView.h"
|
||||
#include "ui_CountRateAnalysisView.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QColorDialog>
|
||||
#include <QMap>
|
||||
#include <QwtPlotCurve>
|
||||
#include <QwtPlotMarker>
|
||||
#include <QwtPlotZoneItem>
|
||||
|
|
@ -198,5 +206,131 @@ void CountRateAnalysisView::setupMenu()
|
|||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,858 @@
|
|||
#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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,61 @@
|
|||
#define EFFICIENCYSCALE_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QListWidgetItem>
|
||||
#include <QTableWidgetItem>
|
||||
#include <QwtPlotCurve>
|
||||
#include <QMap>
|
||||
#include <QVector>
|
||||
#include <QPointF>
|
||||
|
||||
namespace Ui {
|
||||
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
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
@ -15,8 +65,88 @@ public:
|
|||
explicit EfficiencyScale(QWidget *parent = nullptr);
|
||||
~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:
|
||||
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
|
||||
|
|
|
|||
|
|
@ -33,19 +33,19 @@
|
|||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listWidget_3"/>
|
||||
<widget class="QListWidget" name="listWidget" />
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_21">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pBtn_Add_File_3">
|
||||
<widget class="QPushButton" name="pBtn_Add_File">
|
||||
<property name="text">
|
||||
<string>添加</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pBtn_Delete_File_3">
|
||||
<widget class="QPushButton" name="pBtn_Delete_File">
|
||||
<property name="text">
|
||||
<string>删除</string>
|
||||
</property>
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_name_3"/>
|
||||
<widget class="QLineEdit" name="lineEdit_name" />
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
|
|
@ -132,7 +132,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox_channel_3"/>
|
||||
<widget class="QComboBox" name="comboBox_channel" />
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
|
@ -155,14 +155,14 @@
|
|||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_18">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pBtn_Save_3">
|
||||
<widget class="QPushButton" name="pBtn_Save">
|
||||
<property name="text">
|
||||
<string>保存</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pBtn_SaveAs_3">
|
||||
<widget class="QPushButton" name="pBtn_SaveAs">
|
||||
<property name="text">
|
||||
<string>另存为</string>
|
||||
</property>
|
||||
|
|
@ -188,7 +188,7 @@
|
|||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_23">
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox_fit_type_4">
|
||||
<widget class="QComboBox" name="comboBox_fit_type">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
|
|
@ -216,7 +216,7 @@
|
|||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_19">
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox_fit_type_3">
|
||||
<widget class="QComboBox" name="comboBox_fit_type_1">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
|
|
@ -254,14 +254,14 @@
|
|||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_20">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pBtn_Add_3">
|
||||
<widget class="QPushButton" name="pBtn_Add">
|
||||
<property name="text">
|
||||
<string>新增</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pBtn_Delete_3">
|
||||
<widget class="QPushButton" name="pBtn_Delete">
|
||||
<property name="text">
|
||||
<string>删除</string>
|
||||
</property>
|
||||
|
|
@ -287,7 +287,7 @@
|
|||
<item>
|
||||
<widget class="QWidget" name="widget_3" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
<string notr="true" />
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_17">
|
||||
<property name="spacing">
|
||||
|
|
@ -306,7 +306,7 @@
|
|||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="layout_fittingCurve_3">
|
||||
<layout class="QVBoxLayout" name="layout_fittingCurve">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
|
@ -318,7 +318,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tablew_scale_data_3">
|
||||
<widget class="QTableWidget" name="table_scale_data">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
|
|
@ -330,7 +330,37 @@
|
|||
</attribute>
|
||||
<column>
|
||||
<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>
|
||||
</column>
|
||||
<column>
|
||||
|
|
@ -345,7 +375,7 @@
|
|||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>误差</string>
|
||||
<string>误差(%)</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
|
|
@ -359,6 +389,6 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<resources />
|
||||
<connections />
|
||||
</ui>
|
||||
|
|
|
|||
|
|
@ -406,6 +406,7 @@ void EnergyCountPeakFitView::onActionCurveShowSetting()
|
|||
|
||||
void EnergyCountPeakFitView::onActionPlotConfigure()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void EnergyCountPeakFitView::clearAllSelectionRects()
|
||||
|
|
|
|||
|
|
@ -15,6 +15,13 @@
|
|||
#include <QCheckBox>
|
||||
#include <QwtScaleDiv>
|
||||
#include <QPen>
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QColorDialog>
|
||||
#include <QMap>
|
||||
//能量计数谱
|
||||
EnergyCountPlotView::EnergyCountPlotView(QWidget *parent)
|
||||
: MeasureAnalysisView { parent }
|
||||
|
|
@ -285,7 +292,166 @@ void EnergyCountPlotView::onActionCurveShowSetting()
|
|||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include "ParticleCountPlotView.h"
|
||||
#include "reportwidget.h"
|
||||
#include "RegionOfInterest.h"
|
||||
#include "EfficiencyScale.h"
|
||||
#include <array>
|
||||
#include "csv.h"
|
||||
#include <fstream>
|
||||
|
|
@ -159,6 +160,7 @@ MainWindow::~MainWindow()
|
|||
|
||||
void MainWindow::onMeasureStarted(bool success, const QString& message)
|
||||
{
|
||||
LOG_INFO(message);
|
||||
// Q_UNUSED(message);
|
||||
// if (success) {
|
||||
// ui->action_start_measure->setEnabled(false);
|
||||
|
|
@ -174,6 +176,7 @@ void MainWindow::onMeasureStopped(bool success, const QString& message)
|
|||
// ui->action_stop_measure->setEnabled(false);
|
||||
m_AddressCountTimer->stop();
|
||||
m_EnergyCountTimer->stop();
|
||||
LOG_INFO(message);
|
||||
// // 更新树节点状态:把"测量中"改回"已停止"
|
||||
// if (!nodeMap.isEmpty()) {
|
||||
// MeasureAnalysisProjectModel* models = ProjectList::Instance()->GetCurrentProjectModel();
|
||||
|
|
@ -189,7 +192,8 @@ void MainWindow::onMeasureStopped(bool success, const QString& message)
|
|||
|
||||
void MainWindow::onDeviceStatusMessage(const QString& msg)
|
||||
{
|
||||
ShowStatusBarMsg(msg);
|
||||
// ShowStatusBarMsg(msg);
|
||||
LOG_INFO(msg);
|
||||
}
|
||||
|
||||
void MainWindow::initMainWindow()
|
||||
|
|
@ -540,7 +544,7 @@ void MainWindow::onUpdateParticleDataTreeStatus()
|
|||
nodeMap = project_node_items[models->GetProjectName()];
|
||||
QStandardItem *nodeItem = nodeMap[models->GetProjectName()];
|
||||
ProjectList::Instance()->SetNodeStatus(nodeItem,"测量中",true);
|
||||
|
||||
LOG_INFO("测量中");
|
||||
QString dir = ProjectList::Instance()->GetCurrentProjectModel()->GetProjectDir();
|
||||
QString csvPath = QStringLiteral(u"%1/%2").arg(dir).arg("粒子数据.csv");
|
||||
models->SetAllChannelParticleDataFilename(csvPath);
|
||||
|
|
@ -888,7 +892,6 @@ void MainWindow::on_action_analysisReport_triggered()
|
|||
widget->show();
|
||||
}
|
||||
|
||||
#include "EfficiencyScale.h"
|
||||
void MainWindow::on_action_efficiency_scale_mrg_triggered()
|
||||
{
|
||||
// QDialog energy_scale_mrg_dlg;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,17 @@
|
|||
#include <QMessageBox>
|
||||
#include <QHeaderView>
|
||||
#include "AddNuclideDialog.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QColorDialog>
|
||||
#include <QMap>
|
||||
#include <QwtLegend>
|
||||
|
||||
//核素分析
|
||||
// 更新选择框显示
|
||||
static void updateSelectionMarkers(QwtPlot* plot,
|
||||
QwtPlotMarker*& leftMarker,
|
||||
|
|
@ -275,7 +286,133 @@ void NuclideAnalysisView::loadDataFromFile(const QString& data_name, const QStri
|
|||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -28,6 +28,14 @@
|
|||
#include "BatchEnergyScaleDialog.h"
|
||||
#include "FindPeaksResultDialog.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QColorDialog>
|
||||
#include <QMap>
|
||||
//道址计数谱
|
||||
ParticleCountPlotView::ParticleCountPlotView(QWidget* parent)
|
||||
: MeasureAnalysisView { parent }
|
||||
{
|
||||
|
|
@ -120,7 +128,7 @@ void ParticleCountPlotView::updateView(const QMap<QString, QVariant>& data_files
|
|||
// 清除现有所有曲线
|
||||
for (QwtPlotCurve* curve : this->_plot->GetCurveList()) {
|
||||
curve->detach();
|
||||
delete curve;
|
||||
// delete curve;
|
||||
}
|
||||
_plot->clearCurve();
|
||||
|
||||
|
|
@ -230,33 +238,74 @@ void ParticleCountPlotView::setupEnergyScaleDlg()
|
|||
|
||||
void ParticleCountPlotView::loadDataFromFile(const QString& data_name, const QString& filename)
|
||||
{
|
||||
std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
|
||||
std::string count_str = QString(QStringLiteral(u"计数")).toStdString();
|
||||
// std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
|
||||
// std::string count_str = QString(QStringLiteral(u"计数")).toStdString();
|
||||
|
||||
io::CSVReader<
|
||||
2,
|
||||
io::trim_chars<' ', '\t'>,
|
||||
io::double_quote_escape<',', '"'>,
|
||||
io::throw_on_overflow,
|
||||
io::empty_line_comment>
|
||||
reader(QStrToSysPath(filename));
|
||||
reader.read_header(io::ignore_extra_column, address_str, count_str);
|
||||
// io::CSVReader<
|
||||
// 2,
|
||||
// io::trim_chars<' ', '\t'>,
|
||||
// io::double_quote_escape<',', '"'>,
|
||||
// io::throw_on_overflow,
|
||||
// io::empty_line_comment
|
||||
// >
|
||||
// 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;
|
||||
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;
|
||||
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine();
|
||||
QStringList fields = line.split(',');
|
||||
if (fields.size() >= 2) {
|
||||
address = fields[0].trimmed().toInt();
|
||||
particle_count = fields[1].trimmed().toULongLong();
|
||||
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::yLeft, 0.0f, _y_max);
|
||||
|
||||
// 绘制曲线
|
||||
QwtPlotCurve* curve = new QwtPlotCurve(data_name);
|
||||
curve->setSamples(x, y);
|
||||
_plot->AddCurve(curve);
|
||||
|
|
@ -535,5 +584,164 @@ void ParticleCountPlotView::onActionEnergyScale()
|
|||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,16 @@
|
|||
#include <QMenu>
|
||||
#include <QAction>
|
||||
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QColorDialog>
|
||||
#include <QMap>
|
||||
#include <QwtLegend>
|
||||
//粒子入射时间分析
|
||||
ParticleInjectTimeAnalysisView::ParticleInjectTimeAnalysisView(QWidget *parent) :
|
||||
MeasureAnalysisView(parent)
|
||||
{
|
||||
|
|
@ -153,7 +163,133 @@ void ParticleInjectTimeAnalysisView::updateData(bool b_init_update)
|
|||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ void ParticleTimeDifferenceView::loadDataFromFile()
|
|||
|
||||
void ParticleTimeDifferenceView::onActionPlotConfigure()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ParticleTimeDifferenceView::showEvent(QShowEvent *e)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user