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

903 lines
29 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "nmWxSensitive.h"
#include "nmDataAnalyzeManager.h"
#include "nmDataSensitive.h"
#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)
: iDlgBase(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);
}