EnergySpectrumAnalyer/src/EnergyCountPlotView/EnergyCountPlotView.cpp

480 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "EnergyCountPlotView.h"
#include "CustomQwtPlot.h"
#include "csv.h"
#include <GlobalDefine.h>
#include <QHBoxLayout>
#include <QMenu>
#include <QwtLegend>
#include <QwtPlotCanvas>
#include <QwtPlotCurve>
#include <QwtPlotMarker>
#include <QwtScaleMap>
#include <QwtText>
#include <QDialog>
#include <QPushButton>
#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 }
{
this->setViewType(PlotFrame);
QHBoxLayout* layout = new QHBoxLayout(this);
this->_plot = new CustomQwtPlot(this);
_roi = new RegionOfInterest();
connect(_roi, &RegionOfInterest::roiSelected,this, &EnergyCountPlotView::slot_roi_data);
layout->addWidget(this->_plot);
setupPlot();
this->_menu = new QMenu(this);
setupMenu();
}
EnergyCountPlotView::~EnergyCountPlotView()
{
LOG_DEBUG(QStringLiteral(u"%1析构.").arg(this->GetViewName()));
}
void EnergyCountPlotView::InitViewWorkspace(const QString &project_name)
{
Q_UNUSED(project_name);
}
void EnergyCountPlotView::SetAnalyzeDataFilename(const QMap<QString, QVariant> &data_files_set)
{
QStringList ch_count_data_name = data_files_set.keys();
std::sort(ch_count_data_name.begin(), ch_count_data_name.end(), [](const QString& a, const QString& b) {
int num_a = ExtractNumberFromString(a);
int num_b = ExtractNumberFromString(b);
return num_a < num_b;
});
for (const QString& ch_count_data_name : ch_count_data_name) {
if (ch_count_data_name.contains(ch_count_data_name)) {
const QString ch_count_data_filename = data_files_set[ch_count_data_name].toString();
QFileInfo fileInfo(ch_count_data_filename);
if (!fileInfo.exists() || fileInfo.size() == 0) {
continue; // 文件不存在或为空,跳过
}
loadDataFromFile(ch_count_data_name, ch_count_data_filename);
}
}
}
void EnergyCountPlotView::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"能量(KeV)");
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);
// set axis auto scale
_plot->setAxisAutoScale(QwtPlot::xBottom, true);
_plot->setAxisAutoScale(QwtPlot::yLeft, true);
_plot->enableAxis(QwtPlot::xBottom);
_plot->enableAxis(QwtPlot::yLeft);
// 设置QWT图例
QwtLegend* legend = new QwtLegend();
legend->setDefaultItemMode(QwtLegendData::ReadOnly);
_plot->insertLegend(legend, QwtPlot::RightLegend);
_plot->SetAxisDragScale(QwtPlot::xBottom, true);
_plot->SetAxisDragScale(QwtPlot::yLeft, true);
_data_selector = new CustomQwtPlotXaxisSelector(_plot->canvas());
_data_selector->setEnabled(false);
}
void EnergyCountPlotView::setupMenu()
{
this->setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &EnergyCountPlotView::customContextMenuRequested, [this](const QPoint& pos) {
this->_menu->exec(this->mapToGlobal(pos));
});
QAction* action_plot_reset = this->_menu->addAction(QStringLiteral(u"还原"));
action_plot_reset->setObjectName("plot_reset");
connect(action_plot_reset, &QAction::triggered, [this]() {
this->_plot->ResetPlot();
});
this->_menu->addSeparator();
QAction* action_set_curve_show = this->_menu->addAction(QStringLiteral(u"选择曲线"));
action_set_curve_show->setObjectName("curve_show_setting");
connect(action_set_curve_show, &QAction::triggered, this, &EnergyCountPlotView::onActionCurveShowSetting);
QAction* action_plot_config = this->_menu->addAction(QStringLiteral(u"图表配置"));
action_plot_config->setObjectName("plot_config");
connect(action_plot_config, &QAction::triggered, this, &EnergyCountPlotView::onActionPlotConfigure);
QAction* action_roi = this->_menu->addAction(QStringLiteral(u"感兴趣区"));
action_roi->setObjectName("roi");
connect(action_roi,&QAction::triggered,this,&EnergyCountPlotView::onActionRoiTriggered);
}
void EnergyCountPlotView::loadDataFromFile(const QString &data_name, const QString &filename)
{
std::string address_str = QString(QStringLiteral(u"能量(KeV)")).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);
double energy;
unsigned long long energy_count;
QVector<double> x, y;
while (reader.read_row(energy, energy_count)) {
x.push_back(energy);
y.push_back(energy_count);
if ( _x_max < energy )
_x_max = energy;
if ( _y_max < energy_count )
_y_max = energy_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);
}
QwtPlotMarker *EnergyCountPlotView::createVLine(double x, Qt::GlobalColor color, double lineWidth)
{
QwtPlotMarker* marker = new QwtPlotMarker();
marker->setXValue(x);
marker->setLineStyle(QwtPlotMarker::VLine);
QPen pen(color);
pen.setWidthF(lineWidth);
marker->setLinePen(pen);
marker->attach(_plot);
return marker;
}
void EnergyCountPlotView::clearAllRoiMarkers()
{
for (auto marker : _roiMarkers)
{
marker->detach(); // 从绘图面板解绑
delete marker; // 销毁对象
}
_roiMarkers.clear();
_plot->replot(); // 刷新画布清除线条
}
void EnergyCountPlotView::updateView(const QMap<QString, QVariant>& data_files_set)
{
// 清除现有所有曲线
for (QwtPlotCurve* curve : this->_plot->GetCurveList()) {
curve->detach();
delete curve;
}
_plot->clearCurve();
// 重新加载数据
if (!data_files_set.isEmpty()) {
SetAnalyzeDataFilename(data_files_set);
}
// 清除标记并重绘
this->_plot->CleanMarkers();
this->_plot->CleanZoneItems();
this->_plot->ResetPlot();
this->_plot->replot();
}
void EnergyCountPlotView::onActionCurveShowSetting()
{
if (!_curve_show_setting_dlg) {
_curve_show_setting_dlg = new QDialog(this, Qt::Dialog | Qt::WindowCloseButtonHint);
_curve_show_setting_dlg->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
_curve_show_setting_dlg->setSizeGripEnabled(false);
_curve_show_setting_dlg->setWindowTitle(QString(QStringLiteral(u"选择%1曲线显示").arg(this->_plot->title().text())));
_curve_show_setting_dlg->setWindowModality(Qt::WindowModal);
_curve_show_setting_dlg->setModal(false);
QVBoxLayout* layout = new QVBoxLayout(_curve_show_setting_dlg);
const QString& all_ch_text = QStringLiteral(u"全通道");
QStringList list_ch_names;
for (QwtPlotCurve* curve : this->_plot->GetCurveList()) {
const QString& every_ch_text = curve->title().text();
if ( all_ch_text != every_ch_text ) {
list_ch_names.append(every_ch_text);
}
}
std::sort(list_ch_names.begin(), list_ch_names.end(), [](const QString& a, const QString& b) {
int num_a = ExtractNumberFromString(a);
int num_b = ExtractNumberFromString(b);
return num_a < num_b;
});
// 自动计算多列排布
int num_columns = std::sqrt(this->_plot->GetCurveList().size());
if (num_columns == 0)
num_columns = 1;
QVBoxLayout* checkbox_layout = new QVBoxLayout();
QHBoxLayout* checkbox_column_layout = new QHBoxLayout();
QMap<QwtPlotCurve*, QCheckBox*> curve_checkbox_map;
auto addCurveToLayout = [&](const QString& curve_name){
QwtPlotCurve* curve = this->_plot->GetCurve(curve_name);
QCheckBox* check_box = new QCheckBox(curve->title().text());
check_box->setChecked(curve->isVisible());
checkbox_column_layout->addWidget(check_box);
connect(check_box, &QCheckBox::stateChanged, [curve](int state) {
curve->setVisible(state == Qt::Checked);
curve->plot()->replot();
});
curve_checkbox_map[curve] = check_box;
if (checkbox_column_layout->count() >= num_columns) {
checkbox_layout->addLayout(checkbox_column_layout);
checkbox_column_layout = new QHBoxLayout();
}
};
addCurveToLayout(all_ch_text);
for (const QString& ch_name : list_ch_names) {
addCurveToLayout(ch_name);
}
if (checkbox_column_layout->count() < num_columns) {
checkbox_column_layout->addStretch();
}
checkbox_layout->addLayout(checkbox_column_layout);
// 全选和反选
auto curveCheckboxUpdate = [this, curve_checkbox_map]() {
for (QwtPlotCurve* curve : this->_plot->GetCurveList()) {
curve_checkbox_map[curve]->setChecked(curve->isVisible());
}
};
QHBoxLayout* button_layout = new QHBoxLayout();
QPushButton* btn_all_select = new QPushButton(QString(QStringLiteral(u"全选")));
connect(btn_all_select, &QPushButton::clicked, [this, curveCheckboxUpdate]() {
for (QwtPlotCurve* curve : this->_plot->GetCurveList()) {
curve->setVisible(true);
}
curveCheckboxUpdate();
this->_plot->replot();
});
button_layout->addWidget(btn_all_select);
QPushButton* btn_reserve_select = new QPushButton(QString(QStringLiteral(u"反选")));
connect(btn_reserve_select, &QPushButton::clicked, [this, curveCheckboxUpdate]() {
for (QwtPlotCurve* curve : this->_plot->GetCurveList()) {
curve->setVisible(!curve->isVisible());
}
curveCheckboxUpdate();
this->_plot->replot();
});
button_layout->addWidget(btn_reserve_select);
layout->addLayout(button_layout);
layout->addLayout(checkbox_layout);
}
_curve_show_setting_dlg->show();
}
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()
{
clearAllRoiMarkers();
_roi->show();
}
void EnergyCountPlotView::slot_roi_data(ROIItem &item)
{
clearAllRoiMarkers();
// 第一组起始1、结束1 红色竖线,必须存在才插入,所以一定绘制
_roiMarkers.append(createVLine(item.s1, Qt::red, 1.2));
_roiMarkers.append(createVLine(item.e1, Qt::red, 1.2));
// 第二组起始2、结束2 有有效数值才绘制,无值跳过
bool secondGroupValid = !(qIsNaN(item.s2) || qIsNaN(item.e2) || (item.s2 == 0 && item.e2 == 0));
if (secondGroupValid)
{
_roiMarkers.append(createVLine(item.s2, Qt::blue, 1.2));
_roiMarkers.append(createVLine(item.e2, Qt::blue, 1.2));
}
_plot->replot();
}