You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmSubWxs/nmWxSensitive.cpp

904 lines
29 KiB
C++

#include "nmWxSensitive.h"
#include "nmDataAnalyzeManager.h"
#include "nmDataSensitive.h"
#include <QDialog>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QComboBox>
#include <QLabel>
#include <QPushButton>
#include <QStackedWidget>
#include <QCheckBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QTableWidget>
#include <QHeaderView>
#include <QSplitter>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QGroupBox>
#include <QList>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QAbstractSpinBox>
#include <QMessageBox>
#include <QWidget>
#include <QPainter>
#include <QPixmap>
#include <QIcon>
#include <QStyledItemDelegate>
#include <QLineEdit>
#include <math.h>
// ========= 右对齐委托:保证显示和编辑时都是右对齐 =========
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<QLineEdit*>(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<int>(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<int>(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<double> 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<nmDataSensitive::CalculationType>(
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<nmDataSensitive::VariableSampling::Mode>(
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<double> 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<QTableWidget*>(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);
}