EnergySpectrumAnalyer/src/DeviceParameterConfig/DeviceConfigView.cpp
2026-03-30 18:06:52 +08:00

464 lines
16 KiB
C++

#include "DeviceConfigView.h"
#include "DeviceParameterProxy.h"
#include <QTableView>
#include <QStandardItemModel>
#include <QHeaderView>
#include <QPushButton>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QMessageBox>
#include <QDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QListWidget>
#include <QDialogButtonBox>
#include <QLabel>
#include <QScrollArea>
#include <QCheckBox>
#include <QFileDialog>
#include <QStyleOptionButton>
#include <GlobalDefine.h>
#include <QObject>
#include <QDebug>
class DeviceConfigViewModel: public QStandardItemModel
{
public:
DeviceConfigViewModel(QObject *parent = nullptr) : QStandardItemModel(parent){}
virtual Qt::ItemFlags flags(const QModelIndex &index) const {
Qt::ItemFlags f = QStandardItemModel::flags(index);
if ( index.column() == 0 ) {
f = f & Qt::ItemIsUserCheckable;
}
return f;
}
};
DeviceConfigView::DeviceConfigView(QWidget *parent)
: MeasureAnalysisView(parent)
, m_tableView(nullptr)
, m_model(nullptr)
{
initTableView();
}
DeviceConfigView::~DeviceConfigView()
{
}
void DeviceConfigView::initTableView()
{
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
m_tableView = new QTableView(this);
layout->addWidget(m_tableView);
// 设置表头
QStringList headList;
headList << tr("数据传输模式") << tr("硬件增益") << tr("硬件增益选择索引")
<< tr("软件增益") << tr("时间常数") << tr("成形时间") << tr("快通道触发阈值")
<< tr("多道分辨率") << tr("控制功能") << tr("幅度提前延迟") << tr("积分长度")
<< tr("dcfd缩放倍数") << tr("dcfd延迟") << tr("直流偏移") << tr("最大能量范围")
<< tr("Am 峰面积寻峰法的面积比例") << tr("K40与 Am241的峰位校正系数") << tr("高压关断阈值")
<< tr("获取能谱周期") << tr("总测量时间") << tr("总测量次数") << tr("滤波参数")
<< tr("上升时间") << tr("平顶时间") << tr("ICR 校正使能") << tr("CRZA 值")
<< tr("ZA 使能值") << tr("操作");
m_model = new DeviceConfigViewModel(this);
m_model->setColumnCount(headList.size());
m_model->setHorizontalHeaderLabels(headList);
m_model->setRowCount(32);
for (int i = 0; i < 32; ++i) {
QString rowName = tr("通道%1").arg(i + 1);
m_model->setHeaderData(i, Qt::Vertical, rowName, Qt::DisplayRole);
}
// 操作列最后一列不可编辑
int actionCol = headList.size() - 1;
for (int row = 0; row < 32; ++row) {
QStandardItem *item = new QStandardItem(QString());
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
m_model->setItem(row, actionCol, item);
}
m_tableView->setModel(m_model);
m_tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Interactive);
m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
m_tableView->setEditTriggers(QAbstractItemView::DoubleClicked);
// 设置代理
DeviceParameterProxy *proxy = new DeviceParameterProxy(this);
m_tableView->setItemDelegate(proxy);
setupActionColumnButtons();
}
void DeviceConfigView::setupActionColumnButtons()
{
int actionCol = m_model->columnCount() - 1;
for (int row = 0; row < m_model->rowCount(); ++row) {
QModelIndex idx_0 = m_model->index(row, 0);
QModelIndex idx = m_model->index(row, actionCol);
QPushButton *btn = new QPushButton(tr("应用"), m_tableView);
btn->setProperty("row", row);
connect(btn, &QPushButton::clicked, this, &DeviceConfigView::onActionButtonClicked);
m_tableView->setIndexWidget(idx, btn);
}
}
void DeviceConfigView::onActionButtonClicked()
{
QPushButton *btn = qobject_cast<QPushButton*>(sender());
if (!btn) return;
int srcRow = btn->property("row").toInt();
int srcChannel = srcRow + 1;
QList<int> targetChannels = selectTargetChannels(srcChannel);
if (targetChannels.isEmpty()) return;
int paramColCount = m_model->columnCount() - 1;
m_model->blockSignals(true);
for (int ch : targetChannels) {
int dstRow = ch - 1;
if (dstRow == srcRow) continue;
for (int col = 0; col < paramColCount; ++col) {
QVariant srcData = m_model->data(m_model->index(srcRow, col));
m_model->setData(m_model->index(dstRow, col), srcData);
}
}
m_model->blockSignals(false);
emit m_model->dataChanged(m_model->index(0, 0), m_model->index(m_model->rowCount() - 1, paramColCount - 1));
QMessageBox::information(this, tr("提示"),
tr("已成功将通道%1的参数应用到 %2 个通道").arg(srcChannel).arg(targetChannels.size()));
}
QList<int> DeviceConfigView::selectTargetChannels(int currentChannel) const
{
QDialog dlg(const_cast<DeviceConfigView*>(this));
dlg.setWindowTitle(tr("选择目标通道"));
dlg.resize(300, 400);
QVBoxLayout *layout = new QVBoxLayout(&dlg);
QLabel *label = new QLabel(tr("请选择要应用参数的目标通道(可多选):"));
layout->addWidget(label);
QListWidget *listWidget = new QListWidget(&dlg);
for (int i = 1; i <= 32; ++i) {
QString itemText = tr("通道 %1").arg(i);
if (i == currentChannel) itemText += tr(" (源通道)");
QListWidgetItem *item = new QListWidgetItem(itemText, listWidget);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Unchecked);
item->setData(Qt::UserRole, i);
if (i == currentChannel) {
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
}
}
layout->addWidget(listWidget);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dlg);
layout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
QList<int> selected;
if (dlg.exec() == QDialog::Accepted) {
for (int i = 0; i < listWidget->count(); ++i) {
QListWidgetItem *item = listWidget->item(i);
if (item->checkState() == Qt::Checked) {
int ch = item->data(Qt::UserRole).toInt();
if (ch != currentChannel) selected.append(ch);
}
}
}
return selected;
}
bool DeviceConfigView::loadJson(const QString &filePath)
{
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "无法打开文件:" << file.errorString();
return false;
}
QByteArray jsonData = file.readAll();
file.close();
return parseJsonImpl(jsonData);
}
bool DeviceConfigView::parseJsonImpl(const QByteArray &data)
{
QJsonParseError parseError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error != QJsonParseError::NoError) {
qWarning() << "JSON 解析错误:" << parseError.errorString();
return false;
}
if (!jsonDoc.isObject()) {
qWarning() << "JSON 文档不是对象";
return false;
}
QJsonObject rootObj = jsonDoc.object();
QJsonArray channelsArray = rootObj["channels"].toArray();
m_model->blockSignals(true);
// 清空现有数据
for (int row = 0; row < m_model->rowCount(); ++row) {
for (int col = 0; col < m_model->columnCount(); ++col) {
m_model->setData(m_model->index(row, col), QVariant());
}
}
int count = qMin(channelsArray.size(), m_model->rowCount());
for (int i = 0; i < count; ++i) {
QJsonValue channelValue = channelsArray[i];
if (!channelValue.isObject()) continue;
QJsonObject channelObj = channelValue.toObject();
for (int col = 0; col < m_model->columnCount(); ++col) {
QString header = m_model->headerData(col, Qt::Horizontal).toString();
QString jsonKey = m_columnToJsonKey.value(header);
if (jsonKey.isEmpty()) continue;
QJsonValue value = channelObj[jsonKey];
if (value.isUndefined()) continue;
if (value.isString())
m_model->setData(m_model->index(i, col), value.toString());
else if (value.isDouble())
m_model->setData(m_model->index(i, col), value.toDouble());
else if (value.isBool())
m_model->setData(m_model->index(i, col), value.toBool());
}
}
emit m_model->dataChanged(m_model->index(0, 0), m_model->index(m_model->rowCount() - 1, m_model->columnCount() - 1));
emit dataChanged();
m_tableView->viewport()->update();
m_tableView->horizontalHeader()->update();
m_tableView->verticalHeader()->update();
return true;
}
bool DeviceConfigView::saveJson(const QString &filePath)
{
QJsonObject rootObj;
rootObj["command"] = "SET";
QJsonArray channelsArray;
for (int row = 0; row < m_model->rowCount(); ++row) {
QJsonObject channelObj;
for (int col = 0; col < m_model->columnCount(); ++col) {
QString header = m_model->headerData(col, Qt::Horizontal).toString();
QString jsonKey = m_columnToJsonKey.value(header);
if (jsonKey.isEmpty()) continue;
QVariant value = m_model->index(row, col).data(Qt::EditRole);
if (!value.isValid() || value.isNull()) continue;
channelObj[jsonKey] = QJsonValue::fromVariant(value);
}
channelsArray.append(channelObj);
}
rootObj["channels"] = channelsArray;
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "无法创建文件:" << file.errorString();
return false;
}
QJsonDocument doc(rootObj);
file.write(doc.toJson());
file.close();
return true;
}
QStandardItemModel* DeviceConfigView::getModel() const
{
return m_model;
}
void DeviceConfigView::showColumnControlDialog()
{
int colCount = m_model->columnCount();
if (colCount == 0) return;
QDialog dlg(this);
dlg.setWindowTitle(tr("列显示控制"));
dlg.resize(800, 400);
dlg.setWindowModality(Qt::WindowModal);
QVBoxLayout *mainLayout = new QVBoxLayout(&dlg);
// 工具栏
QHBoxLayout *row1Layout = new QHBoxLayout();
QPushButton *btnSelectAll = new QPushButton(tr("全选"));
QPushButton *btnInvert = new QPushButton(tr("反选"));
QPushButton *btnSave = new QPushButton(tr("保存配置"));
QPushButton *btnLoad = new QPushButton(tr("加载配置"));
row1Layout->addWidget(btnSelectAll);
row1Layout->addWidget(btnInvert);
row1Layout->addWidget(btnSave);
row1Layout->addWidget(btnLoad);
row1Layout->addStretch();
mainLayout->addLayout(row1Layout);
QScrollArea *scrollArea = new QScrollArea();
scrollArea->setWidgetResizable(true);
QWidget *scrollWidget = new QWidget();
QGridLayout *gridLayout = new QGridLayout(scrollWidget);
scrollArea->setWidget(scrollWidget);
mainLayout->addWidget(scrollArea);
int gridCols = std::sqrt(colCount);
if (gridCols < 1) gridCols = 1;
QMap<int, QCheckBox*> colCheckBoxMap;
for (int col = 0; col < colCount; ++col) {
QString header = m_model->headerData(col, Qt::Horizontal).toString();
QCheckBox *cb = new QCheckBox(header);
cb->setChecked(!m_tableView->isColumnHidden(col));
connect(cb, &QCheckBox::toggled, this, [this, col](bool checked) {
m_tableView->setColumnHidden(col, !checked);
});
int row = col / gridCols;
int colInGrid = col % gridCols;
gridLayout->addWidget(cb, row, colInGrid);
colCheckBoxMap[col] = cb;
}
connect(btnSelectAll, &QPushButton::clicked, this, [colCheckBoxMap, this]() {
// 先断开所有复选框的信号
for (QCheckBox *cb : colCheckBoxMap) {
cb->blockSignals(true);
cb->setChecked(true);
cb->blockSignals(false);
}
// 一次性更新所有列的隐藏状态
for (int col : colCheckBoxMap.keys()) {
m_tableView->setColumnHidden(col, !colCheckBoxMap[col]->isChecked());
}
});
connect(btnInvert, &QPushButton::clicked, this, [colCheckBoxMap, this]() {
for (QCheckBox *cb : colCheckBoxMap) {
cb->blockSignals(true);
cb->setChecked(!cb->isChecked());
cb->blockSignals(false);
}
for (int col : colCheckBoxMap.keys()) {
m_tableView->setColumnHidden(col, !colCheckBoxMap[col]->isChecked());
}
});
connect(btnSave, &QPushButton::clicked, this, [this, &dlg, colCheckBoxMap]() {
QString saveDir = QDir::currentPath() + "/columnConfigs";
QDir().mkpath(saveDir);
QString fileName = QFileDialog::getSaveFileName(&dlg, tr("保存列配置"),
saveDir + "/untitled.json",
"JSON Files (*.json)");
if (fileName.isEmpty()) return;
QJsonObject root;
for (int col = 0; col < m_model->columnCount(); ++col) {
QString header = m_model->headerData(col, Qt::Horizontal).toString();
bool checked = colCheckBoxMap[col]->isChecked();
root[header] = checked;
}
QJsonDocument doc(root);
QFile file(fileName);
if (file.open(QIODevice::WriteOnly)) {
file.write(doc.toJson());
file.close();
QMessageBox::information(&dlg, tr("成功"), tr("列配置已保存"));
} else {
QMessageBox::warning(&dlg, tr("错误"), tr("无法保存文件:%1").arg(file.errorString()));
}
});
connect(btnLoad, &QPushButton::clicked, this, [this, &dlg, colCheckBoxMap]() {
QString loadDir = QDir::currentPath() + "/columnConfigs";
QString fileName = QFileDialog::getOpenFileName(&dlg, tr("加载列配置"),
loadDir, "JSON Files (*.json)");
if (fileName.isEmpty()) return;
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
QMessageBox::warning(&dlg, tr("错误"), tr("无法打开文件:%1").arg(file.errorString()));
return;
}
QByteArray data = file.readAll();
file.close();
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
if (err.error != QJsonParseError::NoError || !doc.isObject()) {
QMessageBox::warning(&dlg, tr("错误"), tr("JSON解析失败"));
return;
}
QJsonObject root = doc.object();
for (int col = 0; col < m_model->columnCount(); ++col) {
QString header = m_model->headerData(col, Qt::Horizontal).toString();
if (root.contains(header)) {
colCheckBoxMap[col]->blockSignals(true);
colCheckBoxMap[col]->setChecked(root[header].toBool(false));
colCheckBoxMap[col]->blockSignals(false);
}
}
// 一次性更新列隐藏
for (int col : colCheckBoxMap.keys()) {
m_tableView->setColumnHidden(col, !colCheckBoxMap[col]->isChecked());
}
});
dlg.exec();
}
void DeviceConfigView::reset()
{
m_model->blockSignals(true);
for (int row = 0; row < m_model->rowCount(); ++row) {
for (int col = 0; col < m_model->columnCount(); ++col) {
m_model->setData(m_model->index(row, col), QVariant());
}
}
m_model->blockSignals(false);
emit m_model->dataChanged(m_model->index(0, 0), m_model->index(m_model->rowCount() - 1, m_model->columnCount() - 1));
}
void DeviceConfigView::setColumnMapping(const QHash<QString, QString> &mapping)
{
m_columnToJsonKey = mapping;
// 构建反向映射(可选)
m_jsonKeyToColumn.clear();
for (auto it = m_columnToJsonKey.constBegin(); it != m_columnToJsonKey.constEnd(); ++it) {
m_jsonKeyToColumn[it.value()] = m_model->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString().indexOf(it.key());
}
}
void DeviceConfigView::InitViewWorkspace(const QString &project_name)
{
}
void DeviceConfigView::SetAnalyzeDataFilename(const QMap<QString, QVariant> &data_files_set)
{
if (data_files_set.isEmpty()) {
LOG_WARN("文件集合为空");
return;
}
QString filePath = data_files_set.first().toString();
LOG_INFO("读取文件: " + filePath);
}