From 1c6bd01ed617cc1f5b69588b25f220f6366c77cd Mon Sep 17 00:00:00 2001 From: anxinglong <2910824064@qq.com> Date: Wed, 24 Jun 2026 15:56:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AF=BB=E5=8F=96=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E4=B8=BA=E7=BA=BF=E7=A8=8B=E5=A4=84=E7=90=86=EF=BC=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=A0=B8=E7=B4=A0=E5=88=86=E6=9E=90=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E5=88=97=EF=BC=8C=E6=96=B0=E5=A2=9E=E6=A0=B8=E7=B4=A0?= =?UTF-8?q?=E5=88=86=E6=9E=90=E4=BF=9D=E5=AD=98=E5=92=8C=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DataProcessWorkPool.cpp | 349 +++++++++++++++++- src/DataProcessWorkPool.h | 68 ++++ .../EnergyCountPlotView.cpp | 4 + src/MainWindow.cpp | 182 ++++++++- src/MainWindow.h | 11 + .../NuclideAnalysisView.cpp | 70 +++- src/NuclideAnalysisView/NuclideAnalysisView.h | 13 + 7 files changed, 682 insertions(+), 15 deletions(-) diff --git a/src/DataProcessWorkPool.cpp b/src/DataProcessWorkPool.cpp index d385319..6b43c28 100644 --- a/src/DataProcessWorkPool.cpp +++ b/src/DataProcessWorkPool.cpp @@ -23,7 +23,6 @@ #include "EnergyScaleDataModel.h" #include "DataCalcProcess/CoincidenceSpectrumProcess.h" #include "BackgroundTaskListModel.h" -#include "GvfToCsv/GvfToCsv.h" #include using namespace DataProcessWorkPool; @@ -1132,4 +1131,352 @@ bool GvfToCsvDataTask::processTask() } return m_conversionSuccess; +} + +//2026-06-24 +RealtimeGvfDataWorker::RealtimeGvfDataWorker(QObject *parent) + : QObject(parent) +{ +} + +RealtimeGvfDataWorker::~RealtimeGvfDataWorker() +{ +} + +void RealtimeGvfDataWorker::setGvfParser(GvfToCsv *parser) +{ + _gvfToCsv = parser; +} + +void RealtimeGvfDataWorker::syncInitialData( + const QHash> &addrCounts, + const QHash> &energyCounts, + const QHash &allChannelEnergyCounts, + const QHash &addrCountFilenames, + const QHash &energyCountFilenames) +{ + QMutexLocker locker1(&m_channelCountMutex); + QMutexLocker locker2(&m_energyCountMutex); + channel_address_counts = addrCounts; + channel_energy_counts = energyCounts; + all_channel_energy_counts = allChannelEnergyCounts; + particle_count_filename_list = addrCountFilenames; + energy_count_filename_list = energyCountFilenames; +} + +void RealtimeGvfDataWorker::processGvfData(const QByteArray &data, + const QString &projectDir, + const QString &energyScaleFilename, + const QString &projectName) +{ + if (!_gvfToCsv) return; + + QList particles = _gvfToCsv->parseParticleFrames(data); + if (particles.isEmpty()) { + LOG_INFO(QStringLiteral(u"[%1]本次GVF数据未解析到有效粒子,跳过处理").arg(projectName)); + return; + } + + // 1. 粒子数据写入CSV + changeParticleData(particles, projectDir); + emit particleDataWritten(); + + // 2. 道址计数统计 + changeChannelParticleCount(particles, projectDir); + emit addressCountUpdated(); + emit addressCountViewNeedRefresh(); + + // 3. 粒子能量数据(需能量刻度) + if (!energyScaleFilename.isEmpty()) { + changeParticleEnergyData(particles, projectDir, energyScaleFilename, projectName); + } + + // 4. 能量计数统计(需能量刻度) + if (!energyScaleFilename.isEmpty()) { + changeEnergyCountData(particles, projectDir, energyScaleFilename, projectName); + emit energyCountUpdated(); + emit energyCountViewNeedRefresh(); + } +} + +// ---- 粒子数据写入CSV ---- +void RealtimeGvfDataWorker::changeParticleData(QList &dataList, const QString &projectDir) +{ + QString csvPath = QDir(projectDir).filePath(QStringLiteral(u"粒子数据.csv")); + QFile outFile(csvPath); + if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) { + LOG_ERROR(QStringLiteral(u"无法打开CSV文件(追加模式): %1,错误: %2") + .arg(csvPath).arg(outFile.errorString())); + return; + } + + QTextStream out(&outFile); + out.setCodec("UTF-8"); + if (outFile.size() == 0) { + out << QStringLiteral(u"板卡号,通道号,道址,时间计数\n"); + } + + QString csvBuffer; + csvBuffer.reserve(dataList.size() * 64); + for (const auto &p : dataList) { + csvBuffer += QString("%1,%2,%3,%4\n") + .arg(p.boardId).arg(p.channelId) + .arg(p.address).arg(p.timestampCount); + } + out << csvBuffer; + out.flush(); + outFile.close(); +} + +// ---- 道址计数统计 ---- +void RealtimeGvfDataWorker::changeChannelParticleCount(QList &data, const QString &projectDir) +{ + QHash> deltaCounts; + for (const auto &info : data) { + int channel_num = info.boardId * 4 + info.channelId + 1; + uint address = info.address; + if (address >= 4096) continue; + deltaCounts[channel_num][address]++; + } + if (deltaCounts.isEmpty()) return; + + const QString every_ch_count_dir = QDir(projectDir).filePath(QStringLiteral(u"通道道址计数")); + QDir(every_ch_count_dir).mkpath(every_ch_count_dir); + + QMutexLocker locker(&m_channelCountMutex); + bool hasError = false; + QHash newChannelFiles; + for (auto channelIt = deltaCounts.constBegin(); channelIt != deltaCounts.constEnd(); ++channelIt) { + uint channel_num = channelIt.key(); + const auto &addressDeltas = channelIt.value(); + + if (!channel_address_counts.contains(channel_num)) { + std::array init; + init.fill(0); + channel_address_counts.insert(channel_num, init); + } + + auto &channelCounts = channel_address_counts[channel_num]; + for (auto addrIt = addressDeltas.constBegin(); addrIt != addressDeltas.constEnd(); ++addrIt) { + channelCounts[addrIt.key()] += addrIt.value(); + } + + QString count_data_filename; + if (particle_count_filename_list.contains(channel_num)) { + count_data_filename = particle_count_filename_list[channel_num]; + } else { + count_data_filename = QDir(every_ch_count_dir).filePath( + QStringLiteral(u"通道%1粒子计数.csv").arg(channel_num)); + particle_count_filename_list.insert(channel_num, count_data_filename); + newChannelFiles.insert(channel_num, count_data_filename); // 新增的加入记录 + } + QString tmp_filename = count_data_filename + ".tmp"; + QFile outFile(tmp_filename); + if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + hasError = true; + continue; + } + + QTextStream out(&outFile); + out.setCodec("UTF-8"); + out << QStringLiteral(u"道址,计数\n"); + + QString csvBuffer; + csvBuffer.reserve(4096 * 32); + for (int address = 0; address < 4096; ++address) { + csvBuffer += QString("%1,%2\n").arg(address).arg(channelCounts[address]); + } + out << csvBuffer; + out.flush(); + outFile.close(); + + // 写完后原子替换原文件 + QFile::remove(count_data_filename); + QFile::rename(tmp_filename, count_data_filename); + } + // 只通知新增的通道文件 + for (auto it = newChannelFiles.constBegin(); it != newChannelFiles.constEnd(); ++it) { + emit newAddressCountChannelFile(it.key(), it.value()); + } + if (hasError) { + LOG_WARN(QStringLiteral(u"部分通道的道址计数数据写入失败")); + } +} + +// ---- 粒子能量数据 ---- +void RealtimeGvfDataWorker::changeParticleEnergyData(QList &dataList, + const QString &projectDir, + const QString &energyScaleFilename, + const QString &projectName) +{ + EnergyScaleDataModel energy_scale_data_model(energyScaleFilename); + if (!energy_scale_data_model.LoadData() || !energy_scale_data_model.IsValid()) { + LOG_WARN(QStringLiteral(u"[%1]能量刻度数据无效,跳过能谱处理").arg(projectName)); + return; + } + + QMutexLocker locker(&m_energyDataMutex); + QString energy_spectrum_filename = QDir(projectDir).filePath(QStringLiteral(u"能谱数据.csv")); + QFile outFile(energy_spectrum_filename); + if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) { + LOG_ERROR(QStringLiteral(u"[%1]无法打开能谱文件: %2").arg(projectName).arg(energy_spectrum_filename)); + return; + } + + QTextStream out(&outFile); + out.setCodec("UTF-8"); + if (outFile.size() == 0) { + out << QStringLiteral(u"板卡号,通道号,道址,能量(KeV),时间计数\n"); + } + + QString csvBuffer; + csvBuffer.reserve(dataList.size() * 80); + + for (const auto &particle : dataList) { + int channel_num = particle.boardId * 4 + particle.channelId + 1; + const QString channel_name = QStringLiteral(u"通道%1").arg(channel_num); + std::vector coeffs = energy_scale_data_model.GetEnergyFitResultCoeffs(channel_name); + if (coeffs.empty()) continue; + + double energy = GaussPolyCoe::Predict(coeffs, particle.address); + csvBuffer += QString("%1,%2,%3,%4,%5\n") + .arg(particle.boardId).arg(particle.channelId) + .arg(particle.address) + .arg(energy, 0, 'f', 4) + .arg(particle.timestampCount); + } + + if (!csvBuffer.isEmpty()) { + out << csvBuffer; + out.flush(); + } + outFile.close(); + if (!csvBuffer.isEmpty()) + emit particleEnergyDataUpdated(energy_spectrum_filename); + +} + +// ---- 能量计数统计 ---- +void RealtimeGvfDataWorker::changeEnergyCountData(QList &dataList, + const QString &projectDir, + const QString &energyScaleFilename, + const QString &projectName) +{ + EnergyScaleDataModel energy_scale_data_model(energyScaleFilename); + if (!energy_scale_data_model.LoadData() || !energy_scale_data_model.IsValid()) { + LOG_WARN(QStringLiteral(u"[%1]能量刻度数据无效,跳过能量计数处理").arg(projectName)); + return; + } + + QHash> deltaCounts; + QHash deltaAllChannelCounts; + + for (const auto &particle : dataList) { + int channel_num = particle.boardId * 4 + particle.channelId + 1; + const QString channel_name = QStringLiteral(u"通道%1").arg(channel_num); + std::vector coeffs = energy_scale_data_model.GetEnergyFitResultCoeffs(channel_name); + if (coeffs.empty()) continue; + + double energy = GaussPolyCoe::Predict(coeffs, particle.address); + double energy_key = qRound(energy * 10.0) / 10.0; + deltaCounts[channel_num][energy_key]++; + deltaAllChannelCounts[energy_key]++; + } + + if (deltaCounts.isEmpty()) return; + + const QString energy_count_dir = QDir(projectDir).filePath(QStringLiteral(u"通道能量计数")); + QDir(energy_count_dir).mkpath(energy_count_dir); + + QMutexLocker locker(&m_energyCountMutex); + bool hasError = false; + QHash newChannelFiles; + for (auto channelIt = deltaCounts.constBegin(); channelIt != deltaCounts.constEnd(); ++channelIt) { + uint channel_num = channelIt.key(); + const auto &energyDeltas = channelIt.value(); + auto &channelCounts = channel_energy_counts[channel_num]; + + for (auto eIt = energyDeltas.constBegin(); eIt != energyDeltas.constEnd(); ++eIt) { + channelCounts[eIt.key()] += eIt.value(); + } + + QString count_data_filename; + if (energy_count_filename_list.contains(channel_num)) { + count_data_filename = energy_count_filename_list[channel_num]; + } else { + count_data_filename = QDir(energy_count_dir).filePath( + QStringLiteral(u"通道%1能量计数.csv").arg(channel_num)); + energy_count_filename_list.insert(channel_num, count_data_filename); + newChannelFiles.insert(channel_num, count_data_filename); + } + QString tmp_filename = count_data_filename + ".tmp"; + QFile outFile(tmp_filename); + if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + hasError = true; + continue; + } + QTextStream out(&outFile); + out.setCodec("UTF-8"); + out << QStringLiteral(u"能量(KeV),计数\n"); + + QList sortedEnergies = channelCounts.keys(); + std::sort(sortedEnergies.begin(), sortedEnergies.end()); + + QString csvBuffer; + csvBuffer.reserve(sortedEnergies.size() * 40); + for (double energy : sortedEnergies) { + csvBuffer += QString("%1,%2\n").arg(energy, 0, 'f', 1).arg(channelCounts[energy]); + } + out << csvBuffer; + out.flush(); + outFile.close(); + // 原子替换 + QFile::remove(count_data_filename); + QFile::rename(tmp_filename, count_data_filename); + } + + // 全通道能量计数 + if (!deltaAllChannelCounts.isEmpty()) { + for (auto eIt = deltaAllChannelCounts.constBegin(); eIt != deltaAllChannelCounts.constEnd(); ++eIt) { + all_channel_energy_counts[eIt.key()] += eIt.value(); + } + QString all_ch_filename = QDir(energy_count_dir).filePath(QStringLiteral(u"全通道.csv")); + QString tmp_filename = all_ch_filename + ".tmp"; + + QFile allChannelFile(tmp_filename); + if (allChannelFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + QTextStream outAll(&allChannelFile); + outAll.setCodec("UTF-8"); + outAll << QStringLiteral(u"能量(KeV),计数\n"); + + QList sortedAll = all_channel_energy_counts.keys(); + std::sort(sortedAll.begin(), sortedAll.end()); + + QString allCsvBuffer; + allCsvBuffer.reserve(sortedAll.size() * 40); + for (double e : sortedAll) { + allCsvBuffer += QString("%1,%2\n").arg(e, 0, 'f', 3).arg(all_channel_energy_counts[e]); + } + outAll << allCsvBuffer; + outAll.flush(); + allChannelFile.close(); + QFile::remove(all_ch_filename); + QFile::rename(tmp_filename, all_ch_filename); + } else { + hasError = true; + } + } + // 通知主线程:新通道文件已生成,更新项目模型 + for (auto it = newChannelFiles.constBegin(); it != newChannelFiles.constEnd(); ++it) { + emit newEnergyCountChannelFile(it.key(), it.value()); + } + + // 通知主线程:全通道能量计数文件更新 + QString all_ch_filename = QDir(energy_count_dir).filePath(QStringLiteral(u"全通道.csv")); + if (!deltaAllChannelCounts.isEmpty()) { + emit allChannelEnergyCountFileUpdated(all_ch_filename); + } + if (hasError) { + LOG_WARN(QStringLiteral(u"部分通道的能量计数数据写入失败")); + } } \ No newline at end of file diff --git a/src/DataProcessWorkPool.h b/src/DataProcessWorkPool.h index 6849716..5aa1275 100644 --- a/src/DataProcessWorkPool.h +++ b/src/DataProcessWorkPool.h @@ -8,6 +8,9 @@ #include #include #include +#include +#include "GvfToCsv/GvfToCsv.h" + class EnergyScaleDataModel; class MeasureAnalysisProjectModel; @@ -184,6 +187,71 @@ namespace DataProcessWorkPool QString m_error; bool m_conversionSuccess = false; }; + + class RealtimeGvfDataWorker : public QObject + { + Q_OBJECT + public: + explicit RealtimeGvfDataWorker(QObject *parent = nullptr); + ~RealtimeGvfDataWorker(); + + // 设置GVF解析器 + void setGvfParser(GvfToCsv *parser); + + // 同步初始计数数据(开始测量前调用一次) + void syncInitialData( + const QHash> &addrCounts, + const QHash> &energyCounts, + const QHash &allChannelEnergyCounts, + const QHash &addrCountFilenames, + const QHash &energyCountFilenames); + + signals: + // 处理完成信号(回到主线程更新UI) + void particleDataWritten(); // 粒子数据CSV写入完成 + void addressCountUpdated(); // 道址计数更新完成 + void energyCountUpdated(); // 能量计数更新完成 + void addressCountViewNeedRefresh(); // 需要刷新道址计数谱 + void energyCountViewNeedRefresh(); // 需要刷新能量计数谱 + // 道址计数有新通道文件生成(通知主线程更新项目模型和节点) + void newAddressCountChannelFile(uint channelNum, const QString &filename); + // 能量计数有新通道文件生成 + void newEnergyCountChannelFile(uint channelNum, const QString &filename); + // 全通道能量计数文件更新 + void allChannelEnergyCountFileUpdated(const QString &filename); + // 粒子能量数据写入完成(通知主线程更新节点状态) + void particleEnergyDataUpdated(const QString &energySpectrumFilename); + public slots: + // 处理一批GVF数据(在子线程中执行) + void processGvfData(const QByteArray &data, + const QString &projectDir, + const QString &energyScaleFilename, + const QString &projectName); + + private: + void changeParticleData(QList &dataList, const QString &projectDir); + void changeChannelParticleCount(QList &data, const QString &projectDir); + void changeParticleEnergyData(QList &dataList, const QString &projectDir, + const QString &energyScaleFilename, const QString &projectName); + void changeEnergyCountData(QList &dataList, const QString &projectDir, + const QString &energyScaleFilename, const QString &projectName); + + GvfToCsv *_gvfToCsv = nullptr; + + // 通道道址计数缓存 + QHash> channel_address_counts; + // 通道能量计数缓存 + QHash> channel_energy_counts; + // 全通道能量计数缓存 + QHash all_channel_energy_counts; + // 文件路径缓存 + QHash particle_count_filename_list; + QHash energy_count_filename_list; + + QMutex m_channelCountMutex; + QMutex m_energyDataMutex; + QMutex m_energyCountMutex; + }; } #endif // DATAPROCESSWORKPOOL_H diff --git a/src/EnergyCountPlotView/EnergyCountPlotView.cpp b/src/EnergyCountPlotView/EnergyCountPlotView.cpp index fd9bc90..4cbc511 100644 --- a/src/EnergyCountPlotView/EnergyCountPlotView.cpp +++ b/src/EnergyCountPlotView/EnergyCountPlotView.cpp @@ -50,6 +50,10 @@ void EnergyCountPlotView::SetAnalyzeDataFilename(const QMap & 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); } } diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 41a0f8d..9df1750 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -124,6 +124,29 @@ MainWindow::MainWindow(QWidget* parent) initAction(); initStatusBar(); + // 初始化实时GVF数据处理线程 + _gvfProcessThread = new QThread(this); + _gvfWorker = new DataProcessWorkPool::RealtimeGvfDataWorker; + _gvfWorker->setGvfParser(_gvfToCsv); + _gvfWorker->moveToThread(_gvfProcessThread); + + // 连接UI更新信号 + connect(_gvfWorker, &DataProcessWorkPool::RealtimeGvfDataWorker::particleDataWritten, + this, &MainWindow::updataTable); + connect(_gvfWorker, &DataProcessWorkPool::RealtimeGvfDataWorker::addressCountViewNeedRefresh, + this, &MainWindow::onAddressCountViewNeedRefresh); + connect(_gvfWorker, &DataProcessWorkPool::RealtimeGvfDataWorker::energyCountViewNeedRefresh, + this, &MainWindow::onEnergyCountViewNeedRefresh); + connect(_gvfWorker, &DataProcessWorkPool::RealtimeGvfDataWorker::newAddressCountChannelFile, + this, &MainWindow::onNewAddressCountChannelFile); + connect(_gvfWorker, &DataProcessWorkPool::RealtimeGvfDataWorker::newEnergyCountChannelFile, + this, &MainWindow::onNewEnergyCountChannelFile); + connect(_gvfWorker, &DataProcessWorkPool::RealtimeGvfDataWorker::allChannelEnergyCountFileUpdated, + this, &MainWindow::onAllChannelEnergyCountFileUpdated); + connect(_gvfWorker, &DataProcessWorkPool::RealtimeGvfDataWorker::particleEnergyDataUpdated, + this, &MainWindow::onParticleEnergyDataUpdated); + _gvfProcessThread->start(); + this->applyStyleSheet(); _s_main_win = this; } @@ -612,19 +635,27 @@ void MainWindow::onRunningInfo(const QString &run_info) void MainWindow::onGvfData(const QByteArray &data) { - QList particles = _gvfToCsv->parseParticleFrames(data); - //处理粒子数据 - changeParticleData(particles); - //处理道址计数 - changeChannelParticleCount(particles); - //处理能谱数据 - changeParticleEnergyData(particles); - //处理能量计数 - changeEnergyCountData(particles); - //处理道址计数谱 - changeAddressCountView(particles); - //处理能量计数谱 - changeEnergyCountView(particles); + // QList particles = _gvfToCsv->parseParticleFrames(data); + // //处理粒子数据 + // changeParticleData(particles); + // //处理道址计数 + // changeChannelParticleCount(particles); + // //处理能谱数据 + // changeParticleEnergyData(particles); + // //处理能量计数 + // changeEnergyCountData(particles); + // //处理道址计数谱 + // changeAddressCountView(particles); + // //处理能量计数谱 + // changeEnergyCountView(particles); + MeasureAnalysisProjectModel *project_model = ProjectList::Instance()->GetCurrentProjectModel(); + if (!project_model) return; + + QMetaObject::invokeMethod(_gvfWorker, "processGvfData", Qt::QueuedConnection, + Q_ARG(QByteArray, data), + Q_ARG(QString, project_model->GetProjectDir()), + Q_ARG(QString, project_model->GetEnergyScaleFilename()), + Q_ARG(QString, project_model->GetProjectName())); } //处理粒子数据 @@ -1292,3 +1323,128 @@ void MainWindow::on_action_device_connect_cfg_triggered() } +void MainWindow::onAddressCountViewNeedRefresh() +{ + MeasureAnalysisProjectModel *pro_model = ProjectList::Instance()->GetCurrentProjectModel(); + if (!pro_model) return; + + bool status_ok = !pro_model->GetChannelAddressCountDataFilenameList().isEmpty(); + QString status = status_ok ? QStringLiteral(u"有效") : QStringLiteral(u"无效"); + QString item_name = QStringLiteral(u"道址计数谱"); + QStandardItem *particleData = nodeMap[item_name]; + + if (particleData) { + bool bStatus = ProjectList::Instance()->GetNodeStatus(particleData); + if (!bStatus) { + ProjectList::Instance()->SetNodeStatus(particleData, status, true); + m_AddressCountTimer->start(); + } + } +} + +void MainWindow::onEnergyCountViewNeedRefresh() +{ + MeasureAnalysisProjectModel *pro_model = ProjectList::Instance()->GetCurrentProjectModel(); + if (!pro_model) return; + + bool status_ok = !pro_model->GetChannelEnergyCountDataFilenameList().isEmpty(); + QString status = status_ok ? QStringLiteral(u"有效") : QStringLiteral(u"无效"); + QString item_name = QStringLiteral(u"能量计数谱"); + QStandardItem *particleData = nodeMap[item_name]; + + if (particleData) { + bool bStatus = ProjectList::Instance()->GetNodeStatus(particleData); + if (!bStatus) { + ProjectList::Instance()->SetNodeStatus(particleData, status, true); + m_EnergyCountTimer->start(); + } + } +} + +void MainWindow::onNewAddressCountChannelFile(uint channelNum, const QString &filename) +{ + MeasureAnalysisProjectModel *project_model = ProjectList::Instance()->GetCurrentProjectModel(); + if (!project_model) return; + + project_model->SetChannelAddressCountDataFilename(channelNum, filename); + project_model->SaveProjectModel(); + + // 更新项目树节点 + const QString &adrr_count_item_name = QStringLiteral(u"道址计数"); + if (nodeMap.contains(adrr_count_item_name)) { + QStandardItem *adrr_count_item = nodeMap[adrr_count_item_name]; + bool status_ok = !project_model->GetChannelAddressCountDataFilenameList().isEmpty(); + QString status = status_ok ? QStringLiteral(u"有效") : QStringLiteral(u"无效"); + ProjectList::Instance()->SetNodeStatus(adrr_count_item, status, status_ok); + + QString item_name = QStringLiteral(u"通道%1道址计数").arg(channelNum); + if (!nodeMap.contains(item_name)) { + const QVariant &analys_type = QVariant::fromValue(AnalysisType::AddressCountData); + QStandardItem *node_item = ProjectList::Instance()->AddChildNode( + adrr_count_item, item_name, status, analys_type, true, status_ok); + node_item->setData(project_model->GetProjectName(), Qt::UserRole + 2); + node_item->setData(channelNum, Qt::UserRole + 3); + nodeMap[item_name] = node_item; + } + } +} + +void MainWindow::onNewEnergyCountChannelFile(uint channelNum, const QString &filename) +{ + MeasureAnalysisProjectModel *project_model = ProjectList::Instance()->GetCurrentProjectModel(); + if (!project_model) return; + + project_model->SetChannelEnergyCountDataFilename(channelNum, filename); + project_model->SaveProjectModel(); + + // 更新项目树节点 + const QString &energy_count_item_name = QStringLiteral(u"能量计数"); + QMap> all_nodes = ProjectList::Instance()->getProjectNodeItems(); + QMap project_nodes = all_nodes[project_model->GetProjectName()]; + + if (project_nodes.contains(energy_count_item_name)) { + QStandardItem *energy_count_item = project_nodes[energy_count_item_name]; + bool status_ok = !project_model->GetChannelEnergyCountDataFilenameList().isEmpty(); + QString status = status_ok ? QStringLiteral(u"有效") : QStringLiteral(u"无效"); + ProjectList::Instance()->SetNodeStatus(energy_count_item, status, status_ok); + + QString item_name = QStringLiteral(u"通道%1能量计数").arg(channelNum); + if (!project_nodes.contains(item_name)) { + const QVariant &analys_type = QVariant::fromValue(AnalysisType::EnergyCountData); + QStandardItem *node_item = ProjectList::Instance()->AddChildNode( + energy_count_item, item_name, status, analys_type, true, status_ok); + node_item->setData(project_model->GetProjectName(), Qt::UserRole + 2); + node_item->setData(channelNum, Qt::UserRole + 3); + project_nodes[item_name] = node_item; + } + } +} + +void MainWindow::onAllChannelEnergyCountFileUpdated(const QString &filename) +{ + MeasureAnalysisProjectModel *project_model = ProjectList::Instance()->GetCurrentProjectModel(); + if (!project_model) return; + + project_model->SetAllChannelEnergyTotalCountDataFilename(filename); + project_model->SaveProjectModel(); +} + +void MainWindow::onParticleEnergyDataUpdated(const QString &energySpectrumFilename) +{ + MeasureAnalysisProjectModel *project_model = ProjectList::Instance()->GetCurrentProjectModel(); + if (!project_model) return; + + // 更新项目模型 + project_model->SetParticleEnergyDataFilename(energySpectrumFilename); + project_model->SaveProjectModel(); + + // 更新节点状态 + const QString &energy_node_name = QStringLiteral(u"粒子能量数据"); + if (nodeMap.contains(energy_node_name)) { + QStandardItem *energy_node = nodeMap[energy_node_name]; + ProjectList::Instance()->SetNodeStatus(energy_node, QStringLiteral(u"有效"), true); + } + + // 更新数据表格 + // updataTable(); +} \ No newline at end of file diff --git a/src/MainWindow.h b/src/MainWindow.h index ebd308d..e5fb2dc 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -5,6 +5,7 @@ #include #include "GvfToCsv/GvfToCsv.h" #include "MeasureAnalysisProjectModel.h" +#include "DataProcessWorkPool.h" #include QT_BEGIN_NAMESPACE @@ -103,6 +104,13 @@ private slots: //设备连接管理 void on_action_device_connect_cfg_triggered(); + void onAddressCountViewNeedRefresh(); + void onEnergyCountViewNeedRefresh(); + void onNewAddressCountChannelFile(uint channelNum, const QString &filename); + void onNewEnergyCountChannelFile(uint channelNum, const QString &filename); + void onAllChannelEnergyCountFileUpdated(const QString &filename); + void onParticleEnergyDataUpdated(const QString &energySpectrumFilename); + private: QMutex _mutex_info_output; QPlainTextEdit* _plain_edit_info_output; @@ -143,5 +151,8 @@ private: //能量计数视图定时器 QTimer* m_EnergyCountTimer; + QThread *_gvfProcessThread = nullptr; + DataProcessWorkPool::RealtimeGvfDataWorker *_gvfWorker = nullptr; + }; #endif // MAINWINDOW_H diff --git a/src/NuclideAnalysisView/NuclideAnalysisView.cpp b/src/NuclideAnalysisView/NuclideAnalysisView.cpp index 9cbb307..f79cc4c 100644 --- a/src/NuclideAnalysisView/NuclideAnalysisView.cpp +++ b/src/NuclideAnalysisView/NuclideAnalysisView.cpp @@ -314,6 +314,7 @@ void NuclideAnalysisView::onActionPlotClear() _plot->replot(); } + void NuclideAnalysisView::addVerticalLine(double xValue, const QColor& color) { if(!m_bSelectHS) @@ -637,6 +638,18 @@ void NuclideAnalysisView::setupNuclideDialog() QVBoxLayout* layout = new QVBoxLayout(m_nuclideDialog); layout->setContentsMargins(6, 6, 6, 6); + QHBoxLayout* topBarLayout = new QHBoxLayout(); + topBarLayout->addStretch(); + m_addBtn = new QPushButton(QStringLiteral(u"新增核素"), m_nuclideDialog); + m_addBtn->setFixedWidth(80); + connect(m_addBtn, &QPushButton::clicked, this, &NuclideAnalysisView::onAddNuclide); + topBarLayout->addWidget(m_addBtn); + m_saveBtn = new QPushButton(QStringLiteral(u"保存"), m_nuclideDialog); + m_saveBtn->setFixedWidth(80); + connect(m_saveBtn, &QPushButton::clicked, this, &NuclideAnalysisView::onSaveSelectedNuclides); + topBarLayout->addWidget(m_saveBtn); + layout->addLayout(topBarLayout); + m_nuclideTable = new QTableWidget(m_nuclideDialog); QStringList headers = { "核素", "射线类型", "能量(keV)","操作" }; m_nuclideTable->setColumnCount(headers.size()); @@ -746,6 +759,7 @@ void NuclideAnalysisView::queryNuclidesForPeak(const PeakFitResultNuclide& peak) m_nuclideTable->setItem(i, 0, new QTableWidgetItem(m.nuclideName)); m_nuclideTable->setItem(i, 1, new QTableWidgetItem(m.rayType)); m_nuclideTable->setItem(i, 2, new QTableWidgetItem(m.energyDisplay)); + createRowOperationWidgets(i); } if (matches.isEmpty()) { @@ -771,4 +785,58 @@ bool NuclideAnalysisView::openNuclideDatabase() return false; } return db.openDatabase(dbPath, "nuclide_conn"); -} \ No newline at end of file +} + +void NuclideAnalysisView::createRowOperationWidgets(int row) +{ + QWidget* operationWidget = new QWidget(m_nuclideTable); + QHBoxLayout* hLayout = new QHBoxLayout(operationWidget); + hLayout->setContentsMargins(4, 2, 4, 2); + hLayout->setSpacing(8); + + // 复选框 + QCheckBox* checkBox = new QCheckBox(operationWidget); + checkBox->setObjectName("rowCheckBox"); + hLayout->addWidget(checkBox); + hLayout->addStretch(); + + // 删除按钮 + QPushButton* deleteBtn = new QPushButton(QStringLiteral(u"删除"), operationWidget); + deleteBtn->setFixedWidth(50); + deleteBtn->setProperty("rowIndex", row); + connect(deleteBtn, &QPushButton::clicked, this, &NuclideAnalysisView::onDeleteRow); + hLayout->addWidget(deleteBtn); + + m_nuclideTable->setCellWidget(row, 3, operationWidget); +} + +void NuclideAnalysisView::onDeleteRow() +{ + QPushButton* btn = qobject_cast(sender()); + if (!btn) return; + + int row = btn->property("rowIndex").toInt(); + if (row >= 0 && row < m_nuclideTable->rowCount()) { + m_nuclideTable->removeRow(row); + // 更新后续行的rowIndex属性 + for (int i = row; i < m_nuclideTable->rowCount(); ++i) { + QWidget* widget = m_nuclideTable->cellWidget(i, 3); + if (widget) { + QPushButton* delBtn = widget->findChild(); + if (delBtn) { + delBtn->setProperty("rowIndex", i); + } + } + } + } +} + +void NuclideAnalysisView::onSaveSelectedNuclides() +{ + +} + +void NuclideAnalysisView::onAddNuclide() +{ + +} diff --git a/src/NuclideAnalysisView/NuclideAnalysisView.h b/src/NuclideAnalysisView/NuclideAnalysisView.h index 4dd8b4b..1b40bf7 100644 --- a/src/NuclideAnalysisView/NuclideAnalysisView.h +++ b/src/NuclideAnalysisView/NuclideAnalysisView.h @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include #include "sqlitemanager.h" @@ -76,6 +79,8 @@ private: void queryNuclidesForPeak(const PeakFitResultNuclide& peak); bool openNuclideDatabase(); + void createRowOperationWidgets(int row); + private slots: void onActionPlotConfigure(); @@ -84,6 +89,11 @@ private slots: void onActionPlotClear(); + + void onDeleteRow(); + void onSaveSelectedNuclides(); + void onAddNuclide(); + private: CustomQwtPlot* _plot = nullptr; QString _workspace; @@ -109,5 +119,8 @@ private: QDialog* m_nuclideDialog = nullptr; QTableWidget* m_nuclideTable = nullptr; + QPushButton* m_saveBtn = nullptr; + QPushButton* m_addBtn = nullptr; // 新增核素按钮 + }; #endif // NUCLIDEANALYSISVIEW_H \ No newline at end of file