修改道址计数问题,新增能谱数据处理

This commit is contained in:
anxinglong 2026-06-11 18:47:15 +08:00
parent 02cb0839b9
commit 9a8a102dcf
4 changed files with 291 additions and 144 deletions

View File

@ -20,7 +20,6 @@
#include "DataProcessWorkPool.h" #include "DataProcessWorkPool.h"
#include "EnergyScaleForm.h" #include "EnergyScaleForm.h"
#include "MeasureAnalysisHistoryForm.h" #include "MeasureAnalysisHistoryForm.h"
#include "MeasureAnalysisProjectModel.h"
#include "NewMeasureAnalysisDlg.h" #include "NewMeasureAnalysisDlg.h"
#include "MeasureAnalysisView.h" #include "MeasureAnalysisView.h"
#include "MeasureAnalysisTreeView.h" #include "MeasureAnalysisTreeView.h"
@ -33,6 +32,8 @@
#include <QJsonObject> #include <QJsonObject>
#include "MeasureClient.h" #include "MeasureClient.h"
#include "MeasureAnalysisDataTableView.h" #include "MeasureAnalysisDataTableView.h"
#include "DataCalcProcess/GaussPolyCoe.h"
#include "EnergyScaleDataModel.h"
#include "csv.h" #include "csv.h"
#include <fstream> #include <fstream>
@ -388,6 +389,7 @@ void MainWindow::closeProject(const QString& project_name)
void MainWindow::showEvent(QShowEvent *event) void MainWindow::showEvent(QShowEvent *event)
{ {
QMainWindow::showEvent(event); QMainWindow::showEvent(event);
@ -468,8 +470,8 @@ void MainWindow::on_action_start_measure_triggered()
} }
MeasureAnalysisProjectModel* models = ProjectList::Instance()->GetCurrentProjectModel(); MeasureAnalysisProjectModel* models = ProjectList::Instance()->GetCurrentProjectModel();
QMap<QString, QMap<QString, QStandardItem *> > project_node_items = ProjectList::Instance()->getProjectNodeItems(); QMap<QString, QMap<QString, QStandardItem *> > project_node_items = ProjectList::Instance()->getProjectNodeItems();
QMap<QString, QStandardItem *> node = project_node_items[models->GetProjectName()]; nodeMap = project_node_items[models->GetProjectName()];
QStandardItem *nodeItem = node[models->GetProjectName()]; QStandardItem *nodeItem = nodeMap[models->GetProjectName()];
ProjectList::Instance()->SetNodeStatus(nodeItem,"测量中",true); ProjectList::Instance()->SetNodeStatus(nodeItem,"测量中",true);
QString dir = ProjectList::Instance()->GetCurrentProjectModel()->GetProjectDir(); QString dir = ProjectList::Instance()->GetCurrentProjectModel()->GetProjectDir();
@ -479,7 +481,7 @@ void MainWindow::on_action_start_measure_triggered()
QString status = status_ok ? QStringLiteral(u"有效") : QStringLiteral(u"无效"); QString status = status_ok ? QStringLiteral(u"有效") : QStringLiteral(u"无效");
QVariant analys_type = QVariant::fromValue(AnalysisType::ParticleData); QVariant analys_type = QVariant::fromValue(AnalysisType::ParticleData);
QString item_name = QStringLiteral(u"测量粒子数据"); QString item_name = QStringLiteral(u"测量粒子数据");
QStandardItem * particleData = node[item_name]; QStandardItem * particleData = nodeMap[item_name];
ProjectList::Instance()->SetNodeStatus(particleData,status,true); ProjectList::Instance()->SetNodeStatus(particleData,status,true);
//创建通道道址计数文件夹 //创建通道道址计数文件夹
const QString& all_channel_particle_data_filename = models->GetAllChannelParticleDataFilename(); const QString& all_channel_particle_data_filename = models->GetAllChannelParticleDataFilename();
@ -601,7 +603,20 @@ 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);
if (particles.isEmpty()) { //处理粒子数据
changeParticleData(particles);
//处理道址计数
changeChannelParticleCount(particles);
//处理能谱数据
changeParticleEnergyData(particles);
//处理能量计数
changeEnergyCountData(particles);
}
//处理粒子数据
void MainWindow::changeParticleData(QList<ParticleData> &dataList)
{
if (dataList.isEmpty()) {
LOG_INFO(QStringLiteral(u"本次GVF数据未解析到有效粒子跳过写入CSV")); LOG_INFO(QStringLiteral(u"本次GVF数据未解析到有效粒子跳过写入CSV"));
return; return;
} }
@ -625,9 +640,9 @@ void MainWindow::onGvfData(const QByteArray &data)
} }
QString csvBuffer; QString csvBuffer;
csvBuffer.reserve(particles.size() * 64); // 单条数据约64字节预分配足够内存 csvBuffer.reserve(dataList.size() * 64); // 单条数据约64字节预分配足够内存
for (const auto &p : particles) { for (const auto &p : dataList) {
csvBuffer += QString("%1,%2,%3,%4\n") csvBuffer += QString("%1,%2,%3,%4\n")
.arg(p.boardId) .arg(p.boardId)
.arg(p.channelId) .arg(p.channelId)
@ -638,126 +653,283 @@ void MainWindow::onGvfData(const QByteArray &data)
out << csvBuffer; out << csvBuffer;
out.flush(); // 确保数据立即写入磁盘,避免程序崩溃丢失数据 out.flush(); // 确保数据立即写入磁盘,避免程序崩溃丢失数据
outFile.close(); outFile.close();
//处理粒子数据 updataTable();
changeUpdata(particles);
//处理道址计数
changeChannelParticleCount(particles);
} }
void MainWindow::changeUpdata(QList<ParticleData> &data) void MainWindow::updataTable()
{ {
QString dir = ProjectList::Instance()->GetCurrentProjectModel()->GetProjectDir();
QString csvPath = QStringLiteral(u"%1/%2").arg(dir).arg("粒子数据.csv");
auto dockList = _dock_manager->dockWidgetsMap().values(); auto dockList = _dock_manager->dockWidgetsMap().values();
int i = 0;
for(auto dock : dockList) for(auto dock : dockList)
{ {
MeasureAnalysisView* view = dynamic_cast<MeasureAnalysisView*>(dock->widget()); MeasureAnalysisView* view = dynamic_cast<MeasureAnalysisView*>(dock->widget());
if(!view) continue; if(!view) continue;
if(view->GetAnalyzeType() == AnalysisType::ParticleData if(view->GetViewType() == MeasureAnalysisView::DataTable)
&& view->GetViewType() == MeasureAnalysisView::DataTable)
{ {
MeasureAnalysisDataTableView* table = dynamic_cast<MeasureAnalysisDataTableView*>(view); switch(view->GetAnalyzeType())
QString boardId = QString::number(data.at(i).boardId); {
QString channelId = QString::number(data.at(i).channelId); case AnalysisType::ParticleData:{
QString address = QString::number(data.at(i).address); MeasureAnalysisDataTableView* table = dynamic_cast<MeasureAnalysisDataTableView*>(view);
QString timestampCount = QString::number(data.at(i).timestampCount); table->AppendRow();
QStringList dataList; };break;
dataList << boardId << channelId << address << timestampCount; // case AnalysisType::AddressCountData:
// {
// dataList << QString("%1,%2,%3,%4").arg(boardId).arg(channelId).arg(address).arg(timestampCount); // MeasureAnalysisDataTableView* table = dynamic_cast<MeasureAnalysisDataTableView*>(view);
table->AppendRow(dataList,false); // table->AppendRow();
i++; // };break;
case AnalysisType::ParticleEnergyData:
{
MeasureAnalysisDataTableView* table = dynamic_cast<MeasureAnalysisDataTableView*>(view);
table->AppendRow();
};break;
case AnalysisType::EnergyCountData:
{
MeasureAnalysisDataTableView* table = dynamic_cast<MeasureAnalysisDataTableView*>(view);
table->AppendRow();
};break;
}
} }
} }
} }
void MainWindow::changeChannelParticleCount(QList<ParticleData> &data) void MainWindow::changeChannelParticleCount(QList<ParticleData> &data)
{ {
bool ret_ok = true; if (data.isEmpty()) {
return;
}
// 通道号 -> 地址 -> 计数 QHash<uint, QHash<uint, unsigned long long>> deltaCounts; // 通道号 -> 道址 -> 增量
for(auto info : data) int totalParticles = 0;
{
// 板卡和通道号计算,通道号 = 板卡号 * 4 + 通道号 // 计算本次数据的增量计数
for (const auto &info : data) {
int channel_num = (info.boardId) * 4 + (info.channelId + 1); int channel_num = (info.boardId) * 4 + (info.channelId + 1);
// 统计每个通道的粒子计数 deltaCounts[channel_num][info.address]++;
if (!channel_address_counts.contains(channel_num)) { totalParticles++;
channel_address_counts[channel_num] = QMap<uint, unsigned long long>(); }
}
channel_address_counts[channel_num][info.address]++; if (deltaCounts.isEmpty()) {
return;
} }
MeasureAnalysisProjectModel* models = ProjectList::Instance()->GetCurrentProjectModel(); MeasureAnalysisProjectModel* models = ProjectList::Instance()->GetCurrentProjectModel();
if (!models) {
LOG_ERROR(QStringLiteral(u"当前没有打开的测量项目,无法写入道址计数数据"));
return;
}
const QString& projectName = models->GetProjectName();
const QString& every_ch_count_dir = QDir(models->GetProjectDir()).filePath(QStringLiteral(u"通道道址计数")); const QString& every_ch_count_dir = QDir(models->GetProjectDir()).filePath(QStringLiteral(u"通道道址计数"));
QDir every_ch_count_output_dir(every_ch_count_dir); QDir every_ch_count_output_dir(every_ch_count_dir);
// 写入每个通道的粒子计数数据(优化:使用一次打开文件,批量写入) if (!every_ch_count_output_dir.exists()) {
QMap<uint, std::shared_ptr<std::ofstream>> channel_file_streams; if (!every_ch_count_output_dir.mkpath(every_ch_count_dir)) {
// 预创建所有通道的文件流 LOG_ERROR(QStringLiteral(u"无法创建通道道址计数目录: %1").arg(every_ch_count_dir));
for (auto channel_it = channel_address_counts.begin(); channel_it != channel_address_counts.end(); ++channel_it) { return;
uint channel_num = channel_it.key();
QString count_data_filename = every_ch_count_output_dir.filePath(QStringLiteral(u"通道%1粒子计数.csv").arg(channel_num));
particle_count_filename_list.insert(channel_num, count_data_filename);
// 创建文件流
std::shared_ptr<std::ofstream> out(new std::ofstream(QStrToSysPath(count_data_filename)));
channel_file_streams[channel_num] = out;
*out << QString(QStringLiteral(u"道址")).toStdString() << "," << QString(QStringLiteral(u"计数")).toStdString() << std::endl;
}
// 批量写入数据
for (auto channel_it = channel_address_counts.begin(); channel_it != channel_address_counts.end(); ++channel_it) {
uint channel_num = channel_it.key();
const QMap<uint, unsigned long long>& address_counts = channel_it.value();
auto out_stream = channel_file_streams[channel_num];
for (auto address_it = address_counts.begin(); address_it != address_counts.end(); ++address_it) {
uint address = address_it.key();
unsigned long long count = address_it.value();
*out_stream << address << "," << count << std::endl;
} }
} }
channel_file_streams.clear();
const QString& project_name = models->GetProjectName(); QMutexLocker locker(&m_channelCountMutex);
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
if (project_model == nullptr) { bool hasError = false;
ret_ok = false; QHash<uint, QString> newChannelFiles; // 本次新增的通道文件
} else {
// 更新项目模型中的通道粒子计数数据文件名 // 处理每个通道的计数更新
for (auto it = particle_count_filename_list.begin(); it != particle_count_filename_list.end(); ++it) { for (auto channelIt = deltaCounts.constBegin(); channelIt != deltaCounts.constEnd(); ++channelIt) {
project_model->SetChannelAddressCountDataFilename(it.key(), it.value()); uint channel_num = channelIt.key();
const auto& addressDeltas = channelIt.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 = every_ch_count_output_dir.filePath(QStringLiteral(u"通道%1粒子计数.csv").arg(channel_num));
models->SetChannelAddressCountDataFilename(channel_num, count_data_filename);
particle_count_filename_list.insert(channel_num, count_data_filename);
newChannelFiles.insert(channel_num, count_data_filename);
} }
QMap<QString, QMap<QString, QStandardItem *> > project_node_items = ProjectList::Instance()->getProjectNodeItems(); for (auto addressIt = addressDeltas.constBegin(); addressIt != addressDeltas.constEnd(); ++addressIt) {
QMap<QString, QStandardItem *> node_map = project_node_items[models->GetProjectName()]; uint address = addressIt.key();
unsigned long long delta = addressIt.value();
channel_address_counts[channel_num][address] += delta;
}
QFile outFile(count_data_filename);
if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
LOG_ERROR(QStringLiteral(u"无法打开通道%1的计数文件: %2错误: %3")
.arg(channel_num)
.arg(count_data_filename)
.arg(outFile.errorString()));
hasError = true;
continue;
}
QTextStream out(&outFile);
out.setCodec("UTF-8");
// 写入表头
out << QStringLiteral(u"道址,计数\n");
// 写入所有道址的最新总计数(按道址排序)
QString csvBuffer;
csvBuffer.reserve(channel_address_counts[channel_num].size() * 32); // 预分配内存
// 获取排序后的道址列表
QList<uint> sortedAddresses = channel_address_counts[channel_num].keys();
std::sort(sortedAddresses.begin(), sortedAddresses.end());
for (uint address : sortedAddresses) {
unsigned long long total = channel_address_counts[channel_num][address];
csvBuffer += QString("%1,%2\n").arg(address).arg(total);
}
out << csvBuffer;
out.flush(); // 确保数据立即写入磁盘
outFile.close();
}
// 更新项目树节点状态
if (!newChannelFiles.isEmpty() || !hasError) {
for (auto it = newChannelFiles.constBegin(); it != newChannelFiles.constEnd(); ++it) {
models->SetChannelAddressCountDataFilename(it.key(), it.value());
}
const QString& adrr_count_item_name = QStringLiteral(u"道址计数"); const QString& adrr_count_item_name = QStringLiteral(u"道址计数");
const QMap<uint, QString>& filename_list = models->GetChannelAddressCountDataFilenameList(); if (nodeMap.contains(adrr_count_item_name)) {
bool status_ok = false; auto adrr_count_item = nodeMap[adrr_count_item_name];
QString status = QStringLiteral(u"无效"); bool status_ok = !particle_count_filename_list.isEmpty();
if (!filename_list.isEmpty()) { QString status = status_ok ? QStringLiteral(u"有效") : QStringLiteral(u"无效");
status_ok = true;
status = QStringLiteral(u"有效");
}
if (node_map.contains(adrr_count_item_name)) {
auto adrr_count_item = node_map[adrr_count_item_name];
ProjectList::Instance()->SetNodeStatus(adrr_count_item, status, status_ok); ProjectList::Instance()->SetNodeStatus(adrr_count_item, status, status_ok);
for (auto it = filename_list.begin(); it != filename_list.end(); ++it) {
for (auto it = newChannelFiles.constBegin(); it != newChannelFiles.constEnd(); ++it) {
uint ch_num = it.key(); uint ch_num = it.key();
QString item_name = QStringLiteral(u"通道%1道址计数").arg(ch_num); QString item_name = QStringLiteral(u"通道%1道址计数").arg(ch_num);
if(node_map.contains(item_name)) if (nodeMap.contains(item_name)) {
{ continue; // 节点已存在,跳过
return;
} }
models->SaveProjectModel();
const QVariant& analys_type = QVariant::fromValue(AnalysisType::AddressCountData); 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); QStandardItem* node_item = ProjectList::Instance()->AddChildNode(
node_item->setData(project_name, Qt::UserRole + 2); adrr_count_item, item_name, status, analys_type, true, status_ok);
node_item->setData(projectName, Qt::UserRole + 2);
node_item->setData(ch_num, Qt::UserRole + 3); node_item->setData(ch_num, Qt::UserRole + 3);
node_map[item_name] = node_item; nodeMap[item_name] = node_item;
} }
} }
} }
// updataTable();
if (hasError) {
LOG_WARN(QStringLiteral(u"部分通道的道址计数数据写入失败,请检查磁盘空间和文件权限"));
}
}
//处理粒子能量数据
void MainWindow::changeParticleEnergyData(QList<ParticleData> &dataList)
{
if (dataList.isEmpty()) {
return;
}
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetCurrentProjectModel();
if (!project_model) {
LOG_ERROR(QStringLiteral(u"当前没有打开的测量项目,无法处理能谱数据"));
return;
}
const QString& project_name = project_model->GetProjectName();
EnergyScaleDataModel energy_scale_data_model(project_model->GetEnergyScaleFilename());
if (!energy_scale_data_model.LoadData()) {
LOG_WARN(QStringLiteral(u"[%1]加载能量刻度文件失败,跳过本次能谱处理").arg(project_name));
return;
}
if (!energy_scale_data_model.IsValid()) {
LOG_WARN(QStringLiteral(u"[%1]能量刻度数据无效,请检查刻度配置").arg(project_name));
return;
}
QMutexLocker locker(&m_energyDataMutex);
QString energy_spectrum_filename = QDir(project_model->GetProjectDir()).filePath(QStringLiteral(u"能谱数据.csv"));
QFile outFile(energy_spectrum_filename);
if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
LOG_ERROR(QStringLiteral(u"[%1]无法打开能谱文件: %2错误: %3")
.arg(project_name)
.arg(energy_spectrum_filename)
.arg(outFile.errorString()));
return;
}
QTextStream out(&outFile);
out.setCodec("UTF-8");
if ( outFile.size() == 0) {
out << QStringLiteral(u"板卡号,通道号,道址,能量(KeV),时间计数\n");
LOG_DEBUG(QStringLiteral(u"[%1]能谱文件为空,已写入标准表头").arg(project_name));
}
QString csvBuffer;
csvBuffer.reserve(dataList.size() * 80); // 单条数据约80字节预分配足够内存
int totalProcessed = dataList.size();
int successCount = 0;
int skipCount = 0;
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()) {
skipCount++;
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);
successCount++;
}
if (!csvBuffer.isEmpty()) {
out << csvBuffer;
out.flush(); // 强制写入磁盘,防止程序崩溃丢失数据
}
outFile.close();
if (successCount > 0) {
project_model->SetParticleEnergyDataFilename(energy_spectrum_filename);
if (!project_model->SaveProjectModel()) {
LOG_WARN(QStringLiteral(u"[%1]保存项目模型失败,能谱文件路径未持久化").arg(project_name));
}
const QString& energy_node_name = QStringLiteral(u"粒子能量数据");
QMap<QString, QMap<QString, QStandardItem *>> all_nodes = ProjectList::Instance()->getProjectNodeItems();
QMap<QString, QStandardItem *> project_nodes = all_nodes[project_name];
if (project_nodes.contains(energy_node_name)) {
QStandardItem* energy_node = project_nodes[energy_node_name];
ProjectList::Instance()->SetNodeStatus(energy_node, QStringLiteral(u"有效"), true);
}
}
updataTable();
}
//处理能量计数
void MainWindow::changeEnergyCountData(QList<ParticleData> &dataList)
{
} }

View File

@ -4,6 +4,7 @@
#include <QMainWindow> #include <QMainWindow>
#include <QMutex> #include <QMutex>
#include "GvfToCsv/GvfToCsv.h" #include "GvfToCsv/GvfToCsv.h"
#include "MeasureAnalysisProjectModel.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace Ui { namespace Ui {
@ -52,10 +53,19 @@ private:
void initStatusBar(); void initStatusBar();
void applyStyleSheet(); void applyStyleSheet();
void closeProject(const QString &project_name); void closeProject(const QString &project_name);
//更新表格
void updataTable();
//处理粒子数据 //处理粒子数据
void changeUpdata(QList<ParticleData> &data); void changeParticleData(QList<ParticleData> &dataList);
//处理道址计数 //处理道址计数
void changeChannelParticleCount(QList<ParticleData> &data); void changeChannelParticleCount(QList<ParticleData> &data);
//处理粒子能量数据
void changeParticleEnergyData(QList<ParticleData> &dataList);
//处理能量计数
void changeEnergyCountData(QList<ParticleData> &dataList);
signals: signals:
void newProject(const QString &project_name); void newProject(const QString &project_name);
@ -107,8 +117,16 @@ private:
QMap<uint, QMap<uint, unsigned long long>> channel_address_counts; // 通道号 -> 地址 -> 计数 QMap<uint, QMap<uint, unsigned long long>> channel_address_counts; // 通道号 -> 地址 -> 计数
QMap<uint, QString> particle_count_filename_list; QMap<uint, QString> particle_count_filename_list;
//2026-06-11
QMap<QString, QStandardItem *> nodeMap;
QMutex m_channelCountMutex;//
QMutex m_energyDataMutex;//能量谱锁
//能量计数
QHash<uint, QString> m_channelEnergyCountFiles; // 通道号 -> 能量计数文件路径
QHash<uint, QMap<double, unsigned long long>> m_channelEnergyStats; // 通道号 -> 能量bin -> 计数
QMap<double, unsigned long long> m_allChannelEnergyStats; // 全通道能量bin -> 计数
QMutex m_energyCountMutex; // 保护能量计数数据的互斥锁
QHash<QString, std::vector<double>> m_energyScaleCoeffCache;// 能量刻度系数缓存
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View File

@ -95,64 +95,21 @@ void MeasureAnalysisDataTableView::RefreshTableData(const QString &csvFilePath)
_tableView->setBufferSize(_buffer_size); _tableView->setBufferSize(_buffer_size);
} }
void MeasureAnalysisDataTableView::AppendRow(const QVariantList &rowData, bool writeToFile) void MeasureAnalysisDataTableView::AppendRow(/*const QVariantList &rowData, bool writeToFile*/)
{ {
// 1. 前置校验
if (!_tableModel || !_tableModel->dataSource()) {
LOG_WARN(QStringLiteral(u"追加行失败:表格模型或数据源未初始化"));
return;
}
auto dataSource = std::dynamic_pointer_cast<CsvDataSource>(_tableModel->dataSource()); auto dataSource = std::dynamic_pointer_cast<CsvDataSource>(_tableModel->dataSource());
if (!dataSource || !dataSource->isValid()) {
LOG_WARN(QStringLiteral(u"追加行失败CSV数据源无效"));
return;
}
// 2. 列数匹配
const int expectedColumns = _tableModel->columnCount();
if (rowData.size() != expectedColumns) {
LOG_WARN(QStringLiteral(u"追加行失败:列数不匹配,期望%1列实际%2列")
.arg(expectedColumns).arg(rowData.size()));
return;
}
//需要写入文件的
if (writeToFile) {
QFile file(dataSource->filePath());
QTextStream out(&file);
out.setCodec("UTF-8"); // 与读取时的 QString::fromUtf8 保持一致
QStringList escapedFields;
for (const QVariant& field : rowData) {
escapedFields.append(escapeCsvField(field.toString()));
}
out << escapedFields.join(',') << "\n";
file.close();
LOG_DEBUG(QStringLiteral(u"已成功将新行写入CSV文件%1").arg(dataSource->filePath()));
RefreshTableData(dataSource->filePath());
return;
}
QFile file(dataSource->filePath());
if (!file.open(QIODevice::Append | QIODevice::Text)) {
LOG_ERROR(QStringLiteral(u"追加行失败:无法打开文件 %1错误%2")
.arg(file.fileName()).arg(file.errorString()));
return;
}
// 4. 重新加载整个表格,确保模型与文件同步
RefreshTableData(dataSource->filePath()); RefreshTableData(dataSource->filePath());
// 5. 自动滚动到底部
_tableView->scrollToBottom(); _tableView->scrollToBottom();
} }
void MeasureAnalysisDataTableView::AppendRow(const QStringList &rowData, bool writeToFile) // void MeasureAnalysisDataTableView::AppendRow(/*const QStringList &rowData, bool writeToFile*/)
{ // {
QVariantList varList; // // QVariantList varList;
for (const QString& field : rowData) { // // for (const QString& field : rowData) {
varList.append(QVariant(field)); // // varList.append(QVariant(field));
} // // }
AppendRow(varList, writeToFile); // AppendRow(varList, writeToFile);
} // }

View File

@ -23,8 +23,8 @@ public:
// 2026-06-10 表尾插入数据函数支持两种参数类型默认同时写入CSV文件 // 2026-06-10 表尾插入数据函数支持两种参数类型默认同时写入CSV文件
void AppendRow(const QVariantList& rowData, bool writeToFile = true); void AppendRow(/*const QVariantList& rowData, bool writeToFile = true*/);
void AppendRow(const QStringList& rowData, bool writeToFile = true); // void AppendRow(const QStringList& rowData, bool writeToFile = true);
private: private:
// 私有成员变量 // 私有成员变量
VirtualTableView *_tableView; VirtualTableView *_tableView;