#include "nmWxSensitive.h" #include "nmDataAnalyzeManager.h" #include "nmDataSensitive.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ========= 右对齐委托:保证显示和编辑时都是右对齐 ========= class RightAlignDelegate : public QStyledItemDelegate { public: explicit RightAlignDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {} void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override { QStyledItemDelegate::initStyleOption(option, index); option->displayAlignment = Qt::AlignRight | Qt::AlignVCenter; } QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override { QWidget *editor = QStyledItemDelegate::createEditor(parent, option, index); if (QLineEdit *line = qobject_cast(editor)) { line->setAlignment(Qt::AlignRight | Qt::AlignVCenter); } return editor; } }; nmWxSensitive::nmWxSensitive(QWidget *parent) : QDialog(parent), m_selectedValueIndex(0) // 初始化选中的值索引为0 { m_sensitiveData = nmDataAnalyzeManager::getCurrentInstance()->getSensitiveDataCopy(); setupUI(); createConnections(); populateTreeWidget(); createDetailPages(); loadDataToUI(); } nmWxSensitive::~nmWxSensitive() { } void nmWxSensitive::loadDataToUI() { // 1. 加载 Calculation Type calculationTypeCombo->setCurrentIndex( static_cast(m_sensitiveData.getCalculationType()) ); // 2. 加载总模型数 modelCountLabel->setText(QString::number(m_sensitiveData.getTotalModelCount())); // 3. 加载每个变量的数据 for (int i = 0; i < m_sensitiveData.getVariableCount(); ++i) { nmDataSensitive::VariableSampling& var = m_sensitiveData.getVariable(i); QString varName = var.getVarName(); bool enabled = var.getEnabled(); int mode = static_cast(var.getMode()); bool log = var.getLog(); int number = var.getNumber(); // 提前获取值,避免const问题 double modelValue = var.getModelValue().getValue().toDouble(); double minValue = var.getMinValue().getValue().toDouble(); double maxValue = var.getMaxValue().getValue().toDouble(); QString unit = var.getModelValue().getUnit(); // ⭐ 获取单位 QVector values = var.getValues(); // 在树中找到对应的项并设置勾选状态 for (int topIdx = 0; topIdx < variablesTree->topLevelItemCount(); ++topIdx) { QTreeWidgetItem *topItem = variablesTree->topLevelItem(topIdx); for (int childIdx = 0; childIdx < topItem->childCount(); ++childIdx) { QTreeWidgetItem *childItem = topItem->child(childIdx); if (childItem == nullptr) { continue; } if (childItem->text(0) == varName) { childItem->setCheckState(0, enabled ? Qt::Checked : Qt::Unchecked); break; } } } // 找到对应的控件索引 int ctrlIdx = findVariableIndex(varName); if (ctrlIdx < 0) continue; // 加载所有控件数据(从数据中心) m_modeCombos[ctrlIdx]->setCurrentIndex(mode); m_logChecks[ctrlIdx]->setChecked(log); m_numSpins[ctrlIdx]->setValue(number); m_modelSpins[ctrlIdx]->setValue(modelValue); m_minSpins[ctrlIdx]->setValue(minValue); m_maxSpins[ctrlIdx]->setValue(maxValue); // 设置单位标签 m_unitLabels[ctrlIdx]->setText(unit); // 如果是手动模式且有保存的values,加载到表格 if (mode == nmDataSensitive::VariableSampling::MODE_MANUAL && values.size() > 0) { QTableWidget *table = m_valueTables[ctrlIdx]; table->setRowCount(values.size()); for (int row = 0; row < values.size(); ++row) { // 创建箭头项 QTableWidgetItem *arrowItem = new QTableWidgetItem(); arrowItem->setFlags(Qt::ItemIsEnabled); arrowItem->setTextAlignment(Qt::AlignCenter); bool isSelected = (row == m_selectedValueIndex) && (m_selectedValueIndex < values.size()); arrowItem->setIcon(isSelected ? createTriangleIcon() : QIcon()); table->setItem(row, 0, arrowItem); // 创建值项 QTableWidgetItem *valueItem = new QTableWidgetItem(QString::number(values[row], 'f', 1)); valueItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); table->setItem(row, 1, valueItem); } } else { // 自动模式:调用 updateValuesForPage 计算并填充值 updateValuesForPage(ctrlIdx); } } } int nmWxSensitive::findVariableIndex(const QString& varName) { // 根据创建顺序查找变量索引 QStringList allVars; allVars << "Zw" << "Hw" << "Lw" << "Skin" << "C" // Tested Well << "Pi" << "k" << "h" << "φ" << "ntg" << "kz/kr" // Reservoir << "Total compressibility"; // Pvt return allVars.indexOf(varName); } void nmWxSensitive::saveDataFromUI() { // 1. 保存 Calculation Type m_sensitiveData.setCalculationType( static_cast( calculationTypeCombo->currentIndex() ) ); // 2. 计算并保存总模型数 int totalModels = 1; for (int i = 0; i < m_sensitiveData.getVariableCount(); ++i) { nmDataSensitive::VariableSampling& var = m_sensitiveData.getVariable(i); // 检查树中的勾选状态 bool enabled = false; QString varName = var.getVarName(); for (int topIdx = 0; topIdx < variablesTree->topLevelItemCount(); ++topIdx) { QTreeWidgetItem *topItem = variablesTree->topLevelItem(topIdx); for (int childIdx = 0; childIdx < topItem->childCount(); ++childIdx) { QTreeWidgetItem *childItem = topItem->child(childIdx); if (childItem == nullptr) { continue; } if (childItem->text(0) == varName) { enabled = (childItem->checkState(0) == Qt::Checked); break; } } } var.setEnabled(enabled); // 找到对应的控件索引 int ctrlIdx = findVariableIndex(varName); if (ctrlIdx < 0) continue; // 保存控件数据 var.setMode(static_cast( m_modeCombos[ctrlIdx]->currentIndex()) ); var.setLog(m_logChecks[ctrlIdx]->isChecked()); var.setNumber(m_numSpins[ctrlIdx]->value()); var.getModelValue().setValue(m_modelSpins[ctrlIdx]->value()); var.getMinValue().setValue(m_minSpins[ctrlIdx]->value()); var.getMaxValue().setValue(m_maxSpins[ctrlIdx]->value()); // 从表格保存 values QTableWidget *table = m_valueTables[ctrlIdx]; QVector values; for (int row = 0; row < table->rowCount(); ++row) { QTableWidgetItem *item = table->item(row, 1); if (item && !item->text().isEmpty()) { values.append(item->text().toDouble()); } } var.setValues(values); // 计算总模型数 if (enabled) { totalModels *= var.getNumber(); } } m_sensitiveData.setTotalModelCount(totalModels); modelCountLabel->setText(QString::number(totalModels)); } void nmWxSensitive::setupUI() { setWindowTitle(tr("Sensitivity")); setMinimumSize(900, 550); QVBoxLayout *mainLayout = new QVBoxLayout(this); // ===== 顶部:Calculation type ===== QHBoxLayout *calcTypeLayout = new QHBoxLayout(); QLabel *calcTypeLabel = new QLabel(tr("Calculation type:"), this); calculationTypeCombo = new QComboBox(this); calculationTypeCombo->addItem(tr("Deterministic")); calculationTypeCombo->addItem(tr("Deterministic multivariate")); //calculationTypeCombo->addItem(tr("Monte-Carlo")); //calculationTypeCombo->addItem(tr("Monte-Carlo + Improve")); calcTypeLayout->addWidget(calcTypeLabel); calcTypeLayout->addWidget(calculationTypeCombo); calcTypeLayout->addStretch(); mainLayout->addLayout(calcTypeLayout); // ===== 中间:左 Variables 面板 + 右 detailStack ===== QSplitter *centerSplitter = new QSplitter(Qt::Horizontal, this); centerSplitter->setChildrenCollapsible(false); // 不允许把某一侧完全折叠 centerSplitter->setHandleWidth(4); // 中间拖动条宽度 centerSplitter->setStyleSheet( "QSplitter::handle:horizontal {" " background-color: #C0C0C0;" "}" "QSplitter::handle:horizontal:hover {" " background-color: #A0A0A0;" "}" ); // --- 左侧:Variables 面板(QFrame + 标题栏 + Tree)--- QFrame *variablesPanel = new QFrame(this); variablesPanel->setFrameShape(QFrame::StyledPanel); variablesPanel->setFrameShadow(QFrame::Raised); variablesPanel->setMinimumWidth(230); QVBoxLayout *variablesPanelLayout = new QVBoxLayout(variablesPanel); variablesPanelLayout->setContentsMargins(0, 0, 0, 0); variablesPanelLayout->setSpacing(0); // 标题栏 QWidget *varHeader = new QWidget(variablesPanel); varHeader->setFixedHeight(24); varHeader->setAutoFillBackground(true); { QPalette pal = varHeader->palette(); pal.setColor(QPalette::Window, palette().color(QPalette::Midlight)); varHeader->setPalette(pal); } QHBoxLayout *varHeaderLayout = new QHBoxLayout(varHeader); varHeaderLayout->setContentsMargins(6, 0, 6, 0); varHeaderLayout->setSpacing(0); QLabel *varTitle = new QLabel(tr("Variables"), varHeader); varTitle->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); QFont f = varTitle->font(); f.setBold(true); varTitle->setFont(f); varHeaderLayout->addWidget(varTitle); varHeaderLayout->addStretch(); // Tree 区域 variablesTree = new QTreeWidget(variablesPanel); variablesTree->setHeaderHidden(true); variablesTree->setFrameShape(QFrame::NoFrame); // 外层已经有 frame variablesTree->setMinimumHeight(350); variablesPanelLayout->addWidget(varHeader); variablesPanelLayout->addWidget(variablesTree, 1); // 把左侧 panel 加进 splitter centerSplitter->addWidget(variablesPanel); // --- 右侧:detailStack(后面每个 page 自己做标题栏)--- detailStack = new QStackedWidget(this); detailStack->setContentsMargins(0, 0, 0, 0); // index 0:默认提示页 QWidget *blankPage = new QWidget(this); QVBoxLayout *blankLayout = new QVBoxLayout(blankPage); blankLayout->setContentsMargins(0, 0, 0, 0); QLabel *blankLabel = new QLabel(tr("Select a variable on the left."), blankPage); blankLayout->addStretch(); blankLayout->addWidget(blankLabel, 0, Qt::AlignHCenter); blankLayout->addStretch(); detailStack->addWidget(blankPage); // 把右侧 stack 加进 splitter centerSplitter->addWidget(detailStack); // 左边宽度基本固定,右边自适应 centerSplitter->setStretchFactor(0, 0); centerSplitter->setStretchFactor(1, 1); mainLayout->addWidget(centerSplitter, 1); // ===== 底部:Total number of models + 按钮 ===== QHBoxLayout *bottomLayout = new QHBoxLayout(); QLabel *totalLabel = new QLabel(tr("Total number of models:"), this); modelCountLabel = new QLabel(tr("0"), this); bottomLayout->addWidget(totalLabel); bottomLayout->addWidget(modelCountLabel); bottomLayout->addStretch(); generateButton = new QPushButton(tr("Generate"), this); cancelButton = new QPushButton(tr("Cancel"), this); bottomLayout->addWidget(generateButton); bottomLayout->addWidget(cancelButton); mainLayout->addLayout(bottomLayout); } void nmWxSensitive::createConnections() { connect(generateButton, SIGNAL(clicked()), this, SLOT(onGenerateClicked())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(onCancelClicked())); connect(calculationTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onCalculationTypeChanged(int))); connect(variablesTree, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(onTreeItemClicked(QTreeWidgetItem*,int))); } void nmWxSensitive::populateTreeWidget() { variablesTree->clear(); // ===== Tested Well ===== QTreeWidgetItem *testedWellItem = new QTreeWidgetItem(variablesTree); testedWellItem->setText(0, tr("Tested Well")); testedWellItem->setCheckState(0, Qt::Unchecked); testedWellItem->setFlags(testedWellItem->flags() | Qt::ItemIsUserCheckable); QStringList testedWellChildren; testedWellChildren << "Zw" << "Hw" << "Lw" << "Skin" << "C"; int i; for (i = 0; i < testedWellChildren.size(); ++i) { QTreeWidgetItem *childItem = new QTreeWidgetItem(testedWellItem); childItem->setText(0, testedWellChildren.at(i)); childItem->setCheckState(0, Qt::Unchecked); childItem->setFlags(childItem->flags() | Qt::ItemIsUserCheckable); } // ===== Reservoir ===== QTreeWidgetItem *reservoirItem = new QTreeWidgetItem(variablesTree); reservoirItem->setText(0, tr("Reservoir")); reservoirItem->setCheckState(0, Qt::Unchecked); reservoirItem->setFlags(reservoirItem->flags() | Qt::ItemIsUserCheckable); QStringList reservoirChildren; reservoirChildren << "Pi" << "k" << "h" << "φ" << "ntg" << "kz/kr"; for (i = 0; i < reservoirChildren.size(); ++i) { QTreeWidgetItem *childItem = new QTreeWidgetItem(reservoirItem); childItem->setText(0, reservoirChildren.at(i)); childItem->setCheckState(0, Qt::Unchecked); childItem->setFlags(childItem->flags() | Qt::ItemIsUserCheckable); } // ===== Pvt ===== QTreeWidgetItem *pvtItem = new QTreeWidgetItem(variablesTree); pvtItem->setText(0, tr("Pvt")); pvtItem->setCheckState(0, Qt::Unchecked); pvtItem->setFlags(pvtItem->flags() | Qt::ItemIsUserCheckable); QTreeWidgetItem *compressibilityItem = new QTreeWidgetItem(pvtItem); compressibilityItem->setText(0, tr("Total compressibility")); compressibilityItem->setCheckState(0, Qt::Unchecked); compressibilityItem->setFlags(compressibilityItem->flags() | Qt::ItemIsUserCheckable); variablesTree->expandAll(); } void nmWxSensitive::createDetailPages() { int i, j; // 依次为每个"叶子变量"创建一个页面,并把 index 存到 item->data 里 for (i = 0; i < variablesTree->topLevelItemCount(); ++i) { QTreeWidgetItem *topItem = variablesTree->topLevelItem(i); for (j = 0; j < topItem->childCount(); ++j) { QTreeWidgetItem *childItem = topItem->child(j); if (childItem == nullptr) { continue; } QString varName = childItem->text(0); QWidget *page = createVariablePage(varName); int pageIndex = detailStack->addWidget(page); // index 从 1 开始 childItem->setData(0, Qt::UserRole, pageIndex); } } } QIcon nmWxSensitive::createTriangleIcon() { QPixmap trianglePixmap(16, 16); trianglePixmap.fill(Qt::transparent); QPainter painter(&trianglePixmap); painter.setRenderHint(QPainter::Antialiasing, true); // 绘制小三角形(指向右的箭头) QPolygon triangle; triangle << QPoint(4, 6) << QPoint(4, 10) << QPoint(10, 8); painter.setBrush(QBrush(QColor(0, 0, 0))); // 黑色三角形 painter.setPen(QPen(QColor(0, 0, 0), 1)); painter.drawPolygon(triangle); return QIcon(trianglePixmap); } QWidget *nmWxSensitive::createVariablePage(const QString &varName) { QWidget *page = new QWidget(this); QHBoxLayout *pageLayout = new QHBoxLayout(page); pageLayout->setContentsMargins(0, 0, 0, 0); pageLayout->setSpacing(0); // ===== 整个 Sampling 面板:QFrame + 标题栏 + 内容 ===== QFrame *samplingPanel = new QFrame(page); samplingPanel->setFrameShape(QFrame::StyledPanel); samplingPanel->setFrameShadow(QFrame::Raised); QVBoxLayout *panelLayout = new QVBoxLayout(samplingPanel); panelLayout->setContentsMargins(0, 0, 0, 0); panelLayout->setSpacing(0); // ---- 标题栏:Sampling Xxx ---- QWidget *header = new QWidget(samplingPanel); header->setFixedHeight(24); header->setAutoFillBackground(true); { QPalette pal = header->palette(); pal.setColor(QPalette::Window, palette().color(QPalette::Midlight)); header->setPalette(pal); } QHBoxLayout *headerLayout = new QHBoxLayout(header); headerLayout->setContentsMargins(6, 0, 6, 0); headerLayout->setSpacing(0); QLabel *title = new QLabel(tr("Sampling %1").arg(varName), header); QFont tf = title->font(); tf.setBold(true); title->setFont(tf); headerLayout->addWidget(title); headerLayout->addStretch(); panelLayout->addWidget(header); // ---- 内容区:左参数 + 右表格 ---- QWidget *content = new QWidget(samplingPanel); QHBoxLayout *contentLayout = new QHBoxLayout(content); contentLayout->setContentsMargins(6, 0, 6, 6); contentLayout->setSpacing(12); // 左:参数区(GridLayout) QGridLayout *paramLayout = new QGridLayout(); paramLayout->setVerticalSpacing(6); int row = 0; // Mode + Log? QLabel *modeLabel = new QLabel(tr("Mode"), content); QComboBox *modeCombo = new QComboBox(content); modeCombo->addItem(tr("Automatic")); // 0 modeCombo->addItem(tr("Manual")); // 1 QCheckBox *logCheck = new QCheckBox(tr("Log?"), content); QWidget *modeRowWidget = new QWidget(content); QHBoxLayout *modeRowLayout = new QHBoxLayout(modeRowWidget); modeRowLayout->setContentsMargins(0, 0, 0, 0); modeRowLayout->addWidget(modeCombo); modeRowLayout->addWidget(logCheck); modeRowLayout->addStretch(); paramLayout->addWidget(modeLabel, row, 0); paramLayout->addWidget(modeRowWidget, row, 1); ++row; // Number QLabel *numLabel = new QLabel(tr("Number"), content); QSpinBox *numSpin = new QSpinBox(content); numSpin->setRange(1, 1000); //numSpin->setValue(1); numSpin->setButtonSymbols(QAbstractSpinBox::NoButtons); paramLayout->addWidget(numLabel, row, 0); paramLayout->addWidget(numSpin, row, 1); ++row; // 空行(Number 和 Model value 之间) int gapRow = row; paramLayout->setRowMinimumHeight(gapRow, 12); ++row; // Model value + 单位 QLabel *modelLabel = new QLabel(tr("Model value:"), content); QDoubleSpinBox *modelSpin = new QDoubleSpinBox(content); modelSpin->setDecimals(4); modelSpin->setRange(-1e9, 1e9); //modelSpin->setValue(0.0); modelSpin->setButtonSymbols(QAbstractSpinBox::NoButtons); QLabel *unitLabel = new QLabel(tr(""), content); // ⭐ 单位也从数据加载 QWidget *modelRowWidget = new QWidget(content); QHBoxLayout *modelRowLayout = new QHBoxLayout(modelRowWidget); modelRowLayout->setContentsMargins(0, 0, 0, 0); modelRowLayout->addWidget(modelSpin); modelRowLayout->addWidget(unitLabel); modelRowLayout->addStretch(); paramLayout->addWidget(modelLabel, row, 0); paramLayout->addWidget(modelRowWidget, row, 1); ++row; // Minimum QLabel *minLabel = new QLabel(tr("Minimum:"), content); QDoubleSpinBox *minSpin = new QDoubleSpinBox(content); minSpin->setDecimals(4); minSpin->setRange(-1e9, 1e9); //minSpin->setValue(0.0); minSpin->setButtonSymbols(QAbstractSpinBox::NoButtons); paramLayout->addWidget(minLabel, row, 0); paramLayout->addWidget(minSpin, row, 1); ++row; // Maximum QLabel *maxLabel = new QLabel(tr("Maximum:"), content); QDoubleSpinBox *maxSpin = new QDoubleSpinBox(content); maxSpin->setDecimals(4); maxSpin->setRange(-1e9, 1e9); //maxSpin->setValue(0.0); maxSpin->setButtonSymbols(QAbstractSpinBox::NoButtons); paramLayout->addWidget(maxLabel, row, 0); paramLayout->addWidget(maxSpin, row, 1); ++row; // 统一编辑框宽度 int fullWidth = modelSpin->sizeHint().width(); int editWidth = fullWidth * 3 / 4; modeCombo->setFixedWidth(editWidth); numSpin->setFixedWidth(editWidth); modelSpin->setFixedWidth(editWidth); minSpin->setFixedWidth(editWidth); maxSpin->setFixedWidth(editWidth); modeCombo->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); numSpin->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); modelSpin->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); minSpin->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); maxSpin->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QWidget *paramWidget = new QWidget(content); QVBoxLayout *leftVBox = new QVBoxLayout(paramWidget); leftVBox->setContentsMargins(0, 6, 0, 0); leftVBox->addLayout(paramLayout); leftVBox->addStretch(); // 右:Values 表格(双列:箭头 + 值) QTableWidget *valuesTable = new QTableWidget(content); valuesTable->setColumnCount(2); QStringList headers; headers << "" << tr("Values"); valuesTable->setHorizontalHeaderLabels(headers); valuesTable->verticalHeader()->setVisible(false); valuesTable->setColumnWidth(0, 15); valuesTable->setColumnWidth(1, 130); QHeaderView *hh = valuesTable->horizontalHeader(); hh->setResizeMode(QHeaderView::Fixed); hh->setHighlightSections(false); valuesTable->setStyleSheet( "QHeaderView::section {" " border-top: 0px;" " border-left: 0px;" " border-right: 1px solid lightgray;" " border-bottom: 1px solid lightgray;" "}" "QTableCornerButton::section {" " border-top: 0px;" " border-left: 0px;" " border-right: 1px solid lightgray;" " border-bottom: 1px solid lightgray;" "}" ); valuesTable->horizontalHeader()->setStretchLastSection(false); valuesTable->setEditTriggers(QAbstractItemView::DoubleClicked); valuesTable->setSelectionBehavior(QAbstractItemView::SelectRows); valuesTable->setSelectionMode(QAbstractItemView::NoSelection); valuesTable->setFocusPolicy(Qt::NoFocus); valuesTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); valuesTable->setShowGrid(true); valuesTable->setItemDelegateForColumn(1, new RightAlignDelegate(valuesTable)); int tableWidth = valuesTable->frameWidth() * 2 + valuesTable->verticalHeader()->sizeHint().width() + valuesTable->columnWidth(0) + valuesTable->columnWidth(1); valuesTable->setFixedWidth(tableWidth); QWidget *tableContainer = new QWidget(content); QVBoxLayout *tableLayout = new QVBoxLayout(tableContainer); tableLayout->setContentsMargins(0, 0, 0, 0); tableLayout->setSpacing(0); tableLayout->addWidget(valuesTable); tableContainer->setFixedWidth(tableWidth); contentLayout->addWidget(paramWidget, 1); contentLayout->addStretch(); contentLayout->addWidget(tableContainer, 0, Qt::AlignRight); panelLayout->addWidget(content); pageLayout->addWidget(samplingPanel); // ===== 保存控件指针 ===== m_modeCombos.append(modeCombo); m_logChecks.append(logCheck); m_numSpins.append(numSpin); m_modelSpins.append(modelSpin); m_minSpins.append(minSpin); m_maxSpins.append(maxSpin); m_valueTables.append(valuesTable); m_unitLabels.append(unitLabel); connect(numSpin, SIGNAL(valueChanged(int)), this, SLOT(onVariableParamChanged())); connect(minSpin, SIGNAL(valueChanged(double)), this, SLOT(onVariableParamChanged())); connect(maxSpin, SIGNAL(valueChanged(double)), this, SLOT(onVariableParamChanged())); connect(modeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onVariableParamChanged())); connect(logCheck, SIGNAL(toggled(bool)), this, SLOT(onVariableParamChanged())); connect(valuesTable, SIGNAL(cellClicked(int,int)), this, SLOT(onTableCellClicked(int,int))); return page; } void nmWxSensitive::updateValuesForPage(int idx) { if (idx < 0 || idx >= m_numSpins.size()) return; QSpinBox *numSpin = m_numSpins.at(idx); QDoubleSpinBox *minSpin = m_minSpins.at(idx); QDoubleSpinBox *maxSpin = m_maxSpins.at(idx); QDoubleSpinBox *modelSpin = m_modelSpins.at(idx); QComboBox *modeCombo = m_modeCombos.at(idx); QCheckBox *logCheck = m_logChecks.at(idx); QTableWidget *table = m_valueTables.at(idx); int n = numSpin->value(); if (n <= 0) n = 1; table->setRowCount(n); // 为每行创建箭头项,根据当前选中索引决定是否显示箭头 for (int i = 0; i < n; ++i) { QTableWidgetItem *arrowItem = table->item(i, 0); if (!arrowItem) { arrowItem = new QTableWidgetItem(); arrowItem->setFlags(Qt::ItemIsEnabled); // 不可编辑 arrowItem->setTextAlignment(Qt::AlignCenter); table->setItem(i, 0, arrowItem); } // 根据是否为当前选中行设置箭头图标 bool isSelected = (i == m_selectedValueIndex); arrowItem->setIcon(isSelected ? createTriangleIcon() : QIcon()); arrowItem->setText(""); } if (modeCombo->currentIndex() != 0) { // Manual 模式:只控制行数,不自动填充数值 for (int i = 0; i < n; ++i) { QTableWidgetItem *valueItem = table->item(i, 1); if (!valueItem) { valueItem = new QTableWidgetItem(""); valueItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); table->setItem(i, 1, valueItem); } } return; } double minVal = minSpin->value(); double maxVal = maxSpin->value(); if (n == 1) { double v = modelSpin->value(); QTableWidgetItem *item = new QTableWidgetItem(QString::number(v, 'f', 1)); item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); table->setItem(0, 1, item); return; } bool logMode = logCheck->isChecked(); int i; if (logMode && minVal > 0.0 && maxVal > 0.0) { double logMin = log10(minVal); double logMax = log10(maxVal); for (i = 0; i < n; ++i) { double t = double(i) / double(n - 1); double v = pow(10.0, logMin + (logMax - logMin) * t); QTableWidgetItem *item = new QTableWidgetItem(QString::number(v, 'f', 1)); item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); table->setItem(i, 1, item); } } else { // 线性 for (i = 0; i < n; ++i) { double t = double(i) / double(n - 1); double v = minVal + (maxVal - minVal) * t; QTableWidgetItem *item = new QTableWidgetItem(QString::number(v, 'f', 1)); item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); table->setItem(i, 1, item); } } } void nmWxSensitive::onVariableParamChanged() { QObject *s = sender(); int i, idx = -1; for (i = 0; i < m_numSpins.size(); ++i) { if (s == m_numSpins.at(i) || s == m_minSpins.at(i) || s == m_maxSpins.at(i) || s == m_modeCombos.at(i) || s == m_logChecks.at(i)) { idx = i; break; } } if (idx >= 0) { // 重置选中索引,确保在有效范围内 int newCount = m_numSpins.at(idx)->value(); if (m_selectedValueIndex >= newCount) { m_selectedValueIndex = qMax(0, newCount - 1); } updateValuesForPage(idx); } } // 处理表格单元格点击事件 void nmWxSensitive::onTableCellClicked(int row, int column) { Q_UNUSED(column); QObject *s = sender(); QTableWidget *table = qobject_cast(s); if (!table) return; // 更新选中的值索引 m_selectedValueIndex = row; // 重新更新表格以反映新的选中状态 // 找到这个表格对应的页面索引 int pageIdx = -1; for (int i = 0; i < m_valueTables.size(); ++i) { if (m_valueTables.at(i) == table) { pageIdx = i; break; } } if (pageIdx >= 0) { updateValuesForPage(pageIdx); } } void nmWxSensitive::onGenerateClicked() { // 保存界面数据到 m_sensitiveData saveDataFromUI(); // 更新数据到数据中心 nmDataAnalyzeManager::getCurrentInstance()->updateSensitiveData(m_sensitiveData); // 关闭对话框 accept(); } void nmWxSensitive::onCancelClicked() { reject(); } void nmWxSensitive::onCalculationTypeChanged(int index) { if (index == 0) { // Deterministic } else { // Probabilistic } } void nmWxSensitive::onTreeItemClicked(QTreeWidgetItem *item, int column) { Q_UNUSED(column); if (item->childCount() == 0) { QVariant v = item->data(0, Qt::UserRole); if (v.isValid()) { int pageIndex = v.toInt(); detailStack->setCurrentIndex(pageIndex); // 切换页面时重置选中索引为0 m_selectedValueIndex = 0; // 找到对应的页面索引并更新表格 int tableIdx = pageIndex - 1; // 减去默认页面的索引 if (tableIdx >= 0 && tableIdx < m_valueTables.size()) { updateValuesForPage(tableIdx); } return; } } // 父节点等:回到默认页 detailStack->setCurrentIndex(0); }