新增设备参数配置

This commit is contained in:
anxinglong 2026-03-27 12:55:42 +08:00
parent 93225c466e
commit 20d4afc585
13 changed files with 1725 additions and 10 deletions

View File

@ -0,0 +1,446 @@
#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 <QDebug>
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<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);
}

View File

@ -0,0 +1,60 @@
#ifndef DEVICECONFIGVIEW_H
#define DEVICECONFIGVIEW_H
#include <QWidget>
#include <QHash>
#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<QString, QString> &mapping);
virtual void InitViewWorkspace(const QString& project_name) override final;
virtual void SetAnalyzeDataFilename(const QMap<QString, QVariant>& data_files_set);
signals:
void dataChanged();
private slots:
void onActionButtonClicked();
private:
void initTableView();
void setupActionColumnButtons();
bool parseJsonImpl(const QByteArray &data);
QList<int> selectTargetChannels(int currentChannel) const;
QTableView *m_tableView;
QStandardItemModel *m_model;
QHash<QString, QString> m_columnToJsonKey; // 列头 -> JSON键名
QHash<QString, int> m_jsonKeyToColumn; // 反向映射,用于快速查找列索引
};
#endif // DEVICECONFIGVIEW_H

View File

@ -0,0 +1,333 @@
#include "DeviceParameterConfig.h"
#include "DeviceParameterConfigList.h"
#include "DeviceConfigView.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QTextEdit>
#include <QCheckBox>
#include <QPushButton>
#include <QFile>
#include <QMessageBox>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDir>
#include <QDebug>
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<QString, QString> &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;
}

View File

@ -0,0 +1,73 @@
#ifndef DEVICEPARAMETERCONFIG_H
#define DEVICEPARAMETERCONFIG_H
#include <QWidget>
#include <QHash>
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<QString, QString> &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<QString, QString> m_columnToJsonKey; // 列头->JSON键映射
};
#endif // DEVICEPARAMETERCONFIG_H

View File

@ -0,0 +1,214 @@
#include "DeviceParameterConfigList.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QListWidget>
#include <QPushButton>
#include <QDir>
#include <QFile>
#include <QInputDialog>
#include <QMessageBox>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>
#include <QDateTime>
#include <QLabel>
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<QListWidgetItem*> 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;
}

View File

@ -0,0 +1,56 @@
#ifndef DEVICEPARAMETERCONFIGLIST_H
#define DEVICEPARAMETERCONFIGLIST_H
#include <QWidget>
#include <QListWidgetItem>
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

View File

@ -0,0 +1,384 @@
#include "DeviceParameterProxy.h"
#include <QComboBox>
#include <QDoubleSpinBox>
#include <QSpinBox>
#include <QLineEdit>
#include <QToolTip>
#include <QTimer>
#include <QKeyEvent>
#include <QMessageBox>
#include <QRegularExpressionValidator>
#include <QDebug>
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<DeviceParameterProxy*>(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: // 总测量时间__int6464位
{
QLineEdit *edit = new QLineEdit(parent);
edit->setValidator(new Int64Validator(edit));
edit->setProperty("rangeHint", "0 ~ 9223372036854775807");
edit->installEventFilter(const_cast<DeviceParameterProxy*>(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<QComboBox*>(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<QLineEdit*>(editor);
if (edit) edit->setText(value.toString());
break;
}
case 4: // QDoubleSpinBox
case 6:
{
QDoubleSpinBox *spin = qobject_cast<QDoubleSpinBox*>(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<QSpinBox*>(editor);
if (spin) spin->setValue(value.toInt());
break;
}
default:
{
QLineEdit *edit = qobject_cast<QLineEdit*>(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<QComboBox*>(editor);
if (combo) model->setData(index, combo->currentText());
break;
}
case 3: // 软件增益字符串存储因为可能超出int范围
case 8: // 控制功能(字符串存储)
case 19: // 总测量时间(字符串存储)
{
QLineEdit *edit = qobject_cast<QLineEdit*>(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<QDoubleSpinBox*>(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<QSpinBox*>(editor);
if (spin) model->setData(index, spin->value());
break;
}
default:
{
QLineEdit *edit = qobject_cast<QLineEdit*>(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<QLineEdit*>(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<QKeyEvent*>(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);
}

View File

@ -0,0 +1,119 @@
#ifndef DEVICEPARAMETERPROXY_H
#define DEVICEPARAMETERPROXY_H
#include <QStyledItemDelegate>
#include <QIntValidator>
#include <QLineEdit>
// ==================== 自定义验证器 ====================
/**
* @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

View File

@ -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);

View File

@ -20,7 +20,7 @@
<x>0</x>
<y>0</y>
<width>892</width>
<height>22</height>
<height>23</height>
</rect>
</property>
<widget class="QMenu" name="menu_measurement_analysis">

View File

@ -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<AnalysisType>();
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;
}

View File

@ -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;

View File

@ -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 += \