464 lines
16 KiB
C++
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);
|
|
}
|