#include "CountRateAnalysisView.h" #include "ui_CountRateAnalysisView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CustomQwtPlot.h" #include #include #include "csv.h" #include #include "BusyIndicator.h" #include CountRateAnalysisView::CountRateAnalysisView(QWidget *parent) : MeasureAnalysisView(parent), ui(new Ui::CountRateAnalysisView) { ui->setupUi(this); this->setViewType(PlotFrame); this->_menu = new QMenu(this); _busy_indicator = new BusyIndicatorWidget(this); InitUi(); setupMenu(); } CountRateAnalysisView::~CountRateAnalysisView() { delete ui; } void CountRateAnalysisView::InitViewWorkspace(const QString &project_name) { } void CountRateAnalysisView::SetAnalyzeDataFilename(const QMap &data_files_set) { if(!data_files_set.isEmpty()) { _busy_indicator->Start(); auto functionToRun = [this,data_files_set] { QVector data = getParticleInjectTimeData(data_files_set.first().toString()); QMetaObject::invokeMethod(this, [this, data]() { setData(data); _busy_indicator->Stop(); }, Qt::QueuedConnection); }; QThread* load_data_thread = QThread::create(functionToRun); load_data_thread->start(); } } void CountRateAnalysisView::showEvent(QShowEvent *e) { Q_UNUSED(e); if (_busy_indicator) { _busy_indicator->setGeometry(this->rect()); this->update(); } } void CountRateAnalysisView::setData(QVector data) { int energyStart = ui->label_energyStart->text().toInt(); int energyEnd = ui->label_energyEnd->text().toInt(); int nInv = 1; constexpr qint64 NS_PER_SECOND = 1000000000; // 纳秒转秒的基数 if (data.isEmpty()) return; // 添加空列表保护 QVector x; QVector y; const ParticleInjectTime& lostdt = data.last(); const qint64 totalNanoseconds = lostdt.dTime * 5; const int nAllS = static_cast(totalNanoseconds / NS_PER_SECOND) + 1; const int nVecSize = nAllS / nInv; QVector vec(nVecSize); // 默认初始化为0 double x_max = 0; double y_max = 0; for(auto info : data) { const qint64 ns = (info.dTime * 5) / NS_PER_SECOND; const int nidx = static_cast(ns / nInv); // 整数除法,速度快 if (nidx < nVecSize) { ++vec[nidx]; if ( y_max < vec[nidx] ) y_max = vec[nidx]; } } QVector vecXpt(vec.size()); for (int i = 0; i < vec.size(); ++i) { vecXpt[i] = static_cast(i); if ( x_max < vecXpt[i] ) x_max = vecXpt[i]; } plot->SetAxisInitRange(QwtPlot::xBottom, 0.0f, x_max); plot->SetAxisInitRange(QwtPlot::yLeft, 0.0f, y_max); // 创建曲线并设置数据 QwtPlotCurve *curve = new QwtPlotCurve(); curve->setSamples(vecXpt, vec); // 将曲线添加到 CustomQwtPlot 中(会自动分配颜色) plot->AddCurve(curve); // 设置坐标轴范围和标题 // plot->setAxisScale(QwtPlot::xBottom, 0, x.last()); // plot->setAxisScale(QwtPlot::yLeft,minValue , maxValue); // LOG_INFO(QStringLiteral(u"[%1]计数率分析数据读取完毕.").arg(this->GetProjectName()).arg(this->GetViewName())); // 刷新绘图 plot->replot(); } QVector CountRateAnalysisView::getParticleInjectTimeData(QString path) { QVector records; io::CSVReader<4> in(QStrToSysPath(path)); in.read_header(io::ignore_extra_column, "板卡号", "通道号", "能量(KeV)", "时间计数"); int board, channel; double energy, time_count; int lineNumber = 0; // 逐行读取 while (in.read_row(board, channel, energy, time_count)) { int detector = board + channel * 8; lineNumber++; if(detector >= 32) { continue; } ParticleInjectTime rec; rec.index = lineNumber; rec.dEnergy = energy; rec.dTime = time_count; records.append(rec); } return records; } void CountRateAnalysisView::InitUi() { plot = new CustomQwtPlot(); setupPlot(); ui->verticalLayout_2->addWidget(plot); } void CountRateAnalysisView::setupPlot() { plot->setCanvasBackground(Qt::white); QwtPlotCanvas* canvas = qobject_cast(plot->canvas()); canvas->setFrameStyle(QFrame::NoFrame); plot->setAxisTitle(QwtPlot::xBottom, QStringLiteral(u"时间")); plot->setAxisTitle(QwtPlot::yLeft, QStringLiteral(u"计数")); // 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); } void CountRateAnalysisView::setupMenu() { this->setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &CountRateAnalysisView::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_plot_config = this->_menu->addAction(QStringLiteral(u"图表配置")); action_plot_config->setObjectName("plot_config"); connect(action_plot_config, &QAction::triggered, this, &CountRateAnalysisView::onActionPlotConfigure); } void CountRateAnalysisView::onActionPlotConfigure() { QDialog dlg(this); dlg.setWindowTitle(QStringLiteral(u"图表配置")); dlg.setMinimumWidth(320); QVBoxLayout* layout = new QVBoxLayout(&dlg); // X轴标题 QHBoxLayout* xTitleLayout = new QHBoxLayout(); QLabel* xTitleLabel = new QLabel(QStringLiteral(u"X轴标题:"), &dlg); QLineEdit* xTitleEdit = new QLineEdit(plot->axisTitle(QwtPlot::xBottom).text(), &dlg); xTitleLayout->addWidget(xTitleLabel); xTitleLayout->addWidget(xTitleEdit); layout->addLayout(xTitleLayout); // Y轴标题 QHBoxLayout* yTitleLayout = new QHBoxLayout(); QLabel* yTitleLabel = new QLabel(QStringLiteral(u"Y轴标题:"), &dlg); QLineEdit* yTitleEdit = new QLineEdit(plot->axisTitle(QwtPlot::yLeft).text(), &dlg); yTitleLayout->addWidget(yTitleLabel); yTitleLayout->addWidget(yTitleEdit); layout->addLayout(yTitleLayout); layout->addSpacing(8); // 显示网格 QCheckBox* gridCheck = new QCheckBox(QStringLiteral(u"显示网格线"), &dlg); gridCheck->setChecked(plot->axisEnabled(QwtPlot::xBottom) && plot->axisEnabled(QwtPlot::yLeft)); layout->addWidget(gridCheck); // 显示图例 QCheckBox* legendCheck = new QCheckBox(QStringLiteral(u"显示图例"), &dlg); legendCheck->setChecked(plot->legend() != nullptr); layout->addWidget(legendCheck); layout->addSpacing(8); // 曲线颜色 QLabel* curveColorLabel = new QLabel(QStringLiteral(u"曲线颜色:"), &dlg); layout->addWidget(curveColorLabel); // 获取所有曲线 QList curveList; QMap curveColorMap; const QwtPlotItemList& items = plot->itemList(); for (QwtPlotItem* item : items) { if (item->rtti() == QwtPlotItem::Rtti_PlotCurve) { QwtPlotCurve* curve = static_cast(item); curveList.append(curve); curveColorMap.insert(curve, curve->pen().color()); } } // 为每条曲线创建颜色按钮 QList 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(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(); } }