修改读取数据为线程处理,新增核素分析操作列,新增核素分析保存和新增按钮

This commit is contained in:
anxinglong 2026-06-24 15:56:48 +08:00
parent 1c7784702f
commit 1c6bd01ed6
7 changed files with 682 additions and 15 deletions

View File

@ -23,7 +23,6 @@
#include "EnergyScaleDataModel.h"
#include "DataCalcProcess/CoincidenceSpectrumProcess.h"
#include "BackgroundTaskListModel.h"
#include "GvfToCsv/GvfToCsv.h"
#include <QDebug>
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<uint, std::array<unsigned long long, 4096>> &addrCounts,
const QHash<uint, QHash<double, unsigned long long>> &energyCounts,
const QHash<double, unsigned long long> &allChannelEnergyCounts,
const QHash<uint, QString> &addrCountFilenames,
const QHash<uint, QString> &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<ParticleData> 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<ParticleData> &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<ParticleData> &data, const QString &projectDir)
{
QHash<uint, QHash<uint, unsigned long long>> 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<uint, QString> 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<unsigned long long, 4096> 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<ParticleData> &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<double> 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<ParticleData> &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<uint, QHash<double, unsigned long long>> deltaCounts;
QHash<double, unsigned long long> 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<double> 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<uint, QString> 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<double> 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<double> 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"部分通道的能量计数数据写入失败"));
}
}

View File

@ -8,6 +8,9 @@
#include <QMap>
#include <QVariant>
#include <QEventLoop>
#include <array>
#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<uint, std::array<unsigned long long, 4096>> &addrCounts,
const QHash<uint, QHash<double, unsigned long long>> &energyCounts,
const QHash<double, unsigned long long> &allChannelEnergyCounts,
const QHash<uint, QString> &addrCountFilenames,
const QHash<uint, QString> &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<ParticleData> &dataList, const QString &projectDir);
void changeChannelParticleCount(QList<ParticleData> &data, const QString &projectDir);
void changeParticleEnergyData(QList<ParticleData> &dataList, const QString &projectDir,
const QString &energyScaleFilename, const QString &projectName);
void changeEnergyCountData(QList<ParticleData> &dataList, const QString &projectDir,
const QString &energyScaleFilename, const QString &projectName);
GvfToCsv *_gvfToCsv = nullptr;
// 通道道址计数缓存
QHash<uint, std::array<unsigned long long, 4096>> channel_address_counts;
// 通道能量计数缓存
QHash<uint, QHash<double, unsigned long long>> channel_energy_counts;
// 全通道能量计数缓存
QHash<double, unsigned long long> all_channel_energy_counts;
// 文件路径缓存
QHash<uint, QString> particle_count_filename_list;
QHash<uint, QString> energy_count_filename_list;
QMutex m_channelCountMutex;
QMutex m_energyDataMutex;
QMutex m_energyCountMutex;
};
}
#endif // DATAPROCESSWORKPOOL_H

View File

@ -50,6 +50,10 @@ void EnergyCountPlotView::SetAnalyzeDataFilename(const QMap<QString, QVariant> &
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);
}
}

View File

@ -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<ParticleData> particles = _gvfToCsv->parseParticleFrames(data);
//处理粒子数据
changeParticleData(particles);
//处理道址计数
changeChannelParticleCount(particles);
//处理能谱数据
changeParticleEnergyData(particles);
//处理能量计数
changeEnergyCountData(particles);
//处理道址计数谱
changeAddressCountView(particles);
//处理能量计数谱
changeEnergyCountView(particles);
// QList<ParticleData> 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<QString, QMap<QString, QStandardItem *>> all_nodes = ProjectList::Instance()->getProjectNodeItems();
QMap<QString, QStandardItem *> 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();
}

View File

@ -5,6 +5,7 @@
#include <QMutex>
#include "GvfToCsv/GvfToCsv.h"
#include "MeasureAnalysisProjectModel.h"
#include "DataProcessWorkPool.h"
#include <QTimer>
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

View File

@ -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");
}
}
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<QPushButton*>(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<QPushButton*>();
if (delBtn) {
delBtn->setProperty("rowIndex", i);
}
}
}
}
}
void NuclideAnalysisView::onSaveSelectedNuclides()
{
}
void NuclideAnalysisView::onAddNuclide()
{
}

View File

@ -12,6 +12,9 @@
#include <QDialog>
#include <QTableWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QCheckBox>
#include <QHBoxLayout>
#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