修改读取数据为线程处理,新增核素分析操作列,新增核素分析保存和新增按钮
This commit is contained in:
parent
1c7784702f
commit
1c6bd01ed6
|
|
@ -23,7 +23,6 @@
|
||||||
#include "EnergyScaleDataModel.h"
|
#include "EnergyScaleDataModel.h"
|
||||||
#include "DataCalcProcess/CoincidenceSpectrumProcess.h"
|
#include "DataCalcProcess/CoincidenceSpectrumProcess.h"
|
||||||
#include "BackgroundTaskListModel.h"
|
#include "BackgroundTaskListModel.h"
|
||||||
#include "GvfToCsv/GvfToCsv.h"
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
using namespace DataProcessWorkPool;
|
using namespace DataProcessWorkPool;
|
||||||
|
|
@ -1133,3 +1132,351 @@ bool GvfToCsvDataTask::processTask()
|
||||||
|
|
||||||
return m_conversionSuccess;
|
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"部分通道的能量计数数据写入失败"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,9 @@
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
|
#include <array>
|
||||||
|
#include "GvfToCsv/GvfToCsv.h"
|
||||||
|
|
||||||
class EnergyScaleDataModel;
|
class EnergyScaleDataModel;
|
||||||
class MeasureAnalysisProjectModel;
|
class MeasureAnalysisProjectModel;
|
||||||
|
|
||||||
|
|
@ -184,6 +187,71 @@ namespace DataProcessWorkPool
|
||||||
QString m_error;
|
QString m_error;
|
||||||
bool m_conversionSuccess = false;
|
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
|
#endif // DATAPROCESSWORKPOOL_H
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,10 @@ void EnergyCountPlotView::SetAnalyzeDataFilename(const QMap<QString, QVariant> &
|
||||||
for (const QString& ch_count_data_name : ch_count_data_name) {
|
for (const QString& ch_count_data_name : ch_count_data_name) {
|
||||||
if (ch_count_data_name.contains(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();
|
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);
|
loadDataFromFile(ch_count_data_name, ch_count_data_filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,29 @@ MainWindow::MainWindow(QWidget* parent)
|
||||||
initAction();
|
initAction();
|
||||||
initStatusBar();
|
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();
|
this->applyStyleSheet();
|
||||||
_s_main_win = this;
|
_s_main_win = this;
|
||||||
}
|
}
|
||||||
|
|
@ -612,19 +635,27 @@ void MainWindow::onRunningInfo(const QString &run_info)
|
||||||
|
|
||||||
void MainWindow::onGvfData(const QByteArray &data)
|
void MainWindow::onGvfData(const QByteArray &data)
|
||||||
{
|
{
|
||||||
QList<ParticleData> particles = _gvfToCsv->parseParticleFrames(data);
|
// QList<ParticleData> particles = _gvfToCsv->parseParticleFrames(data);
|
||||||
//处理粒子数据
|
// //处理粒子数据
|
||||||
changeParticleData(particles);
|
// changeParticleData(particles);
|
||||||
//处理道址计数
|
// //处理道址计数
|
||||||
changeChannelParticleCount(particles);
|
// changeChannelParticleCount(particles);
|
||||||
//处理能谱数据
|
// //处理能谱数据
|
||||||
changeParticleEnergyData(particles);
|
// changeParticleEnergyData(particles);
|
||||||
//处理能量计数
|
// //处理能量计数
|
||||||
changeEnergyCountData(particles);
|
// changeEnergyCountData(particles);
|
||||||
//处理道址计数谱
|
// //处理道址计数谱
|
||||||
changeAddressCountView(particles);
|
// changeAddressCountView(particles);
|
||||||
//处理能量计数谱
|
// //处理能量计数谱
|
||||||
changeEnergyCountView(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();
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include "GvfToCsv/GvfToCsv.h"
|
#include "GvfToCsv/GvfToCsv.h"
|
||||||
#include "MeasureAnalysisProjectModel.h"
|
#include "MeasureAnalysisProjectModel.h"
|
||||||
|
#include "DataProcessWorkPool.h"
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
@ -103,6 +104,13 @@ private slots:
|
||||||
//设备连接管理
|
//设备连接管理
|
||||||
void on_action_device_connect_cfg_triggered();
|
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:
|
private:
|
||||||
QMutex _mutex_info_output;
|
QMutex _mutex_info_output;
|
||||||
QPlainTextEdit* _plain_edit_info_output;
|
QPlainTextEdit* _plain_edit_info_output;
|
||||||
|
|
@ -143,5 +151,8 @@ private:
|
||||||
//能量计数视图定时器
|
//能量计数视图定时器
|
||||||
QTimer* m_EnergyCountTimer;
|
QTimer* m_EnergyCountTimer;
|
||||||
|
|
||||||
|
QThread *_gvfProcessThread = nullptr;
|
||||||
|
DataProcessWorkPool::RealtimeGvfDataWorker *_gvfWorker = nullptr;
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|
|
||||||
|
|
@ -314,6 +314,7 @@ void NuclideAnalysisView::onActionPlotClear()
|
||||||
_plot->replot();
|
_plot->replot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void NuclideAnalysisView::addVerticalLine(double xValue, const QColor& color)
|
void NuclideAnalysisView::addVerticalLine(double xValue, const QColor& color)
|
||||||
{
|
{
|
||||||
if(!m_bSelectHS)
|
if(!m_bSelectHS)
|
||||||
|
|
@ -637,6 +638,18 @@ void NuclideAnalysisView::setupNuclideDialog()
|
||||||
QVBoxLayout* layout = new QVBoxLayout(m_nuclideDialog);
|
QVBoxLayout* layout = new QVBoxLayout(m_nuclideDialog);
|
||||||
layout->setContentsMargins(6, 6, 6, 6);
|
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);
|
m_nuclideTable = new QTableWidget(m_nuclideDialog);
|
||||||
QStringList headers = { "核素", "射线类型", "能量(keV)","操作" };
|
QStringList headers = { "核素", "射线类型", "能量(keV)","操作" };
|
||||||
m_nuclideTable->setColumnCount(headers.size());
|
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, 0, new QTableWidgetItem(m.nuclideName));
|
||||||
m_nuclideTable->setItem(i, 1, new QTableWidgetItem(m.rayType));
|
m_nuclideTable->setItem(i, 1, new QTableWidgetItem(m.rayType));
|
||||||
m_nuclideTable->setItem(i, 2, new QTableWidgetItem(m.energyDisplay));
|
m_nuclideTable->setItem(i, 2, new QTableWidgetItem(m.energyDisplay));
|
||||||
|
createRowOperationWidgets(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matches.isEmpty()) {
|
if (matches.isEmpty()) {
|
||||||
|
|
@ -772,3 +786,57 @@ bool NuclideAnalysisView::openNuclideDatabase()
|
||||||
}
|
}
|
||||||
return db.openDatabase(dbPath, "nuclide_conn");
|
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()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QTableWidget>
|
#include <QTableWidget>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QHBoxLayout>
|
||||||
#include "sqlitemanager.h"
|
#include "sqlitemanager.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -76,6 +79,8 @@ private:
|
||||||
void queryNuclidesForPeak(const PeakFitResultNuclide& peak);
|
void queryNuclidesForPeak(const PeakFitResultNuclide& peak);
|
||||||
bool openNuclideDatabase();
|
bool openNuclideDatabase();
|
||||||
|
|
||||||
|
void createRowOperationWidgets(int row);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onActionPlotConfigure();
|
void onActionPlotConfigure();
|
||||||
|
|
||||||
|
|
@ -84,6 +89,11 @@ private slots:
|
||||||
|
|
||||||
void onActionPlotClear();
|
void onActionPlotClear();
|
||||||
|
|
||||||
|
|
||||||
|
void onDeleteRow();
|
||||||
|
void onSaveSelectedNuclides();
|
||||||
|
void onAddNuclide();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CustomQwtPlot* _plot = nullptr;
|
CustomQwtPlot* _plot = nullptr;
|
||||||
QString _workspace;
|
QString _workspace;
|
||||||
|
|
@ -109,5 +119,8 @@ private:
|
||||||
|
|
||||||
QDialog* m_nuclideDialog = nullptr;
|
QDialog* m_nuclideDialog = nullptr;
|
||||||
QTableWidget* m_nuclideTable = nullptr;
|
QTableWidget* m_nuclideTable = nullptr;
|
||||||
|
QPushButton* m_saveBtn = nullptr;
|
||||||
|
QPushButton* m_addBtn = nullptr; // 新增核素按钮
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif // NUCLIDEANALYSISVIEW_H
|
#endif // NUCLIDEANALYSISVIEW_H
|
||||||
Loading…
Reference in New Issue
Block a user