From 4291065053cb37ffa21f44d16d9553c6292111e3 Mon Sep 17 00:00:00 2001 From: anxinglong <2910824064@qq.com> Date: Mon, 1 Jun 2026 17:45:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=89=E7=BB=B4=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=88=9D=E7=BA=A7=E7=B2=92=E5=AD=90=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=E5=92=8C=E6=AC=A1=E7=BA=A7=E7=B2=92=E5=AD=90=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=8C=E4=BF=AE=E5=A4=8D=E8=83=BD=E9=87=8F?= =?UTF-8?q?=E5=88=BB=E5=BA=A6=E5=8A=A0=E8=BD=BD=E6=97=B6=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=E8=BD=AF=E4=BB=B6=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E6=A0=8F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/EnergyScaleForm.cpp | 3 +- .../DeviceParamsTableForm.cpp | 14 +- src/NewMeasureAnalysisDlg.cpp | 46 +- src/NewMeasureAnalysisDlg.h | 1 + src/NewMeasureAnalysisDlg.ui | 4 +- src/RegionOfInterest/RegionOfInterest.cpp | 14 + src/RegionOfInterest/RegionOfInterest.h | 22 + src/RegionOfInterest/RegionOfInterest.ui | 19 + .../ConformityAnalysis.cpp | 456 ++++++++++++++---- .../ConformityAnalysis.h | 25 +- .../DetectorStatusSummary.cpp | 2 - .../ThreeDDisplay.cpp | 2 - src/main.cpp | 2 +- src/src.pro | 4 + 14 files changed, 501 insertions(+), 113 deletions(-) create mode 100644 src/RegionOfInterest/RegionOfInterest.cpp create mode 100644 src/RegionOfInterest/RegionOfInterest.h create mode 100644 src/RegionOfInterest/RegionOfInterest.ui diff --git a/src/EnergyScaleForm.cpp b/src/EnergyScaleForm.cpp index a280bdf..362163f 100644 --- a/src/EnergyScaleForm.cpp +++ b/src/EnergyScaleForm.cpp @@ -69,6 +69,7 @@ void EnergyScaleForm::SetAnalyzeDataFilename(const QMap &data QString baseName =QString("[%1]%2").arg(dir.dirName()).arg(fileInfo.baseName()); ui->lineEdit_name->setText(baseName); ui->lineEdit_name->setReadOnly(true); + ui->groupBox_2->hide(); ui->pBtn_SaveAs->setText(QStringLiteral(u"保存到系统")); m_currentFilePath = data_filename; QString errorMsg; @@ -970,7 +971,7 @@ void EnergyScaleForm::on_pBtn_SaveAs_clicked() systemEnble = false; // 选择保存路径 QString defaultDir = QDir(qApp->applicationDirPath()).filePath("configure/EnergyScale"); - QString targetFilePath = QFileDialog::getSaveFileName(this, + targetFilePath = QFileDialog::getSaveFileName(this, "另存为能量刻度文件", defaultDir, "JSON文件 (*.json);;所有文件 (*.*)"); diff --git a/src/MeasureDeviceParamsConfigView/DeviceParamsTableForm.cpp b/src/MeasureDeviceParamsConfigView/DeviceParamsTableForm.cpp index 1c3342b..390561a 100644 --- a/src/MeasureDeviceParamsConfigView/DeviceParamsTableForm.cpp +++ b/src/MeasureDeviceParamsConfigView/DeviceParamsTableForm.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "DeviceParamsSaveToSysDlg.h" static QStringList s_addr_count_list { "256", "512", "1024", "2048", "4096", "8192", "16384" }; @@ -386,12 +387,17 @@ void DeviceParamsTableForm::onCfgChannelSelectBtnClicked() int num_columns = std::sqrt(32); if (num_columns == 0) num_columns = 1; + + QList all_checkboxes; + QVBoxLayout* checkbox_layout = new QVBoxLayout(); QHBoxLayout* checkbox_column_layout = new QHBoxLayout(); for (int row = 0; row < 32; ++row) { QCheckBox* check_box = new QCheckBox(QStringLiteral(u"通道%1").arg(row + 1)); check_box->setChecked(!ui->params_cfg_table->isRowHidden(row)); checkbox_column_layout->addWidget(check_box); + all_checkboxes.append(check_box); + connect(check_box, &QCheckBox::stateChanged, [this, row](int state) { ui->params_cfg_table->setRowHidden(row, state == Qt::Unchecked); }); @@ -407,16 +413,18 @@ void DeviceParamsTableForm::onCfgChannelSelectBtnClicked() // 全选和反选 QHBoxLayout* button_layout = new QHBoxLayout(); QPushButton* btn_all_select = new QPushButton(QString(QStringLiteral(u"全选"))); - connect(btn_all_select, &QPushButton::clicked, [this]() { + connect(btn_all_select, &QPushButton::clicked, [this, all_checkboxes]() { for (int row = 0; row < 32; ++row) { - ui->params_cfg_table->setRowHidden(row, true); + ui->params_cfg_table->setRowHidden(row, false); + all_checkboxes[row]->setChecked(true); } }); button_layout->addWidget(btn_all_select); QPushButton* btn_reserve_select = new QPushButton(QString(QStringLiteral(u"反选"))); - connect(btn_reserve_select, &QPushButton::clicked, [this]() { + connect(btn_reserve_select, &QPushButton::clicked, [this, all_checkboxes]() { for (int row = 0; row < 32; ++row) { ui->params_cfg_table->setRowHidden(row, !ui->params_cfg_table->isRowHidden(row)); + all_checkboxes[row]->setChecked(!all_checkboxes[row]->isChecked()); } }); button_layout->addWidget(btn_reserve_select); diff --git a/src/NewMeasureAnalysisDlg.cpp b/src/NewMeasureAnalysisDlg.cpp index 53d0754..046c205 100644 --- a/src/NewMeasureAnalysisDlg.cpp +++ b/src/NewMeasureAnalysisDlg.cpp @@ -6,6 +6,9 @@ #include #include #include +#include +#include +#include #include "DataProcessWorkPool.h" #include "GlobalDefine.h" NewMeasureAnalysisDlg::NewMeasureAnalysisDlg(QWidget *parent) @@ -33,7 +36,9 @@ NewMeasureAnalysisDlg::~NewMeasureAnalysisDlg() void NewMeasureAnalysisDlg::initialization() { ui->progressBar->setVisible(false); - + ui->comboBox_measure_param->addItem(QStringLiteral(u"无")); + ui->comboBox_energy_scale->addItem(QStringLiteral(u"无")); + ui->comboBox_efficiency_scale->addItem(QStringLiteral(u"无")); QRegExp rx(R"(^[^\\/:*?"<>|]+$)"); QValidator *validator = new QRegExpValidator(rx, this); ui->lineEdit_name->setValidator(validator); @@ -173,7 +178,8 @@ void NewMeasureAnalysisDlg::newProject(const QString& particle_data_filename) QString project_energy_scale_filename = QDir(project_dir_path).filePath(QStringLiteral(u"能量刻度.json")); QFileInfo energy_scale_file_info(energy_scale_filename); if (energy_scale_file_info.exists()) { - QFile::copy(energy_scale_filename, project_energy_scale_filename); + readJsonAsSave(energy_scale_filename,project_energy_scale_filename); + // QFile::copy(energy_scale_filename, project_energy_scale_filename); } bool is_measure_complete = !particle_data_filename.isEmpty(); @@ -346,3 +352,39 @@ void NewMeasureAnalysisDlg::startParticleSortTask(const QString& data_file_path, separate_task->SetFinishedNotifier(this, "onNewProjectFromFileFinished", project_name); separate_task->StartTask(); } + +void NewMeasureAnalysisDlg::readJsonAsSave(QString sourceFile, QString targetFile) +{ + QFile file(sourceFile); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug() << "无法打开文件:" << sourceFile; + return ; + } + + QByteArray jsonData = file.readAll(); + file.close(); + + QJsonParseError parseError; + QJsonDocument doc = QJsonDocument::fromJson(jsonData, &parseError); + + if (parseError.error != QJsonParseError::NoError) { + return ; + } + + QJsonObject rootObj = doc.object(); + + if (rootObj.contains("Remark")) { + rootObj.remove("Remark"); + + } + + QJsonDocument newDoc(rootObj); + + QFile saveFile(targetFile); + if (saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + // 格式化输出,方便阅读 + saveFile.write(newDoc.toJson(QJsonDocument::Indented)); + saveFile.close(); + } +} diff --git a/src/NewMeasureAnalysisDlg.h b/src/NewMeasureAnalysisDlg.h index 6cdfee7..a94abdc 100644 --- a/src/NewMeasureAnalysisDlg.h +++ b/src/NewMeasureAnalysisDlg.h @@ -23,6 +23,7 @@ private: void startParticleSortTask(const QString& data_file_path, const QString& project_name, const QString& project_dir_path); + void readJsonAsSave(QString sourceFile,QString targetFile); private slots: void onNewProjectFromFileFinished(bool ok, const QString& project_name, const QVariant &data); void on_btn_ok_clicked(); diff --git a/src/NewMeasureAnalysisDlg.ui b/src/NewMeasureAnalysisDlg.ui index 41b378a..fcc20d7 100644 --- a/src/NewMeasureAnalysisDlg.ui +++ b/src/NewMeasureAnalysisDlg.ui @@ -6,8 +6,8 @@ 0 0 - 550 - 278 + 631 + 314 diff --git a/src/RegionOfInterest/RegionOfInterest.cpp b/src/RegionOfInterest/RegionOfInterest.cpp new file mode 100644 index 0000000..b17f847 --- /dev/null +++ b/src/RegionOfInterest/RegionOfInterest.cpp @@ -0,0 +1,14 @@ +#include "RegionOfInterest.h" +#include "ui_RegionOfInterest.h" + +RegionOfInterest::RegionOfInterest(QWidget *parent) + : QWidget(parent) + , ui(new Ui::RegionOfInterest) +{ + ui->setupUi(this); +} + +RegionOfInterest::~RegionOfInterest() +{ + delete ui; +} diff --git a/src/RegionOfInterest/RegionOfInterest.h b/src/RegionOfInterest/RegionOfInterest.h new file mode 100644 index 0000000..e6c827d --- /dev/null +++ b/src/RegionOfInterest/RegionOfInterest.h @@ -0,0 +1,22 @@ +#ifndef REGIONOFINTEREST_H +#define REGIONOFINTEREST_H + +#include + +namespace Ui { +class RegionOfInterest; +} + +class RegionOfInterest : public QWidget +{ + Q_OBJECT + +public: + explicit RegionOfInterest(QWidget *parent = nullptr); + ~RegionOfInterest(); + +private: + Ui::RegionOfInterest *ui; +}; + +#endif // REGIONOFINTEREST_H diff --git a/src/RegionOfInterest/RegionOfInterest.ui b/src/RegionOfInterest/RegionOfInterest.ui new file mode 100644 index 0000000..9e59fc0 --- /dev/null +++ b/src/RegionOfInterest/RegionOfInterest.ui @@ -0,0 +1,19 @@ + + + RegionOfInterest + + + + 0 + 0 + 711 + 505 + + + + Form + + + + + diff --git a/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.cpp b/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.cpp index edf96e9..fb3d28e 100644 --- a/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.cpp +++ b/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.cpp @@ -38,7 +38,6 @@ QJsonObject ConformityHistoryItem::toJson() const obj["secondaryEnergyStart"] = secondaryEnergyStart; obj["secondaryEnergyEnd"] = secondaryEnergyEnd; - // ✅ 修复致命BUG:不再无限递归嵌套 QJsonArray boardChannelJson; for (int board = 0; board < MAX_BOARD; ++board) { QJsonArray channelJson; @@ -55,6 +54,24 @@ QJsonObject ConformityHistoryItem::toJson() const } obj["firstParticleData"] = firstParticleJson; + QJsonArray matrixJson; + for (int pBoard = 0; pBoard < MAX_BOARD; ++pBoard) { + QJsonArray pChannelArray; + for (int pChannel = 0; pChannel < MAX_CHANNEL; ++pChannel) { + QJsonArray sBoardArray; + for (int sBoard = 0; sBoard < MAX_BOARD; ++sBoard) { + QJsonArray sChannelArray; + for (int sChannel = 0; sChannel < MAX_CHANNEL; ++sChannel) { + sChannelArray.append(coincidenceMatrix[pBoard][pChannel][sBoard][sChannel]); + } + sBoardArray.append(sChannelArray); + } + pChannelArray.append(sBoardArray); + } + matrixJson.append(pChannelArray); + } + obj["coincidenceMatrix"] = matrixJson; + return obj; } @@ -70,6 +87,7 @@ ConformityHistoryItem ConformityHistoryItem::fromJson(const QJsonObject& obj) item.secondaryEnergyStart = obj["secondaryEnergyStart"].toDouble(); item.secondaryEnergyEnd = obj["secondaryEnergyEnd"].toDouble(); + // 板卡通道数据 memset(item.boardChannelData, 0, sizeof(item.boardChannelData)); QJsonArray boardChannelJson = obj["boardChannelData"].toArray(); for (int board = 0; board < MAX_BOARD && board < boardChannelJson.size(); ++board) { @@ -79,12 +97,30 @@ ConformityHistoryItem ConformityHistoryItem::fromJson(const QJsonObject& obj) } } + // 初级粒子数据 QJsonObject firstParticleJson = obj["firstParticleData"].toObject(); for (const QString& key : firstParticleJson.keys()) { item.firstParticleData[key] = firstParticleJson[key].toInt(); } + // 新增:符合矩阵数据 + memset(item.coincidenceMatrix, 0, sizeof(item.coincidenceMatrix)); + QJsonArray matrixJson = obj["coincidenceMatrix"].toArray(); + for (int pBoard = 0; pBoard < MAX_BOARD && pBoard < matrixJson.size(); ++pBoard) { + QJsonArray pChannelArray = matrixJson[pBoard].toArray(); + for (int pChannel = 0; pChannel < MAX_CHANNEL && pChannel < pChannelArray.size(); ++pChannel) { + QJsonArray sBoardArray = pChannelArray[pChannel].toArray(); + for (int sBoard = 0; sBoard < MAX_BOARD && sBoard < sBoardArray.size(); ++sBoard) { + QJsonArray sChannelArray = sBoardArray[sBoard].toArray(); + for (int sChannel = 0; sChannel < MAX_CHANNEL && sChannel < sChannelArray.size(); ++sChannel) { + item.coincidenceMatrix[pBoard][pChannel][sBoard][sChannel] = sChannelArray[sChannel].toInt(); + } + } + } + } + item.surfaceData.clear(); + item.channelSurfaceData.clear(); return item; } @@ -99,7 +135,9 @@ ConformityCalculatedResult ConformityHistoryItem::toCalculatedResult() const result.secondaryEnergyStart = secondaryEnergyStart; result.secondaryEnergyEnd = secondaryEnergyEnd; memcpy(result.boardChannelData, boardChannelData, sizeof(boardChannelData)); + memcpy(result.coincidenceMatrix, coincidenceMatrix, sizeof(coincidenceMatrix)); result.firstParticleData = firstParticleData; + result.channelSurfaceData = channelSurfaceData; result.surfaceData = surfaceData; return result; } @@ -116,7 +154,9 @@ ConformityHistoryItem ConformityHistoryItem::fromCalculatedResult(const Conformi item.secondaryEnergyStart = result.secondaryEnergyStart; item.secondaryEnergyEnd = result.secondaryEnergyEnd; memcpy(item.boardChannelData, result.boardChannelData, sizeof(result.boardChannelData)); + memcpy(item.coincidenceMatrix, result.coincidenceMatrix, sizeof(result.coincidenceMatrix)); item.firstParticleData = result.firstParticleData; + item.channelSurfaceData = result.channelSurfaceData; item.surfaceData = result.surfaceData; return item; } @@ -171,12 +211,47 @@ void ConformityAnalysis::InitViewWorkspace(const QString &project_name) void ConformityAnalysis::SetAnalyzeDataFilename(const QMap &data_files_set) { - if (data_files_set.isEmpty()) return; + // if (data_files_set.isEmpty()) return; + // clearAllCachedData(); + // freeEventVector(_currentSpectrumData); + // m_conformFileMap.clear(); + + // for (const QString& key : data_files_set.keys()) { + // bool ok; + // int cnt = key.toInt(&ok); + // if (ok && cnt >=2 && cnt <=9) { + // m_conformFileMap[cnt] = data_files_set[key].toString(); + // } + // } + + // if (m_conformFileMap.isEmpty()) { + // qWarning() << "[ConformityAnalysis] 未找到任何有效的符合数数据文件"; + // return; + // } + + // if (m_conformFileMap.contains(2)) { + + // ConformityCalculatedResult result = streamParseAndCalculate(2, m_conformFileMap[2]); + + // QMutexLocker cacheLocker(&m_cacheMutex); + // m_calculatedCache[2] = result; + // cacheLocker.unlock(); + + // displayConformData(2); + + // QMetaObject::invokeMethod(this, [=]() { + // saveResultToHistory(result); + // }, Qt::QueuedConnection); + // } + // m_isParsing = true; + // parseAllConformDataInBackground(); + + if (data_files_set.isEmpty()) return; clearAllCachedData(); - freeEventVector(_currentSpectrumData); m_conformFileMap.clear(); + // 构建符合数->文件路径映射 for (const QString& key : data_files_set.keys()) { bool ok; int cnt = key.toInt(&ok); @@ -190,22 +265,108 @@ void ConformityAnalysis::SetAnalyzeDataFilename(const QMap &d return; } - if (m_conformFileMap.contains(2)) { + // 重新加载最新的历史数据 + loadHistoryFromFile(); - ConformityCalculatedResult result = streamParseAndCalculate(2, m_conformFileMap[2]); + // 检查每个文件是否需要重新解析 + QList needParseConformCounts; + QList canUseHistoryConformCounts; - QMutexLocker cacheLocker(&m_cacheMutex); - m_calculatedCache[2] = result; - cacheLocker.unlock(); + for (int conformCount : m_conformFileMap.keys()) { + QString fileName = m_conformFileMap[conformCount]; + QFileInfo fileInfo(fileName); - displayConformData(2); + // 查找对应的历史记录 + int historyIdx = findHistoryIndex(fileName, conformCount); - QMetaObject::invokeMethod(this, [=]() { - saveResultToHistory(result); - }, Qt::QueuedConnection); + if (historyIdx >= 0 && fileInfo.exists()) { + const ConformityHistoryItem& historyItem = _conformityHistoryList[historyIdx]; + // 比较文件最后修改时间和历史记录时间戳 + // 如果文件修改时间早于历史记录保存时间,说明文件未变,可以使用历史数据 + if (fileInfo.lastModified() <= historyItem.timestamp) { + // 直接将历史数据转换为预计算结果存入缓存 + ConformityCalculatedResult result = historyItem.toCalculatedResult(); + QMutexLocker cacheLocker(&m_cacheMutex); + m_calculatedCache[conformCount] = result; + canUseHistoryConformCounts.append(conformCount); + qDebug() << "[ConformityAnalysis] " << conformCount << "重符合数据使用历史缓存,跳过解析"; + continue; + } + } + + // 文件不存在或已修改,需要重新解析 + needParseConformCounts.append(conformCount); + qDebug() << "[ConformityAnalysis] " << conformCount << "重符合数据需要重新解析"; } - m_isParsing = true; - parseAllConformDataInBackground(); + + // 优先显示2重符合数据(如果有历史缓存) + if (canUseHistoryConformCounts.contains(2)) { + displayConformData(2); + } + + // 如果有需要解析的文件,启动后台解析 + if (!needParseConformCounts.isEmpty()) { + m_isParsing = true; + // 先解析2重符合(如果需要),保证界面快速显示 + if (needParseConformCounts.contains(2)) { + ConformityCalculatedResult result = streamParseAndCalculate(2, m_conformFileMap[2]); + QMutexLocker cacheLocker(&m_cacheMutex); + m_calculatedCache[2] = result; + cacheLocker.unlock(); + displayConformData(2); + QMetaObject::invokeMethod(this, [=]() { + saveResultToHistory(result); + }, Qt::QueuedConnection); + needParseConformCounts.removeOne(2); + } + + // 后台解析剩余的符合数 + parseRemainingConformDataInBackground(needParseConformCounts); + } else { + m_isParsing = false; + qDebug() << "[ConformityAnalysis] 所有数据均使用历史缓存,无需解析"; + } +} + +void ConformityAnalysis::parseRemainingConformDataInBackground(const QList& conformCounts) +{ + if (conformCounts.isEmpty()) { + m_isParsing = false; + return; + } + + QList sortedCounts = conformCounts; + std::sort(sortedCounts.begin(), sortedCounts.end()); + + auto parseTask = [this, sortedCounts]() { + for (int conformCount : sortedCounts) { + if (m_parseWatcher && m_parseWatcher->isCanceled()) + break; + QString fileName = m_conformFileMap[conformCount]; + ConformityCalculatedResult result = streamParseAndCalculate(conformCount, fileName); + QMutexLocker cacheLocker(&m_cacheMutex); + m_calculatedCache[conformCount] = result; + cacheLocker.unlock(); + if (conformCount == m_currentConformCount) { + QMetaObject::invokeMethod(this, "displayConformData", Qt::QueuedConnection, + Q_ARG(int, conformCount)); + } + QMetaObject::invokeMethod(this, [=]() { + saveResultToHistory(result); + }, Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "onSingleParseFinished", Qt::QueuedConnection, Q_ARG(int, conformCount)); + } + }; + + if (m_parseWatcher) { + m_parseWatcher->cancel(); + m_parseWatcher->waitForFinished(); + delete m_parseWatcher; + } + + m_parseWatcher = new QFutureWatcher(this); + connect(m_parseWatcher, &QFutureWatcher::finished, this, &ConformityAnalysis::onAllParseFinished); + m_parseWatcher->setFuture(QtConcurrent::run(parseTask)); } void ConformityAnalysis::parseAllConformDataInBackground() @@ -219,21 +380,15 @@ void ConformityAnalysis::parseAllConformDataInBackground() for (int conformCount : conformCounts) { if (m_parseWatcher && m_parseWatcher->isCanceled()) break; - QString fileName = m_conformFileMap[conformCount]; - - ConformityCalculatedResult result = streamParseAndCalculate(conformCount, fileName); - QMutexLocker cacheLocker(&m_cacheMutex); m_calculatedCache[conformCount] = result; cacheLocker.unlock(); - if (conformCount == m_currentConformCount) { QMetaObject::invokeMethod(this, "displayConformData", Qt::QueuedConnection, Q_ARG(int, conformCount)); } - QMetaObject::invokeMethod(this, [=]() { saveResultToHistory(result); }, Qt::QueuedConnection); @@ -252,12 +407,14 @@ ConformityCalculatedResult ConformityAnalysis::streamParseAndCalculate(int confo ConformityCalculatedResult result; result.conformCount = conformCount; result.dataFileName = fileName; - memset(result.boardChannelData, 0, sizeof(result.boardChannelData)); - for (int board = 1; board <= 8; ++board) { - for (int channel = 1; channel <= 4; ++channel) { + memset(result.coincidenceMatrix, 0, sizeof(result.coincidenceMatrix)); + + for (int board = 1; board <= MAX_BOARD; ++board) { + for (int channel = 1; channel <= MAX_CHANNEL; ++channel) { QString key = QStringLiteral(u"widget_%1_%2").arg(board).arg(channel); result.firstParticleData[key] = 0; + result.channelSurfaceData[key] = QVector(); } } @@ -266,13 +423,11 @@ ConformityCalculatedResult ConformityAnalysis::streamParseAndCalculate(int confo double minSecondVal = std::numeric_limits::max(); double maxSecondVal = 0.0; + QVector currentEventParticles; int currentEventId = -1; - float primaryEnergy = 0.0f; - float secondaryEnergySum = 0.0f; int eventCount = 0; if (!QFileInfo::exists(fileName)) { - qCritical() << "[ConformityAnalysis] CSV文件不存在:" << fileName; return result; } @@ -294,25 +449,49 @@ ConformityCalculatedResult ConformityAnalysis::streamParseAndCalculate(int confo particleCoincidenceEvent event; while (reader.read_row(event.eventId, event.board, event.channel, event.energy, event.timeCounter)) { eventCount++; - result.boardChannelData[event.board][event.channel]++; if (event.eventId != currentEventId) { - if (currentEventId != -1) { - SurfacePoint point; - point.primaryEnergy = primaryEnergy; - point.secondaryEnergySum = secondaryEnergySum; - point.count = 1; - result.surfaceData.append(point); + if (currentEventId != -1 && !currentEventParticles.isEmpty()) { + SurfacePoint globalPoint; + const auto& primaryParticle = currentEventParticles.first(); + globalPoint.primaryEnergy = static_cast(primaryParticle.energy); + globalPoint.secondaryEnergySum = 0.0f; + + for (int i = 1; i < currentEventParticles.size(); ++i) { + globalPoint.secondaryEnergySum += static_cast(currentEventParticles[i].energy); + } + globalPoint.count = 1; + result.surfaceData.append(globalPoint); + + int primaryBoard = primaryParticle.board; + int primaryChannel = primaryParticle.channel; + QString primaryKey = QStringLiteral(u"widget_%1_%2").arg(primaryBoard + 1).arg(primaryChannel + 1); + + SurfacePoint channelPoint; + channelPoint.primaryEnergy = static_cast(primaryParticle.energy); + channelPoint.secondaryEnergySum = 0.0f; + + for (int secondaryIdx = 1; secondaryIdx < currentEventParticles.size(); ++secondaryIdx) { + const auto& secondaryParticle = currentEventParticles[secondaryIdx]; + int secondaryBoard = secondaryParticle.board; + int secondaryChannel = secondaryParticle.channel; + + result.coincidenceMatrix[primaryBoard][primaryChannel][secondaryBoard][secondaryChannel]++; + channelPoint.secondaryEnergySum += static_cast(secondaryParticle.energy); + } + + channelPoint.count = 1; + result.channelSurfaceData[primaryKey].append(channelPoint); } currentEventId = event.eventId; - primaryEnergy = static_cast(event.energy); - secondaryEnergySum = 0.0f; + currentEventParticles.clear(); + currentEventParticles.append(event); int boardId = event.board + 1; int channelId = event.channel + 1; - if (boardId >= 1 && boardId <= 8 && channelId >= 1 && channelId <= 4) { + if (boardId >= 1 && boardId <= MAX_BOARD && channelId >= 1 && channelId <= MAX_CHANNEL) { QString key = QStringLiteral(u"widget_%1_%2").arg(boardId).arg(channelId); result.firstParticleData[key]++; } @@ -320,23 +499,48 @@ ConformityCalculatedResult ConformityAnalysis::streamParseAndCalculate(int confo if (event.energy < minFirstVal) minFirstVal = event.energy; if (event.energy > maxFirstVal) maxFirstVal = event.energy; } else { - secondaryEnergySum += static_cast(event.energy); - + currentEventParticles.append(event); if (event.energy < minSecondVal) minSecondVal = event.energy; if (event.energy > maxSecondVal) maxSecondVal = event.energy; } } - if (currentEventId != -1) { - SurfacePoint point; - point.primaryEnergy = primaryEnergy; - point.secondaryEnergySum = secondaryEnergySum; - point.count = 1; - result.surfaceData.append(point); + if (currentEventId != -1 && !currentEventParticles.isEmpty()) { + SurfacePoint globalPoint; + const auto& primaryParticle = currentEventParticles.first(); + globalPoint.primaryEnergy = static_cast(primaryParticle.energy); + globalPoint.secondaryEnergySum = 0.0f; + + for (int i = 1; i < currentEventParticles.size(); ++i) { + globalPoint.secondaryEnergySum += static_cast(currentEventParticles[i].energy); + } + globalPoint.count = 1; + result.surfaceData.append(globalPoint); + + int primaryBoard = primaryParticle.board; + int primaryChannel = primaryParticle.channel; + QString primaryKey = QStringLiteral(u"widget_%1_%2").arg(primaryBoard + 1).arg(primaryChannel + 1); + + SurfacePoint channelPoint; + channelPoint.primaryEnergy = static_cast(primaryParticle.energy); + channelPoint.secondaryEnergySum = 0.0f; + + for (int secondaryIdx = 1; secondaryIdx < currentEventParticles.size(); ++secondaryIdx) { + const auto& secondaryParticle = currentEventParticles[secondaryIdx]; + int secondaryBoard = secondaryParticle.board; + int secondaryChannel = secondaryParticle.channel; + result.coincidenceMatrix[primaryBoard][primaryChannel][secondaryBoard][secondaryChannel]++; + channelPoint.secondaryEnergySum += static_cast(secondaryParticle.energy); + } + + channelPoint.count = 1; + result.channelSurfaceData[primaryKey].append(channelPoint); } result.surfaceData.squeeze(); - + for (auto& vec : result.channelSurfaceData) { + vec.squeeze(); + } } catch (const std::exception& e) { qCritical() << "[ConformityAnalysis] CSV解析异常:" << e.what(); return result; @@ -347,19 +551,18 @@ ConformityCalculatedResult ConformityAnalysis::streamParseAndCalculate(int confo result.primaryEnergyEnd = maxFirstVal; result.secondaryEnergyStart = (minSecondVal == std::numeric_limits::max()) ? 0.0 : minSecondVal; result.secondaryEnergyEnd = maxSecondVal; + return result; } void ConformityAnalysis::saveResultToHistory(const ConformityCalculatedResult& result) { - QMutexLocker locker(&m_historyMutex); ConformityHistoryItem item = ConformityHistoryItem::fromCalculatedResult(result); - - // 1. 流式写入JSON(零大小限制) + // 保存JSON元数据 saveJsonStream(result.conformCount, item); - // 2. 二进制保存超大曲面数据 - saveSurfaceDataToBinary(result.conformCount, item.surfaceData); + // 保存二进制曲面数据(全局+所有通道) + saveSurfaceDataToBinary(result.conformCount, item.surfaceData, item.channelSurfaceData); } void ConformityAnalysis::saveJsonStream(int conformCount, const ConformityHistoryItem& item) @@ -367,13 +570,11 @@ void ConformityAnalysis::saveJsonStream(int conformCount, const ConformityHistor QDir dir(_workspace); dir.mkpath("."); QString path = dir.filePath(QString("conformity_%1.json").arg(conformCount)); - QFile file(path); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { qCritical() << "无法打开文件写入:" << file.errorString(); return; } - QTextStream stream(&file); stream.setCodec("UTF-8"); // 强制浮点数用固定小数格式,避免科学计数法 @@ -406,7 +607,6 @@ void ConformityAnalysis::saveJsonStream(int conformCount, const ConformityHistor }; stream << "{" << '\n'; - stream << " \"timestamp\": \"" << escapeJson(item.timestamp.toString(Qt::ISODate)) << "\"," << '\n'; stream << " \"dataFileName\": \"" << escapeJson(item.dataFileName) << "\"," << '\n'; stream << " \"conformCount\": " << item.conformCount << "," << '\n'; @@ -416,6 +616,7 @@ void ConformityAnalysis::saveJsonStream(int conformCount, const ConformityHistor stream << " \"secondaryEnergyStart\": " << item.secondaryEnergyStart << "," << '\n'; stream << " \"secondaryEnergyEnd\": " << item.secondaryEnergyEnd << "," << '\n'; + // 板卡通道数据 stream << " \"boardChannelData\": [" << '\n'; for (int board = 0; board < MAX_BOARD; ++board) { stream << " ["; @@ -429,6 +630,7 @@ void ConformityAnalysis::saveJsonStream(int conformCount, const ConformityHistor } stream << " ]," << '\n'; + // 初级粒子数据 stream << " \"firstParticleData\": {" << '\n'; QList keys = item.firstParticleData.keys(); for (int i = 0; i < keys.size(); ++i) { @@ -436,26 +638,55 @@ void ConformityAnalysis::saveJsonStream(int conformCount, const ConformityHistor if (i < keys.size() - 1) stream << ","; stream << '\n'; } - stream << " }" << '\n'; + stream << " }," << '\n'; + + // 符合矩阵数据 + stream << " \"coincidenceMatrix\": [" << '\n'; + for (int pBoard = 0; pBoard < MAX_BOARD; ++pBoard) { + stream << " [" << '\n'; + for (int pChannel = 0; pChannel < MAX_CHANNEL; ++pChannel) { + stream << " ["; + for (int sBoard = 0; sBoard < MAX_BOARD; ++sBoard) { + stream << "["; + for (int sChannel = 0; sChannel < MAX_CHANNEL; ++sChannel) { + stream << item.coincidenceMatrix[pBoard][pChannel][sBoard][sChannel]; + if (sChannel < MAX_CHANNEL - 1) stream << ", "; + } + stream << "]"; + if (sBoard < MAX_BOARD - 1) stream << ", "; + } + stream << "]"; + if (pChannel < MAX_CHANNEL - 1) stream << ","; + stream << '\n'; + } + stream << " ]"; + if (pBoard < MAX_BOARD - 1) stream << ","; + stream << '\n'; + } + stream << " ]" << '\n'; stream << "}" << '\n'; - file.close(); } -void ConformityAnalysis::saveSurfaceDataToBinary(int conformCount, const QVector& data) + +void ConformityAnalysis::saveSurfaceDataToBinary(int conformCount, const QVector &globalData, const QMap > &channelData) { QDir dir(_workspace); QString path = dir.filePath(QString("surface_%1.bin").arg(conformCount)); - QFile file(path); if (!file.open(QIODevice::WriteOnly)) { qCritical() << "无法打开二进制文件写入:" << file.errorString(); return; } - QDataStream stream(&file); - stream << data; + // 先保存全局曲面数据 + stream << globalData; + // 再保存通道曲面数据数量和内容 + stream << channelData.size(); + for (auto it = channelData.begin(); it != channelData.end(); ++it) { + stream << it.key() << it.value(); + } file.close(); } @@ -465,12 +696,9 @@ void ConformityAnalysis::loadHistoryFromFile() for (int c = 2; c <= 9; ++c) { QString jsonPath = QDir(_workspace).filePath(QString("conformity_%1.json").arg(c)); QString binPath = QDir(_workspace).filePath(QString("surface_%1.bin").arg(c)); - if (!QFile::exists(jsonPath)) { - // qDebug() << "[ConformityAnalysis] " << c << "重符合历史文件不存在:" << jsonPath; continue; } - QFile f(jsonPath); if (!f.open(QIODevice::ReadOnly)) { qWarning() << "[ConformityAnalysis] 无法打开JSON文件:" << jsonPath; @@ -478,7 +706,6 @@ void ConformityAnalysis::loadHistoryFromFile() } QJsonDocument doc = QJsonDocument::fromJson(f.readAll()); f.close(); - ConformityHistoryItem item = ConformityHistoryItem::fromJson(doc.object()); if (QFile::exists(binPath)) { @@ -486,18 +713,23 @@ void ConformityAnalysis::loadHistoryFromFile() if (binFile.open(QIODevice::ReadOnly)) { QDataStream stream(&binFile); stream >> item.surfaceData; + int channelCount; + stream >> channelCount; + for (int i = 0; i < channelCount; ++i) { + QString key; + QVector data; + stream >> key >> data; + item.channelSurfaceData[key] = data; + } binFile.close(); } } - _conformityHistoryList.append(item); } } -// ==================== 单个符合数解析完成 ==================== void ConformityAnalysis::onSingleParseFinished(int conformCount) { - qDebug() << "[ConformityAnalysis] " << conformCount << "重符合数据处理完成"; } void ConformityAnalysis::onAllParseFinished() @@ -535,7 +767,6 @@ void ConformityAnalysis::displayConformData(int conformCount) setAllBoardData(); setThreeUiData(); ui->widget_3D->setSurfaceData(m_surfaceData); - } void ConformityAnalysis::slot_ConformCountChanged(int index) @@ -552,7 +783,6 @@ QVector ConformityAnalysis::readCsv(const QString& fi QVector dataList; if (!QFileInfo::exists(fileName)) { - qWarning() << "[ConformityAnalysis] 文件不存在:" << fileName; return dataList; } @@ -590,7 +820,6 @@ QVector ConformityAnalysis::readCsv(const QString& fi return dataList; } -// 释放事件数组内存 void ConformityAnalysis::freeEventVector(QVector& vec) { qDeleteAll(vec); @@ -752,46 +981,65 @@ void ConformityAnalysis::slot_ClickedBoard(int board,int channel) if (!m_calculatedCache.contains(m_currentConformCount)) { return; } - QString fileName = m_calculatedCache[m_currentConformCount].dataFileName; + const ConformityCalculatedResult& result = m_calculatedCache[m_currentConformCount]; locker.unlock(); - if (_currentSpectrumData.isEmpty()) { - _currentSpectrumData = readCsv(fileName); + QString primaryKey = QStringLiteral(u"widget_%1_%2").arg(board).arg(channel); + if (!result.channelSurfaceData.contains(primaryKey)) { + qWarning() << "[ConformityAnalysis] 通道数据不存在:" << primaryKey; + return; } - QVector data = handleBasicSubordinate(board, channel, _currentSpectrumData); - if(data.size() <= 0) return; + const QVector& channelSurface = result.channelSurfaceData[primaryKey]; + ui->widget_3D->setSurfaceData(channelSurface); - QVector tempSurface = generateSurfaceData(data); - ui->widget_3D->setSurfaceData(tempSurface); - - m_iComply = data.size(); - - double localFirstStart, localFirstEnd; - double localSecondStart, localSecondEnd; - calculateFirstSecondRange(data, localFirstStart, localFirstEnd, localSecondStart, localSecondEnd); - - m_dFirstStart = localFirstStart; + double localFirstStart = std::numeric_limits::max(); + double localFirstEnd = 0.0; + double localSecondStart = std::numeric_limits::max(); + double localSecondEnd = 0.0; + for (const auto& point : channelSurface) { + if (point.primaryEnergy < localFirstStart) localFirstStart = point.primaryEnergy; + if (point.primaryEnergy > localFirstEnd) localFirstEnd = point.primaryEnergy; + if (point.secondaryEnergySum < localSecondStart) localSecondStart = point.secondaryEnergySum; + if (point.secondaryEnergySum > localSecondEnd) localSecondEnd = point.secondaryEnergySum; + } + m_dFirstStart = (localFirstStart == std::numeric_limits::max()) ? 0.0 : localFirstStart; m_dFirstEnd = localFirstEnd; - m_dSecondStart = localSecondStart; + m_dSecondStart = (localSecondStart == std::numeric_limits::max()) ? 0.0 : localSecondStart; m_dSecondEnd = localSecondEnd; - + m_iComply = channelSurface.size(); setThreeUiData(); - m_subordinate = handleSubordinate(data, board, channel); + m_subordinate.clear(); int maxSubordinate = 0; - for (int val : m_subordinate.values()) { - if (val > maxSubordinate) maxSubordinate = val; + int primaryBoardIdx = board - 1; + int primaryChannelIdx = channel - 1; + + for (int sBoard = 0; sBoard < MAX_BOARD; ++sBoard) { + for (int sChannel = 0; sChannel < MAX_CHANNEL; ++sChannel) { + QString key = QStringLiteral(u"widget_%1_%2").arg(sBoard + 1).arg(sChannel + 1); + int count = result.coincidenceMatrix[primaryBoardIdx][primaryChannelIdx][sBoard][sChannel]; + m_subordinate[key] = count; + if (count > maxSubordinate) maxSubordinate = count; + } } + ui->widget->setAllWidgetColorMaxValue(maxSubordinate); - for (int i = 0; i < m_subordinate.keys().size();i++) { - QString objectName =m_subordinate.keys().at(i); - QStringList parts = objectName.split('_'); - int bd = parts[1].toInt(); - int ch = parts[2].toInt(); - ui->widget->setWidgetData(bd,ch,m_boardChannel[bd - 1][ch - 1],m_subordinate[objectName]); + for (int sBoard = 1; sBoard <= MAX_BOARD; ++sBoard) { + for (int sChannel = 1; sChannel <= MAX_CHANNEL; ++sChannel) { + if (sBoard == board && sChannel == channel) { + continue; + } + QString key = QStringLiteral(u"widget_%1_%2").arg(sBoard).arg(sChannel); + int totalCount = m_boardChannel[sBoard - 1][sChannel - 1]; + int secondCount = m_subordinate[key]; + ui->widget->setWidgetData(sBoard, sChannel, totalCount, secondCount); + } } - ui->widget->setWidgetData(board,channel,m_boardChannel[board - 1][channel - 1],data.size()); + + int primaryTotalCount = m_boardChannel[primaryBoardIdx][primaryChannelIdx]; + int primaryCount = channelSurface.size(); + ui->widget->setWidgetData(board, channel, primaryTotalCount, primaryCount); } QMap ConformityAnalysis::handleSubordinate(QVector &eventData,int Board, int Channel) @@ -851,7 +1099,9 @@ void ConformityAnalysis::saveHistoryToFile() int idx = findHistoryIndex(m_conformFileMap.value(conformCount), conformCount); if (idx >= 0) { saveJsonStream(conformCount, _conformityHistoryList[idx]); - saveSurfaceDataToBinary(conformCount, _conformityHistoryList[idx].surfaceData); + saveSurfaceDataToBinary(conformCount, + _conformityHistoryList[idx].surfaceData, + _conformityHistoryList[idx].channelSurfaceData); } } } @@ -897,9 +1147,19 @@ int ConformityAnalysis::saveCurrentAnalysisToHistory(int conformCount) int ConformityAnalysis::findHistoryIndex(const QString& fileName, int conformCount) const { + // for (int i = 0; i < _conformityHistoryList.size(); ++i) { + // const ConformityHistoryItem& item = _conformityHistoryList[i]; + // if (item.dataFileName == fileName && item.conformCount == conformCount) { + // return i; + // } + // } + // return -1; + + QString absoluteFileName = QFileInfo(fileName).absoluteFilePath(); for (int i = 0; i < _conformityHistoryList.size(); ++i) { const ConformityHistoryItem& item = _conformityHistoryList[i]; - if (item.dataFileName == fileName && item.conformCount == conformCount) { + if (QFileInfo(item.dataFileName).absoluteFilePath() == absoluteFileName + && item.conformCount == conformCount) { return i; } } diff --git a/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.h b/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.h index e3955f2..b2808a7 100644 --- a/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.h +++ b/src/ThreeDimensionalConformityAnalysisView/ConformityAnalysis.h @@ -37,6 +37,7 @@ struct ConformityCalculatedResult { int conformCount; // 符合粒子数 QString dataFileName; // 原始CSV文件名 int totalEvents; // 符合事件总计数 + int totalParticles; // 总粒子数 double primaryEnergyStart; // 初级粒子能量范围-起始 double primaryEnergyEnd; // 初级粒子能量范围-结束 double secondaryEnergyStart; // 次级粒子能量范围-起始 @@ -44,14 +45,23 @@ struct ConformityCalculatedResult { // 板卡通道计数数据(8板×4通道) int boardChannelData[MAX_BOARD][MAX_CHANNEL]; + // 初级粒子计数数据 QMap firstParticleData; - // 三维曲面数据 + + // ✅ 新增:次级粒子符合矩阵 [初级板卡][初级通道][次级板卡][次级通道] = 符合次数 + int coincidenceMatrix[MAX_BOARD][MAX_CHANNEL][MAX_BOARD][MAX_CHANNEL]; + + // ✅ 新增:每个板卡通道作为初级粒子的曲面数据 + QMap> channelSurfaceData; + + // 三维曲面数据(全局) QVector surfaceData; // 构造函数 ConformityCalculatedResult() { memset(boardChannelData, 0, sizeof(boardChannelData)); + memset(coincidenceMatrix, 0, sizeof(coincidenceMatrix)); // 初始化符合矩阵 totalEvents = 0; primaryEnergyStart = primaryEnergyEnd = 0.0; secondaryEnergyStart = secondaryEnergyEnd = 0.0; @@ -75,6 +85,13 @@ struct ConformityHistoryItem { int boardChannelData[MAX_BOARD][MAX_CHANNEL]; // 初级粒子计数数据 QMap firstParticleData; + + // ✅ 新增:符合矩阵 + int coincidenceMatrix[MAX_BOARD][MAX_CHANNEL][MAX_BOARD][MAX_CHANNEL]; + + // ✅ 新增:每个通道的曲面数据 + QMap> channelSurfaceData; + // 三维曲面数据 QVector surfaceData; @@ -148,7 +165,8 @@ private: // 流式写入JSON(零大小限制) void saveJsonStream(int conformCount, const ConformityHistoryItem& item); // 二进制保存超大曲面数据 - void saveSurfaceDataToBinary(int conformCount, const QVector& data); + void saveSurfaceDataToBinary(int conformCount, const QVector& globalData, const QMap>& channelData); + // 仅保存指定符合数的历史记录到对应文件 void saveSingleHistoryToFile(int conformCount, const ConformityHistoryItem& item); int saveCurrentAnalysisToHistory(int conformCount); @@ -167,6 +185,9 @@ private: QVector readCsv(const QString& fileName); // 释放事件数组内存 void freeEventVector(QVector& vec); + // 后台解析剩余的符合数数据 + void parseRemainingConformDataInBackground(const QList& conformCounts); + private: Ui::ConformityAnalysis *ui; diff --git a/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.cpp b/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.cpp index f27d3ba..467028e 100644 --- a/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.cpp +++ b/src/ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.cpp @@ -28,7 +28,6 @@ DetectorStatusSummary::~DetectorStatusSummary() delete ui; } -// ========== 原有接口(仅修改样式表相关部分) ========== void DetectorStatusSummary::setName(QString name) { ui->label_name->setText(name); @@ -76,7 +75,6 @@ void DetectorStatusSummary::setColorMaxValue(int maxValue) m_nMaxValue = maxValue; } -// 关键:移除所有硬编码的样式表背景色 void DetectorStatusSummary::setInitWidgetColor() { m_backgroundColor = QColor("#0E508A"); diff --git a/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.cpp b/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.cpp index e3f1afd..a5688e1 100644 --- a/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.cpp +++ b/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.cpp @@ -209,12 +209,10 @@ void ThreeDDisplay::_updateSurfaceData() // m_surface->axisZ()->setRange(minSecondary, maxSecondary); if (flag) { - qDebug()<< maxPrimary << maxSecondary; m_surface->axisX()->setRange(0, maxPrimary); m_surface->axisZ()->setRange(0, maxSecondary); flag = false; } - qDebug()<axisY()->setRange(0, getMaxCount(dataMatrix)); } diff --git a/src/main.cpp b/src/main.cpp index 6d672cd..7650cfa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,7 +44,7 @@ int main(int argc, char *argv[]) // 设置应用信息 app.setApplicationName("EnergySpectrumAnalyer"); app.setApplicationVersion(QVersionNumber(1, 0, 0).toString()); - app.setApplicationDisplayName(QStringLiteral(u"符合能谱分测量析软件")); + app.setApplicationDisplayName(QStringLiteral(u"符合能谱测量分析软件")); app.setOrganizationName(QStringLiteral("NINT,4,4")); QString app_desc = QStringLiteral(u"\ 符合能谱测量分析软件是面向核物理符合测量场景的专用分析工具,\ diff --git a/src/src.pro b/src/src.pro index 8f3fe69..ab59bc8 100644 --- a/src/src.pro +++ b/src/src.pro @@ -106,6 +106,7 @@ SOURCES += \ MeasureAnalysisProjectModel.cpp \ ParticleInjectTimeView/ParticleInjectTimeAnalysisView.cpp \ ParticleTimeDifferenceView/ParticleTimeDifferenceView.cpp \ + RegionOfInterest/RegionOfInterest.cpp \ VirtualTable/CsvDataSource.cpp \ VirtualTable/SampleDataSource.cpp \ VirtualTable/VirtualTableModel.cpp \ @@ -161,6 +162,7 @@ HEADERS += \ MeasureAnalysisProjectModel.h \ ParticleInjectTimeView/ParticleInjectTimeAnalysisView.h \ ParticleTimeDifferenceView/ParticleTimeDifferenceView.h \ + RegionOfInterest/RegionOfInterest.h \ VirtualTable/CsvDataSource.h \ VirtualTable/DataSource.h \ VirtualTable/SampleDataSource.h \ @@ -187,6 +189,7 @@ FORMS += \ MeasureDeviceParamsConfigView/DeviceParamsTableForm.ui \ ParticleCountPlotView/BatchEnergyScaleDialog.ui \ NewMeasureAnalysisDlg.ui \ + RegionOfInterest/RegionOfInterest.ui \ ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.ui \ ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.ui \ ThreeDimensionalConformityAnalysisView/ThreeDDisplay.ui \ @@ -203,3 +206,4 @@ contains(DEFINES, ENABLE_DEBUG) { } } +