#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 #include class DeviceConfigViewModel: public QStandardItemModel { public: DeviceConfigViewModel(QObject *parent = nullptr) : QStandardItemModel(parent){} virtual Qt::ItemFlags flags(const QModelIndex &index) const { Qt::ItemFlags f = QStandardItemModel::flags(index); if ( index.column() == 0 ) { f = f & Qt::ItemIsUserCheckable; } return f; } }; DeviceConfigView::DeviceConfigView(QWidget *parent) : MeasureAnalysisView(parent) , m_tableView(nullptr) , m_model(nullptr) { initTableView(); } DeviceConfigView::~DeviceConfigView() { } void DeviceConfigView::initTableView() { QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); m_tableView = new QTableView(this); layout->addWidget(m_tableView); // 设置表头 QStringList headList; headList << tr("数据传输模式") << tr("硬件增益") << tr("硬件增益选择索引") << tr("软件增益") << tr("时间常数") << tr("成形时间") << tr("快通道触发阈值") << tr("多道分辨率") << tr("控制功能") << tr("幅度提前延迟") << tr("积分长度") << tr("dcfd缩放倍数") << tr("dcfd延迟") << tr("直流偏移") << tr("最大能量范围") << tr("Am 峰面积寻峰法的面积比例") << tr("K40与 Am241的峰位校正系数") << tr("高压关断阈值") << tr("获取能谱周期") << tr("总测量时间") << tr("总测量次数") << tr("滤波参数") << tr("上升时间") << tr("平顶时间") << tr("ICR 校正使能") << tr("CRZA 值") << tr("ZA 使能值") << tr("操作"); m_model = new DeviceConfigViewModel(this); m_model->setColumnCount(headList.size()); m_model->setHorizontalHeaderLabels(headList); m_model->setRowCount(32); for (int i = 0; i < 32; ++i) { QString rowName = tr("通道%1").arg(i + 1); m_model->setHeaderData(i, Qt::Vertical, rowName, Qt::DisplayRole); } // 操作列最后一列不可编辑 int actionCol = headList.size() - 1; for (int row = 0; row < 32; ++row) { QStandardItem *item = new QStandardItem(QString()); item->setFlags(item->flags() & ~Qt::ItemIsEditable); m_model->setItem(row, actionCol, item); } m_tableView->setModel(m_model); m_tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Interactive); m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); m_tableView->setEditTriggers(QAbstractItemView::DoubleClicked); // 设置代理 DeviceParameterProxy *proxy = new DeviceParameterProxy(this); m_tableView->setItemDelegate(proxy); setupActionColumnButtons(); } void DeviceConfigView::setupActionColumnButtons() { int actionCol = m_model->columnCount() - 1; for (int row = 0; row < m_model->rowCount(); ++row) { QModelIndex idx_0 = m_model->index(row, 0); QModelIndex idx = m_model->index(row, actionCol); QPushButton *btn = new QPushButton(tr("应用"), m_tableView); btn->setProperty("row", row); connect(btn, &QPushButton::clicked, this, &DeviceConfigView::onActionButtonClicked); m_tableView->setIndexWidget(idx, btn); } } void DeviceConfigView::onActionButtonClicked() { QPushButton *btn = qobject_cast(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); }