From 20d4afc585917e55b30e3064b649c93f1b32f913 Mon Sep 17 00:00:00 2001 From: anxinglong <2910824064@qq.com> Date: Fri, 27 Mar 2026 12:55:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=AE=BE=E5=A4=87=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeviceConfigView.cpp | 446 ++++++++++++++++++ src/DeviceParameterConfig/DeviceConfigView.h | 60 +++ .../DeviceParameterConfig.cpp | 333 +++++++++++++ .../DeviceParameterConfig.h | 73 +++ .../DeviceParameterConfigList.cpp | 214 +++++++++ .../DeviceParameterConfigList.h | 56 +++ .../DeviceParameterProxy.cpp | 384 +++++++++++++++ .../DeviceParameterProxy.h | 119 +++++ src/MainWindow.cpp | 5 +- src/MainWindow.ui | 2 +- src/MeasureAnalysisTreeView.cpp | 18 +- src/MeasureAnalysisView.cpp | 6 +- src/src.pro | 19 +- 13 files changed, 1725 insertions(+), 10 deletions(-) create mode 100644 src/DeviceParameterConfig/DeviceConfigView.cpp create mode 100644 src/DeviceParameterConfig/DeviceConfigView.h create mode 100644 src/DeviceParameterConfig/DeviceParameterConfig.cpp create mode 100644 src/DeviceParameterConfig/DeviceParameterConfig.h create mode 100644 src/DeviceParameterConfig/DeviceParameterConfigList.cpp create mode 100644 src/DeviceParameterConfig/DeviceParameterConfigList.h create mode 100644 src/DeviceParameterConfig/DeviceParameterProxy.cpp create mode 100644 src/DeviceParameterConfig/DeviceParameterProxy.h diff --git a/src/DeviceParameterConfig/DeviceConfigView.cpp b/src/DeviceParameterConfig/DeviceConfigView.cpp new file mode 100644 index 0000000..3ff2543 --- /dev/null +++ b/src/DeviceParameterConfig/DeviceConfigView.cpp @@ -0,0 +1,446 @@ +#include "DeviceConfigView.h" +#include "DeviceParameterProxy.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 QStandardItemModel(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 = 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(sender()); + if (!btn) return; + + int srcRow = btn->property("row").toInt(); + int srcChannel = srcRow + 1; + + QList 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 DeviceConfigView::selectTargetChannels(int currentChannel) const +{ + QDialog dlg(const_cast(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 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 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 &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 &data_files_set) +{ + if (data_files_set.isEmpty()) { + LOG_WARN("文件集合为空"); + return; + } + + QString filePath = data_files_set.first().toString(); + LOG_INFO("读取文件: " + filePath); +} diff --git a/src/DeviceParameterConfig/DeviceConfigView.h b/src/DeviceParameterConfig/DeviceConfigView.h new file mode 100644 index 0000000..29d21ac --- /dev/null +++ b/src/DeviceParameterConfig/DeviceConfigView.h @@ -0,0 +1,60 @@ +#ifndef DEVICECONFIGVIEW_H +#define DEVICECONFIGVIEW_H + +#include +#include +#include "MeasureAnalysisView.h" + +class QTableView; +class QStandardItemModel; +class QPushButton; + + +class DeviceConfigView : public MeasureAnalysisView +{ + Q_OBJECT +public: + explicit DeviceConfigView(QWidget *parent = nullptr); + ~DeviceConfigView(); + + + bool loadJson(const QString &filePath); + + + bool saveJson(const QString &filePath); + + + QStandardItemModel* getModel() const; + + + void showColumnControlDialog(); + + + void reset(); + + + void setColumnMapping(const QHash &mapping); + + virtual void InitViewWorkspace(const QString& project_name) override final; + virtual void SetAnalyzeDataFilename(const QMap& data_files_set); + +signals: + + void dataChanged(); + +private slots: + void onActionButtonClicked(); + +private: + void initTableView(); + void setupActionColumnButtons(); + bool parseJsonImpl(const QByteArray &data); + QList selectTargetChannels(int currentChannel) const; + + QTableView *m_tableView; + QStandardItemModel *m_model; + QHash m_columnToJsonKey; // 列头 -> JSON键名 + QHash m_jsonKeyToColumn; // 反向映射,用于快速查找列索引 +}; + +#endif // DEVICECONFIGVIEW_H diff --git a/src/DeviceParameterConfig/DeviceParameterConfig.cpp b/src/DeviceParameterConfig/DeviceParameterConfig.cpp new file mode 100644 index 0000000..ccc662a --- /dev/null +++ b/src/DeviceParameterConfig/DeviceParameterConfig.cpp @@ -0,0 +1,333 @@ +#include "DeviceParameterConfig.h" +#include "DeviceParameterConfigList.h" +#include "DeviceConfigView.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DeviceParameterConfig::DeviceParameterConfig(QWidget *parent) + : QWidget(parent) + , m_configList(nullptr) + , m_configView(nullptr) + , m_lineEditFileName(nullptr) + , m_textEditRemark(nullptr) + , m_checkBoxSaveToDevice(nullptr) + , m_btnSave(nullptr) + , m_btnSetColumn(nullptr) +{ + setupUi(); + initColumnMapping(); // 设置默认映射 + m_configView->setColumnMapping(m_columnToJsonKey); + m_configList->loadConfigsFromFolder(); // 加载默认目录下的配置文件 +} + +DeviceParameterConfig::~DeviceParameterConfig() +{ +} + +void DeviceParameterConfig::setupUi() +{ + // 主布局:左右分栏 + QHBoxLayout *mainLayout = new QHBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(10); + + // 左侧:配置列表 + m_configList = new DeviceParameterConfigList(this); + mainLayout->addWidget(m_configList, 1); + + // 右侧:垂直布局 + QVBoxLayout *rightLayout = new QVBoxLayout(); + mainLayout->addLayout(rightLayout, 9); + + // 右侧上部:文件名、保存按钮、备注说明 + QHBoxLayout *topLayout = new QHBoxLayout(); + QLabel *labelName = new QLabel(tr("配置命名:"), this); + m_lineEditFileName = new QLineEdit(this); + m_btnSave = new QPushButton(tr("保存到测量分析"), this); + m_checkBoxSaveToDevice = new QCheckBox(tr("保存到设备参数配置管理"), this); + topLayout->addWidget(labelName); + topLayout->addWidget(m_lineEditFileName, 1); + topLayout->addWidget(m_btnSave); + topLayout->addWidget(m_checkBoxSaveToDevice); + rightLayout->addLayout(topLayout); + + QHBoxLayout *remarkLayout = new QHBoxLayout(); + QLabel *labelRemark = new QLabel(tr("备注说明:"), this); + m_textEditRemark = new QTextEdit(this); + m_textEditRemark->setMaximumHeight(80); + remarkLayout->addWidget(labelRemark); + remarkLayout->addWidget(m_textEditRemark, 1); + rightLayout->addLayout(remarkLayout); + + // 右侧中部:设置列按钮 + QHBoxLayout *buttonLayout = new QHBoxLayout(); + m_btnSetColumn = new QPushButton(tr("设置列"), this); + buttonLayout->addWidget(m_btnSetColumn); + buttonLayout->addStretch(); + rightLayout->addLayout(buttonLayout); + + // 右侧下部:参数表格视图 + m_configView = new DeviceConfigView(this); + rightLayout->addWidget(m_configView, 1); + + // 连接信号槽 + connect(m_configList, &DeviceParameterConfigList::configSelected, + this, &DeviceParameterConfig::onConfigSelected); + connect(m_configList, &DeviceParameterConfigList::configDeleted, + this, &DeviceParameterConfig::onConfigDeleted); + connect(m_btnSave, &QPushButton::clicked, + this, &DeviceParameterConfig::onSaveButtonClicked); + connect(m_btnSetColumn, &QPushButton::clicked, + this, &DeviceParameterConfig::onSetColumnButtonClicked); + connect(m_configList, &DeviceParameterConfigList::createNewConfigRequested, + this, &DeviceParameterConfig::onCreateNewConfigRequested); + connect(m_configView, &DeviceConfigView::dataChanged, + this, [this]() { + // 数据改变时,在文件名后添加星号表示未保存 + if (!m_lineEditFileName->text().isEmpty() && !m_lineEditFileName->text().endsWith(" *")) { + m_lineEditFileName->setText(m_lineEditFileName->text() + " *"); + } + }); +} + +void DeviceParameterConfig::initColumnMapping() +{ + // 与原 EquipmentParameterSettingManagement 完全相同的映射 + m_columnToJsonKey.clear(); + m_columnToJsonKey[tr("数据传输模式")] = "eTransferModel"; + m_columnToJsonKey[tr("硬件增益")] = "iDeviceGain"; + m_columnToJsonKey[tr("硬件增益选择索引")] = "iDeviceGainSelectIndex"; + m_columnToJsonKey[tr("软件增益")] = "iSoftGain"; + m_columnToJsonKey[tr("时间常数")] = "dConstTime"; + m_columnToJsonKey[tr("成形时间")] = "iFormTime"; + m_columnToJsonKey[tr("快通道触发阈值")] = "iFastChannelTrigerValue"; + m_columnToJsonKey[tr("多道分辨率")] = "iChannelNum"; + m_columnToJsonKey[tr("控制功能")] = ""; + m_columnToJsonKey[tr("幅度提前延迟")] = ""; + m_columnToJsonKey[tr("积分长度")] = ""; + m_columnToJsonKey[tr("dcfd缩放倍数")] = ""; + m_columnToJsonKey[tr("dcfd延迟")] = ""; + m_columnToJsonKey[tr("直流偏移")] = "iCurrentOffset"; + m_columnToJsonKey[tr("最大能量范围")] = "iMaxEnergy"; + m_columnToJsonKey[tr("Am 峰面积寻峰法的面积比例")] = "iAMPeakDiv"; + m_columnToJsonKey[tr("K40与 Am241的峰位校正系数")] = "iHVDelt"; + m_columnToJsonKey[tr("高压关断阈值")] = "iHVDelt"; + m_columnToJsonKey[tr("获取能谱周期")] = "iGetSpecturmPeirod"; + m_columnToJsonKey[tr("总测量时间")] = "iTotalMeasureTime"; + m_columnToJsonKey[tr("总测量次数")] = "iTotalMeasureCount"; + m_columnToJsonKey[tr("滤波参数")] = "iTrapeTopShitBit"; + m_columnToJsonKey[tr("上升时间")] = "iRiseTime"; + m_columnToJsonKey[tr("平顶时间")] = "iTopTime"; + m_columnToJsonKey[tr("ICR 校正使能")] = "bICRCorrect"; + m_columnToJsonKey[tr("CRZA 值")] = "iCRZAValue"; + m_columnToJsonKey[tr("ZA 使能值")] = "iZAEnable"; +} + +void DeviceParameterConfig::setColumnMapping(const QHash &mapping) +{ + m_columnToJsonKey = mapping; + m_configView->setColumnMapping(mapping); +} + +bool DeviceParameterConfig::loadConfig(const QString &filePath) +{ + if (!m_configView->loadJson(filePath)) + return false; + + m_currentFilePath = filePath; + QFileInfo info(filePath); + m_lineEditFileName->setText(info.fileName()); + + // 读取 JSON 中的备注字段 + QFile file(filePath); + if (file.open(QIODevice::ReadOnly)) { + QByteArray data = file.readAll(); + file.close(); + QJsonDocument doc = QJsonDocument::fromJson(data); + if (doc.isObject()) { + QJsonObject obj = doc.object(); + if (obj.contains("remark")) { + m_textEditRemark->setPlainText(obj["remark"].toString()); + } else { + m_textEditRemark->clear(); + } + } + } + + emit configLoaded(filePath); // 此信号未在头文件中声明,可添加;若不需要可移除 + return true; +} + +bool DeviceParameterConfig::saveCurrentConfig() +{ + if (m_currentFilePath.isEmpty()) { + QMessageBox::warning(this, tr("错误"), tr("没有选中的配置文件")); + return false; + } + + // 保存表格数据 + if (!m_configView->saveJson(m_currentFilePath)) + return false; + + // 保存备注到 JSON + QFile file(m_currentFilePath); + if (!file.open(QIODevice::ReadOnly)) { + qWarning() << "无法打开文件读取备注:" << file.errorString(); + return false; + } + QByteArray data = file.readAll(); + file.close(); + + QJsonDocument doc = QJsonDocument::fromJson(data); + if (!doc.isObject()) { + qWarning() << "JSON 不是对象"; + return false; + } + QJsonObject root = doc.object(); + root["remark"] = m_textEditRemark->toPlainText(); + + if (!file.open(QIODevice::WriteOnly)) { + qWarning() << "无法写入文件:" << file.errorString(); + return false; + } + file.write(QJsonDocument(root).toJson()); + file.close(); + + // 移除文件名后的星号 + QString fileName = m_lineEditFileName->text(); + if (fileName.endsWith(" *")) + fileName.chop(2); + m_lineEditFileName->setText(fileName); + + return true; +} + +QString DeviceParameterConfig::getCurrentFileName() const +{ + return QFileInfo(m_currentFilePath).fileName(); +} + +QString DeviceParameterConfig::getRemark() const +{ + return m_textEditRemark->toPlainText(); +} + +void DeviceParameterConfig::setRemark(const QString &remark) +{ + m_textEditRemark->setPlainText(remark); +} + +void DeviceParameterConfig::onConfigSelected(const QString &filePath) +{ + loadConfig(filePath); +} + +void DeviceParameterConfig::onConfigDeleted(const QString &fileName) +{ + if (m_lineEditFileName->text() == fileName) { + m_lineEditFileName->clear(); + m_textEditRemark->clear(); + m_configView->reset(); + m_currentFilePath.clear(); + } +} + +void DeviceParameterConfig::onCreateNewConfigRequested(const QString &fileName) +{ + QString filePath = QDir::currentPath() + "/parameterConfig/" + fileName; + if (createDefaultConfigFile(filePath)) { + // 刷新列表 + m_configList->refresh(); + // 选中新建的文件 + m_configList->setCurrentFile(fileName); + // 自动加载该配置(可选) + loadConfig(filePath); + } else { + QMessageBox::warning(this, tr("错误"), tr("无法创建配置文件:%1").arg(filePath)); + } +} + +void DeviceParameterConfig::onSaveButtonClicked() +{ + if (!saveCurrentConfig()) { + QMessageBox::warning(this, tr("错误"), tr("保存配置文件失败")); + return; + } + + // 如果勾选了“保存到设备参数配置管理”,刷新左侧列表 + if (m_checkBoxSaveToDevice->isChecked()) { + m_configList->refresh(); + } + + // 发出信号,供外部处理(例如将参数发送到测量分析模块) + emit saveToMeasurementRequested(m_currentFilePath); +} + +void DeviceParameterConfig::onSetColumnButtonClicked() +{ + m_configView->showColumnControlDialog(); +} + +bool DeviceParameterConfig::createDefaultConfigFile(const QString &filePath) +{ + // 创建默认配置文件(含 32 个通道) + QJsonObject rootObj; + rootObj["command"] = "SET"; + QJsonArray channelsArray; + + QJsonObject defaultChannel; + defaultChannel["eTransferModel"] = "eSpecturmMode"; + defaultChannel["iDeviceGain"] = 1; + defaultChannel["iDeviceGainSelectIndex"] = 1; + defaultChannel["iSoftGain"] = 3000; + defaultChannel["dConstTime"] = 45.0; + defaultChannel["iFormTime"] = 3; + defaultChannel["iFastChannelTrigerValue"] = 10.0; + defaultChannel["iChannelNum"] = 1024; + defaultChannel["iHighVoltage"] = 0; + defaultChannel["iInputVoltageDesc"] = 0; + defaultChannel["iCRDivMode"] = 0; + defaultChannel["iInputSignalPostive"] = 0; + defaultChannel["iCurrentOffset"] = 0; + defaultChannel["iMaxEnergy"] = 0; + defaultChannel["iAMPeakDiv"] = 0; + defaultChannel["iHVDelt"] = 0; + defaultChannel["iHVCtrl"] = 0; + defaultChannel["iGetSpecturmPeirod"] = 1; + defaultChannel["iTotalMeasureTime"] = 10; + defaultChannel["iTotalMeasureCount"] = 1; + defaultChannel["iTrapeTopShitBit"] = 0; + defaultChannel["iRiseTime"] = 2; + defaultChannel["iTopTime"] = 2; + defaultChannel["bICRCorrect"] = false; + defaultChannel["iCRZAValue"] = 0; + defaultChannel["iZAEnable"] = 30; + defaultChannel["reserve"] = QJsonArray(); + defaultChannel["pUserConfigInfo"] = QJsonObject(); + + for (int i = 0; i < 32; ++i) { + channelsArray.append(defaultChannel); + } + rootObj["channels"] = channelsArray; + rootObj["remark"] = ""; + + QJsonDocument doc(rootObj); + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly)) + return false; + file.write(doc.toJson()); + file.close(); + return true; +} diff --git a/src/DeviceParameterConfig/DeviceParameterConfig.h b/src/DeviceParameterConfig/DeviceParameterConfig.h new file mode 100644 index 0000000..07dbebb --- /dev/null +++ b/src/DeviceParameterConfig/DeviceParameterConfig.h @@ -0,0 +1,73 @@ +#ifndef DEVICEPARAMETERCONFIG_H +#define DEVICEPARAMETERCONFIG_H + +#include +#include + +class DeviceParameterConfigList; +class DeviceConfigView; +class QLineEdit; +class QTextEdit; +class QCheckBox; +class QPushButton; + + +class DeviceParameterConfig : public QWidget +{ + Q_OBJECT +public: + explicit DeviceParameterConfig(QWidget *parent = nullptr); + ~DeviceParameterConfig(); + + + void setColumnMapping(const QHash &mapping); + + + bool loadConfig(const QString &filePath); + + + bool saveCurrentConfig(); + + + QString getCurrentFileName() const; + + + QString getRemark() const; + + + void setRemark(const QString &remark); + +signals: + + void saveToMeasurementRequested(const QString &filePath); + + void configLoaded(QString ); + +private slots: + void onConfigSelected(const QString &filePath); + void onConfigDeleted(const QString &fileName); + void onCreateNewConfigRequested(const QString &fileName); + void onSaveButtonClicked(); + void onSetColumnButtonClicked(); + +private: + void setupUi(); + bool createDefaultConfigFile(const QString &filePath); + void initColumnMapping(); // 初始化默认映射(若外部未调用则使用默认) + + // 子控件 + DeviceParameterConfigList *m_configList; + DeviceConfigView *m_configView; + + // 周边控件 + QLineEdit *m_lineEditFileName; + QTextEdit *m_textEditRemark; + QCheckBox *m_checkBoxSaveToDevice; + QPushButton *m_btnSave; + QPushButton *m_btnSetColumn; + + QString m_currentFilePath; + QHash m_columnToJsonKey; // 列头->JSON键映射 +}; + +#endif // DEVICEPARAMETERCONFIG_H diff --git a/src/DeviceParameterConfig/DeviceParameterConfigList.cpp b/src/DeviceParameterConfig/DeviceParameterConfigList.cpp new file mode 100644 index 0000000..37eb8d8 --- /dev/null +++ b/src/DeviceParameterConfig/DeviceParameterConfigList.cpp @@ -0,0 +1,214 @@ +#include "DeviceParameterConfigList.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +DeviceParameterConfigList::DeviceParameterConfigList(QWidget *parent) + : QWidget(parent) + , m_listWidget(nullptr) + , m_btnAdd(nullptr) + , m_btnDelete(nullptr) +{ + setupUi(); +} + +DeviceParameterConfigList::~DeviceParameterConfigList() +{ +} + +void DeviceParameterConfigList::setupUi() +{ + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + + QLabel *ListWidgetName = new QLabel("设备参数配置列表"); + mainLayout->addWidget(ListWidgetName); + + m_listWidget = new QListWidget(this); + mainLayout->addWidget(m_listWidget); + + QHBoxLayout *btnLayout = new QHBoxLayout(); + m_btnAdd = new QPushButton(tr("添加"), this); + m_btnDelete = new QPushButton(tr("删除"), this); + btnLayout->addWidget(m_btnAdd); + btnLayout->addWidget(m_btnDelete); + btnLayout->addStretch(); + mainLayout->addLayout(btnLayout); + + connect(m_listWidget, &QListWidget::itemClicked, this, &DeviceParameterConfigList::onItemClicked); + connect(m_btnAdd, &QPushButton::clicked, this, &DeviceParameterConfigList::onAddClicked); + connect(m_btnDelete, &QPushButton::clicked, this, &DeviceParameterConfigList::onDeleteClicked); +} + +void DeviceParameterConfigList::loadConfigsFromFolder(const QString &folderPath) +{ + if (folderPath.isEmpty()) { + m_folderPath = QDir::currentPath() + "/parameterConfig"; + } else { + m_folderPath = folderPath; + } + + QDir dir(m_folderPath); + if (!dir.exists()) { + dir.mkpath(m_folderPath); + } + + dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + QStringList files = dir.entryList(); + updateList(files); +} + +void DeviceParameterConfigList::updateList(const QStringList &fileList) +{ + m_listWidget->clear(); + for (const QString &file : fileList) { + if (file.endsWith(".json", Qt::CaseInsensitive)) { + m_listWidget->addItem(file); + } + } +} + +QString DeviceParameterConfigList::getCurrentFilePath() const +{ + QListWidgetItem *item = m_listWidget->currentItem(); + if (!item) return QString(); + return m_folderPath + "/" + item->text(); +} + +QString DeviceParameterConfigList::getCurrentFileName() const +{ + QListWidgetItem *item = m_listWidget->currentItem(); + return item ? item->text() : QString(); +} + +void DeviceParameterConfigList::setCurrentFile(const QString &fileName) +{ + QList items = m_listWidget->findItems(fileName, Qt::MatchExactly); + if (!items.isEmpty()) { + m_listWidget->setCurrentItem(items.first()); + emit configSelected(getCurrentFilePath()); + } +} + +void DeviceParameterConfigList::refresh() +{ + loadConfigsFromFolder(m_folderPath); +} + +void DeviceParameterConfigList::onItemClicked(QListWidgetItem *item) +{ + if (item) { + emit configSelected(m_folderPath + "/" + item->text()); + } +} + +void DeviceParameterConfigList::onAddClicked() +{ + bool ok; + QString fileName = QInputDialog::getText(this, tr("新建配置"), + tr("请输入配置文件名(无需后缀):"), + QLineEdit::Normal, + QString("NewConfig_%1").arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss")), + &ok); + if (!ok || fileName.isEmpty()) return; + + if (!fileName.endsWith(".json", Qt::CaseInsensitive)) + fileName += ".json"; + + QString filePath = m_folderPath + "/" + fileName; + if (QFile::exists(filePath)) { + QMessageBox::warning(this, tr("错误"), tr("文件已存在,请使用其他名称。")); + return; + } + + // 发出创建请求,由外部(主窗口)决定默认内容 + emit createNewConfigRequested(fileName); +} + +void DeviceParameterConfigList::onDeleteClicked() +{ + QListWidgetItem *currentItem = m_listWidget->currentItem(); + if (!currentItem) { + QMessageBox::information(this, tr("提示"), tr("请先选择要删除的配置文件。")); + return; + } + + QString fileName = currentItem->text(); + QString filePath = m_folderPath + "/" + fileName; + + int ret = QMessageBox::question(this, tr("确认删除"), + tr("确定要删除文件 \"%1\" 吗?").arg(fileName), + QMessageBox::Yes | QMessageBox::No); + if (ret != QMessageBox::Yes) return; + + QFile file(filePath); + if (file.remove()) { + delete currentItem; + emit configDeleted(fileName); + QMessageBox::information(this, tr("成功"), tr("文件已删除。")); + } else { + QMessageBox::warning(this, tr("错误"), tr("无法删除文件:%1").arg(file.errorString())); + } +} + +bool DeviceParameterConfigList::createDefaultConfigFile(const QString &filePath) +{ + // 默认配置内容(与原来保持一致) + QJsonObject rootObj; + rootObj["command"] = "SET"; + QJsonArray channelsArray; + + QJsonObject defaultChannel; + defaultChannel["eTransferModel"] = "eSpecturmMode"; + defaultChannel["iDeviceGain"] = 1; + defaultChannel["iDeviceGainSelectIndex"] = 1; + defaultChannel["iSoftGain"] = 3000; + defaultChannel["dConstTime"] = 45.0; + defaultChannel["iFormTime"] = 3; + defaultChannel["iFastChannelTrigerValue"] = 10.0; + defaultChannel["iChannelNum"] = 1024; + defaultChannel["iHighVoltage"] = 0; + defaultChannel["iInputVoltageDesc"] = 0; + defaultChannel["iCRDivMode"] = 0; + defaultChannel["iInputSignalPostive"] = 0; + defaultChannel["iCurrentOffset"] = 0; + defaultChannel["iMaxEnergy"] = 0; + defaultChannel["iAMPeakDiv"] = 0; + defaultChannel["iHVDelt"] = 0; + defaultChannel["iHVCtrl"] = 0; + defaultChannel["iGetSpecturmPeirod"] = 1; + defaultChannel["iTotalMeasureTime"] = 10; + defaultChannel["iTotalMeasureCount"] = 1; + defaultChannel["iTrapeTopShitBit"] = 0; + defaultChannel["iRiseTime"] = 2; + defaultChannel["iTopTime"] = 2; + defaultChannel["bICRCorrect"] = false; + defaultChannel["iCRZAValue"] = 0; + defaultChannel["iZAEnable"] = 30; + defaultChannel["reserve"] = QJsonArray(); + defaultChannel["pUserConfigInfo"] = QJsonObject(); + + for (int i = 0; i < 32; ++i) { + channelsArray.append(defaultChannel); + } + rootObj["channels"] = channelsArray; + + QJsonDocument doc(rootObj); + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly)) { + return false; + } + file.write(doc.toJson()); + file.close(); + return true; +} diff --git a/src/DeviceParameterConfig/DeviceParameterConfigList.h b/src/DeviceParameterConfig/DeviceParameterConfigList.h new file mode 100644 index 0000000..d4707b1 --- /dev/null +++ b/src/DeviceParameterConfig/DeviceParameterConfigList.h @@ -0,0 +1,56 @@ +#ifndef DEVICEPARAMETERCONFIGLIST_H +#define DEVICEPARAMETERCONFIGLIST_H + +#include +#include + +class QListWidget; +class QPushButton; + + +class DeviceParameterConfigList : public QWidget +{ + Q_OBJECT +public: + explicit DeviceParameterConfigList(QWidget *parent = nullptr); + ~DeviceParameterConfigList(); + + + void loadConfigsFromFolder(const QString &folderPath = QString()); + + + QString getCurrentFilePath() const; + + + QString getCurrentFileName() const; + + + void setCurrentFile(const QString &fileName); + + void refresh(); + +signals: + + void configSelected(const QString &filePath); + + void configDeleted(const QString &fileName); + + void createNewConfigRequested(const QString &fileName); + +private slots: + void onItemClicked(QListWidgetItem *item); + void onAddClicked(); + void onDeleteClicked(); + +private: + void setupUi(); + void updateList(const QStringList &fileList); + bool createDefaultConfigFile(const QString &filePath); + + QListWidget *m_listWidget; + QPushButton *m_btnAdd; + QPushButton *m_btnDelete; + QString m_folderPath; +}; + +#endif // DEVICEPARAMETERCONFIGLIST_H diff --git a/src/DeviceParameterConfig/DeviceParameterProxy.cpp b/src/DeviceParameterConfig/DeviceParameterProxy.cpp new file mode 100644 index 0000000..bb70586 --- /dev/null +++ b/src/DeviceParameterConfig/DeviceParameterProxy.cpp @@ -0,0 +1,384 @@ +#include "DeviceParameterProxy.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DeviceParameterProxy::DeviceParameterProxy(QObject *parent) : QStyledItemDelegate(parent) +{ +} + +QWidget* DeviceParameterProxy::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + int col = index.column(); + switch (col) + { + case 0: // 数据传输模式(枚举) + { + QComboBox *combo = new QComboBox(parent); + combo->addItems(QStringList() << "谱线模式" << "波形模式" << "ADC模式" << "列表模式" << "α/β模式"); + return combo; + } + case 1: // 硬件增益(档位) + { + QComboBox *combo = new QComboBox(parent); + combo->addItems(QStringList() << "1" << "2" << "4" << "8" << "16"); + return combo; + } + case 2: // 硬件增益选择索引 + { + QComboBox *combo = new QComboBox(parent); + combo->addItems(QStringList() << "1" << "2" << "3" << "4" << "5"); + return combo; + } + case 3: // 软件增益(32位无符号) + { + QLineEdit *edit = new QLineEdit(parent); + edit->setValidator(new UInt32Validator(edit)); + edit->setProperty("rangeHint", "0 ~ 4294967295"); + edit->installEventFilter(const_cast(this)); + return edit; + } + case 4: // 时间常数(double μs) + { + QDoubleSpinBox *spin = new QDoubleSpinBox(parent); + spin->setRange(0.0, 1000.0); + spin->setSingleStep(1.0); + spin->setDecimals(2); + return spin; + } + case 5: // 成形时间(int μs) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(1, 10); + return spin; + } + case 6: // 快通道触发阈值(double 0.1mV) + { + QDoubleSpinBox *spin = new QDoubleSpinBox(parent); + spin->setRange(0.0, 10000.0); + spin->setSingleStep(0.1); + spin->setDecimals(1); + return spin; + } + case 7: // 多道分辨率(2的幂) + { + QComboBox *combo = new QComboBox(parent); + combo->addItems(QStringList() << "256" << "512" << "1024" << "2048" << "4096" << "8192" << "16384"); + return combo; + } + case 8: // 控制功能(位掩码,暂用十进制输入) + { + QLineEdit *edit = new QLineEdit(parent); + // 允许输入十进制数字,不设范围限制(业务层处理) + return edit; + } + case 9: // 幅度提前延迟(unsigned char) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(0, 255); + return spin; + } + case 10: // 积分长度(unsigned char) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(0, 255); + return spin; + } + case 11: // dcfd缩放倍数(unsigned char) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(0, 255); + return spin; + } + case 12: // dcfd延迟(unsigned char) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(0, 255); + return spin; + } + case 13: // 直流偏移(mV) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(-1000, 1000); + return spin; + } + case 14: // 最大能量范围(keV) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(0, 99999); + return spin; + } + case 15: // Am峰面积寻峰法的面积比例(%) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(0, 100); + return spin; + } + case 16: // K40与Am241的峰位校正系数(short) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(-32768, 32767); + return spin; + } + case 17: // 高压关断阈值(short) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(-32768, 32767); + return spin; + } + case 18: // 获取能谱周期(秒) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(0, 86400); // 一天 + return spin; + } + case 19: // 总测量时间(__int64,64位) + { + QLineEdit *edit = new QLineEdit(parent); + edit->setValidator(new Int64Validator(edit)); + edit->setProperty("rangeHint", "0 ~ 9223372036854775807"); + edit->installEventFilter(const_cast(this)); + return edit; + } + case 20: // 总测量次数 + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(0, 1000000); // 可调大 + return spin; + } + case 21: // 滤波参数 + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(0, 3); + return spin; + } + case 22: // 上升时间(μs) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(1, 255); + return spin; + } + case 23: // 平顶时间(μs) + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(1, 255); + return spin; + } + case 24: // ICR校正使能(布尔) + { + QComboBox *combo = new QComboBox(parent); + combo->addItems(QStringList() << "关闭" << "启用"); + return combo; + } + case 25: // CRZA值 + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(0, 1000000); // 合理范围 + return spin; + } + case 26: // ZA使能值 + { + QSpinBox *spin = new QSpinBox(parent); + spin->setRange(0, 100); + return spin; + } + default: + return new QLineEdit(parent); + } +} + +void DeviceParameterProxy::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + QVariant value = index.data(Qt::EditRole); + int col = index.column(); + + switch (col) + { + case 0: // QComboBox + case 1: + case 2: + case 7: + case 24: + { + QComboBox *combo = qobject_cast(editor); + if (combo) { + int idx = combo->findText(value.toString()); + if (idx >= 0) combo->setCurrentIndex(idx); + } + break; + } + case 3: // QLineEdit(软件增益) + case 8: // QLineEdit(控制功能) + case 19: // QLineEdit(总测量时间) + { + QLineEdit *edit = qobject_cast(editor); + if (edit) edit->setText(value.toString()); + break; + } + case 4: // QDoubleSpinBox + case 6: + { + QDoubleSpinBox *spin = qobject_cast(editor); + if (spin) spin->setValue(value.toDouble()); + break; + } + case 5: // QSpinBox + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 20: + case 21: + case 22: + case 23: + case 25: + case 26: + { + QSpinBox *spin = qobject_cast(editor); + if (spin) spin->setValue(value.toInt()); + break; + } + default: + { + QLineEdit *edit = qobject_cast(editor); + if (edit) edit->setText(value.toString()); + break; + } + } +} + +void DeviceParameterProxy::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const +{ + int col = index.column(); + + switch (col) + { + case 0: + case 1: + case 2: + case 7: + case 24: + { + QComboBox *combo = qobject_cast(editor); + if (combo) model->setData(index, combo->currentText()); + break; + } + case 3: // 软件增益(字符串存储,因为可能超出int范围) + case 8: // 控制功能(字符串存储) + case 19: // 总测量时间(字符串存储) + { + QLineEdit *edit = qobject_cast(editor); +// if (edit) model->setData(index, edit->text()); + if (edit) { + // 提交前再次验证(防止通过回车提交无效数据) + QString text = edit->text(); + int pos = 0; + if (edit->validator() && edit->validator()->validate(text, pos) == QValidator::Acceptable) { + model->setData(index, text); + } else { + // 无效数据不写入模型,并显示提示 + QString rangeHint = edit->property("rangeHint").toString(); + QPoint pos = edit->mapToGlobal(edit->rect().topRight()); + QToolTip::showText(pos, QString("取值范围: %1").arg(rangeHint), edit); + // 焦点留在编辑器(但此时编辑器即将销毁,所以需要延迟重新打开?) + // 实际上 setModelData 被调用时编辑器即将销毁,我们无法阻止。 + // 因此更好的方法是在事件过滤器中拦截回车,避免走到这里。 + } + } + break; + } + case 4: + case 6: + { + QDoubleSpinBox *spin = qobject_cast(editor); + if (spin) model->setData(index, spin->value()); + break; + } + case 5: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 20: + case 21: + case 22: + case 23: + case 25: + case 26: + { + QSpinBox *spin = qobject_cast(editor); + if (spin) model->setData(index, spin->value()); + break; + } + default: + { + QLineEdit *edit = qobject_cast(editor); + if (edit) model->setData(index, edit->text()); + break; + } + } +} + +void DeviceParameterProxy::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + editor->setGeometry(option.rect); +} + +bool DeviceParameterProxy::eventFilter(QObject *obj, QEvent *event) +{ + QLineEdit *edit = qobject_cast(obj); + if (!edit || !edit->validator()) + return QStyledItemDelegate::eventFilter(obj, event); + + if (event->type() == QEvent::FocusOut) { + QString text = edit->text(); + int pos = 0; + if (edit->validator()->validate(text, pos) != QValidator::Acceptable) { + QString rangeHint = edit->property("rangeHint").toString(); + QPoint pos = edit->mapToGlobal(edit->rect().topRight()); + QToolTip::showText(pos, QString("取值范围: %1").arg(rangeHint), edit); + // 延迟重新获得焦点 + QTimer::singleShot(0, edit, SLOT(setFocus())); + return true; // 阻止焦点离开 + } else { + QToolTip::hideText(); + } + } + else if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { + QString text = edit->text(); + int pos = 0; + if (edit->validator()->validate(text, pos) != QValidator::Acceptable) { + QString rangeHint = edit->property("rangeHint").toString(); + QPoint pos = edit->mapToGlobal(edit->rect().topRight()); + QToolTip::showText(pos, QString("取值范围: %1").arg(rangeHint), edit); + return true; // 拦截回车,不提交 + } else { + QToolTip::hideText(); + // 允许回车提交,Qt 会正常处理 + } + } + } + + return QStyledItemDelegate::eventFilter(obj, event); +} diff --git a/src/DeviceParameterConfig/DeviceParameterProxy.h b/src/DeviceParameterConfig/DeviceParameterProxy.h new file mode 100644 index 0000000..ab437d5 --- /dev/null +++ b/src/DeviceParameterConfig/DeviceParameterProxy.h @@ -0,0 +1,119 @@ +#ifndef DEVICEPARAMETERPROXY_H +#define DEVICEPARAMETERPROXY_H + +#include +#include +#include + +// ==================== 自定义验证器 ==================== + +/** + * @brief 无符号32位整数验证器(范围 0 ~ 4294967295) + */ +class UInt32Validator : public QValidator +{ +public: + UInt32Validator(QObject *parent = nullptr) : QValidator(parent) {} + + State validate(QString &input, int &pos) const override + { + if (input.isEmpty()) + return Intermediate; + + bool ok; + quint64 value = input.toULongLong(&ok); + if (!ok) + return Invalid; + + if (value <= 4294967295ULL) + return Acceptable; + + return Invalid; + } + + void fixup(QString &input) const override + { + input.clear(); + } +}; + +/** + * @brief 有符号64位整数验证器(范围 0 ~ 9223372036854775807) + * 用于总测量时间(__int64) + */ +class Int64Validator : public QValidator +{ +public: + Int64Validator(QObject *parent = nullptr) : QValidator(parent) {} + + State validate(QString &input, int &pos) const override + { + if (input.isEmpty()) + return Intermediate; + + bool ok; + qint64 value = input.toLongLong(&ok); + if (!ok) + return Invalid; + + if (value >= 0 && value <= 9223372036854775807LL) + return Acceptable; + + return Invalid; + } + + void fixup(QString &input) const override + { + input.clear(); + } +}; + +/** + * @brief 设备参数表格的代理类,用于自定义单元格的编辑器 + * + * 根据不同的列(参数类型)提供不同的编辑控件,例如下拉框、数字输入框等。 + */ +class DeviceParameterProxy : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit DeviceParameterProxy(QObject *parent = nullptr); + + /** + * @brief 创建编辑器控件,根据列索引返回不同的编辑器 + * @param parent 父窗口 + * @param option 样式选项 + * @param index 当前单元格的索引 + * @return 返回对应列的编辑器控件指针 + */ + QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + + /** + * @brief 将模型中的数据设置到编辑器中 + * @param editor 编辑器控件 + * @param index 当前单元格的索引 + */ + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + + /** + * @brief 将编辑器中的数据写回模型 + * @param editor 编辑器控件 + * @param model 数据模型 + * @param index 当前单元格的索引 + */ + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; + + /** + * @brief 更新编辑器的几何位置 + * @param editor 编辑器控件 + * @param option 样式选项 + * @param index 当前单元格的索引 + */ + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; // 事件过滤器,用于验证输入和显示提示 +}; + + +#endif // DEVICEPARAMETERPROXY_H diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 47a43f4..e80fb18 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -26,7 +26,7 @@ #include "MeasureAnalysisView.h" #include "MeasureAnalysisTreeView.h" #include "GlobalDefine.h" - +#include "DeviceParameterConfig.h" using namespace ads; MainWindow* MainWindow::_s_main_win = nullptr; @@ -219,7 +219,8 @@ void MainWindow::initAction() device_cfg_mrg_dlg.setObjectName("MeasureDeviceParamsCfgFormDlg"); device_cfg_mrg_dlg.setWindowTitle(QStringLiteral(u"测量设备参数配置管理")); device_cfg_mrg_dlg.setWindowFlags(device_cfg_mrg_dlg.windowFlags() | Qt::WindowMaximizeButtonHint); - MeasureDeviceParamsCfgForm* measure_dev_params_cfg_form = new MeasureDeviceParamsCfgForm(&device_cfg_mrg_dlg); +// MeasureDeviceParamsCfgForm* measure_dev_params_cfg_form = new MeasureDeviceParamsCfgForm(&device_cfg_mrg_dlg); + DeviceParameterConfig * measure_dev_params_cfg_form = new DeviceParameterConfig(&device_cfg_mrg_dlg); QHBoxLayout* hlayout = new QHBoxLayout; hlayout->addWidget(measure_dev_params_cfg_form); device_cfg_mrg_dlg.setLayout(hlayout); diff --git a/src/MainWindow.ui b/src/MainWindow.ui index aadc8b6..18b05c9 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -20,7 +20,7 @@ 0 0 892 - 22 + 23 diff --git a/src/MeasureAnalysisTreeView.cpp b/src/MeasureAnalysisTreeView.cpp index d2d4368..9624da5 100644 --- a/src/MeasureAnalysisTreeView.cpp +++ b/src/MeasureAnalysisTreeView.cpp @@ -64,7 +64,12 @@ void MeasureAnalysisTreeView::onNodeDoubleClicked(const QModelIndex& index) if (!item) return; if (!_model->GetNodeStatus(item)) { - return; + QVariant analysis_type_data = _model->GetNodeUserData(item, ProjectList::NodeType); + if (!analysis_type_data.isValid()) + return; + AnalysisType analysis_type = analysis_type_data.value(); + if(analysis_type != AnalysisType::DeviceParamsCfg) + return; } const QString& item_text = item->text(); QVariant project_name_data = _model->GetNodeUserData(item, ProjectList::ProjectName); @@ -232,6 +237,17 @@ void MeasureAnalysisTreeView::onNodeDoubleClicked(const QModelIndex& index) } } } break; + case AnalysisType::DeviceParamsCfg: { + MeasureAnalysisProjectModel* project_model = _model->GetProjectModel(project_name); + if (project_model) { + QString file_name = project_model->GetMeasureDeviceParamsCfgFilename(); + if (!file_name.isEmpty()) { + data_files_set[QStringLiteral(u"设备参数配置")] = file_name; + } else { + data_files_set[QStringLiteral(u"设备参数配置")] = QString(""); + } + } + } break; default: break; } diff --git a/src/MeasureAnalysisView.cpp b/src/MeasureAnalysisView.cpp index b74e5d1..bd2cb87 100644 --- a/src/MeasureAnalysisView.cpp +++ b/src/MeasureAnalysisView.cpp @@ -8,7 +8,7 @@ #include "ParticleTimePoorView.h" #include "ConformityAnalysis.h" #include "EnergyCountPeakFitView.h" - +#include "DeviceConfigView.h" MeasureAnalysisView *MeasureAnalysisView::NewAnalyzeView(AnalysisType view_type) { MeasureAnalysisView* new_view = nullptr; @@ -18,8 +18,8 @@ MeasureAnalysisView *MeasureAnalysisView::NewAnalyzeView(AnalysisType view_type) // new_view->setDeleteOnClose(true); } break; case AnalysisType::DeviceParamsCfg: { - // new_view = new MeasureAnalysisDataTableView; - // new_view->setDeleteOnClose(true); + new_view = new DeviceConfigView; + new_view->setDeleteOnClose(true); } break; case AnalysisType::EnergyScale: { // new_view = new MeasureAnalysisDataTableView; diff --git a/src/src.pro b/src/src.pro index e1cce09..131cbbb 100644 --- a/src/src.pro +++ b/src/src.pro @@ -40,7 +40,9 @@ INCLUDEPATH += \ $${PWD}/ThreeDimensionalConformityAnalysisView \ $${PWD}/EnergyCountPeakFitView \ $${PWD}/MeasureAnalysisDataTableView \ - $${PWD}/MeasureAnalysisHistoryForm + $${PWD}/MeasureAnalysisHistoryForm \ + $${PWD}/DeviceParameterConfig + DEPENDPATH += \ $${PWD}/MeasureAnalysisParticleCountPlotView \ @@ -52,13 +54,18 @@ DEPENDPATH += \ $${PWD}/ThreeDimensionalConformityAnalysisView \ $${PWD}/EnergyCountPeakFitView \ $${PWD}/MeasureAnalysisDataTableView \ - $${PWD}/MeasureAnalysisHistoryForm + $${PWD}/MeasureAnalysisHistoryForm \ + $${PWD}/DeviceParameterConfig + SOURCES += \ AboutDlg.cpp \ CountRateAnalysisView/CountRateAnalysisView.cpp \ CustomQwtPlot.cpp \ DataProcessWorkPool.cpp \ + DeviceParameterConfig/DeviceConfigView.cpp \ + DeviceParameterConfig/DeviceParameterConfig.cpp \ + DeviceParameterConfig/DeviceParameterConfigList.cpp \ EnergyCountPeakFitView/EnergyCountPeakFitView.cpp \ EnergyCountPlotView/EnergyCountPlotView.cpp \ EnergyScaleDataModel.cpp \ @@ -85,6 +92,7 @@ SOURCES += \ ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.cpp \ ThreeDimensionalConformityAnalysisView/ThreeDDisplay.cpp \ EnergyCountPeakFitView/EnergyCountPeakFitView.cpp \ + DeviceParameterConfig/DeviceParameterProxy.cpp \ main.cpp HEADERS += \ @@ -93,6 +101,9 @@ HEADERS += \ CountRateAnalysisView/CountRateAnalysisView.h \ CustomQwtPlot.h \ DataProcessWorkPool.h \ + DeviceParameterConfig/DeviceConfigView.h \ + DeviceParameterConfig/DeviceParameterConfig.h \ + DeviceParameterConfig/DeviceParameterConfigList.h \ EnergyCountPeakFitView/EnergyCountPeakFitView.h \ EnergyCountPlotView/EnergyCountPlotView.h \ EnergyScaleDataModel.h \ @@ -120,7 +131,9 @@ HEADERS += \ ThreeDimensionalConformityAnalysisView/DetectorStatusSummary.h \ ThreeDimensionalConformityAnalysisView/ParticleDataStatistics.h \ ThreeDimensionalConformityAnalysisView/ThreeDDisplay.h \ - EnergyCountPeakFitView/EnergyCountPeakFitView.h + EnergyCountPeakFitView/EnergyCountPeakFitView.h \ + DeviceParameterConfig/DeviceParameterProxy.h + FORMS += \