#include "nmWxForecast.h" #include #include #include #include #include nmWxForecast::nmWxForecast(QWidget *parent) : iDlgBase(parent) , m_initialPressureComponent(nullptr) , m_abandonmentRateComponent(nullptr) , m_maximumRateComponent(nullptr) , m_forecastDurationComponent(nullptr) , m_producingPressureComponent(nullptr) , m_producingPressureEndComponent(nullptr) , m_pressureChangePeriodComponent(nullptr) , m_pressureTableWidget(nullptr) , m_pDurationUnitCombo(nullptr) , m_pPressureUnitCombo(nullptr) , m_rateTableWidget(nullptr) , m_rDurationUnitCombo(nullptr) , m_rRateUnitCombo(nullptr) , m_extMultiR(nullptr) , m_extStackedWidget(nullptr) , m_extEmpty(nullptr) { setWindowTitle("Forecast"); setFixedSize(520, 560); PressureRow tempDefaults; m_currentDurationUnit = tempDefaults.duration.getUnit(); m_currentPressureUnit = tempDefaults.pressure.getUnit(); FlowRow tempFlowDefaults; m_currentRateUnit = tempFlowDefaults.q0.getUnit(); // 先构建UI setupUI(); setupConnections(); // 从数据中心加载数据 loadDataFromManager(); // 根据加载的数据更新界面 updateParametersForOption(); } nmWxForecast::~nmWxForecast() { } void nmWxForecast::loadDataFromManager() { // 从数据中心获取预测数据的副本 nmDataAnalyzeManager* pManager = nmDataAnalyzeManager::getCurrentInstance(); if(pManager) { m_forecastData = pManager->getForecastDataCopy(); // 根据数据设置单选按钮 nmDataForecast::ForecastOption option = m_forecastData.getForecastOption(); switch(option) { case nmDataForecast::ConstantPressureOption: m_constantPressureRadio->setChecked(true); break; case nmDataForecast::DecliningPressureOption: m_decliningPressureRadio->setChecked(true); break; case nmDataForecast::MultiplePressuresOption: m_multiplePressuresRadio->setChecked(true); break; case nmDataForecast::MultipleRatesOption: m_multipleRatesRadio->setChecked(true); break; } } else { // 如果数据中心不存在,使用默认数据 m_constantPressureRadio->setChecked(true); m_forecastData.setForecastOption(nmDataForecast::ConstantPressureOption); } } void nmWxForecast::saveDataToManager() { // 先从界面更新数据 updateDataFromUI(); // 保存到数据中心 nmDataAnalyzeManager* pManager = nmDataAnalyzeManager::getCurrentInstance(); if(pManager) { pManager->updateForecastData(m_forecastData); } } void nmWxForecast::setupUI() { m_mainLayout = new QVBoxLayout(this); // ========== Forecast options ========== m_forecastOptionsGroup = new QGroupBox("Forecast options", this); m_forecastOptionsLayout = new QVBoxLayout(m_forecastOptionsGroup); m_constantPressureRadio = new QRadioButton("Constant pressure", this); m_decliningPressureRadio = new QRadioButton("Declining pressure (ramp)", this); m_multiplePressuresRadio = new QRadioButton("Multiple pressures", this); m_multipleRatesRadio = new QRadioButton("Multiple rates", this); m_forecastButtonGroup = new QButtonGroup(this); m_forecastButtonGroup->addButton(m_constantPressureRadio, 0); m_forecastButtonGroup->addButton(m_decliningPressureRadio, 1); m_forecastButtonGroup->addButton(m_multiplePressuresRadio, 2); m_forecastButtonGroup->addButton(m_multipleRatesRadio, 3); m_usePastHistoryCheck = new QCheckBox("Use past history", this); m_surfacePressuresCheck = new QCheckBox("Surface pressures", this); m_usePastHistoryCheck->setEnabled(false); m_surfacePressuresCheck->setEnabled(false); m_forecastOptionsLayout->addWidget(m_constantPressureRadio); m_forecastOptionsLayout->addWidget(m_decliningPressureRadio); m_forecastOptionsLayout->addWidget(m_multiplePressuresRadio); m_forecastOptionsLayout->addWidget(m_multipleRatesRadio); QHBoxLayout *checkBoxLayout = new QHBoxLayout(); checkBoxLayout->addWidget(m_usePastHistoryCheck); checkBoxLayout->addWidget(m_surfacePressuresCheck); checkBoxLayout->addStretch(); m_forecastOptionsLayout->addLayout(checkBoxLayout); // ========== Parameters ========== m_parametersGroup = new QGroupBox("Parameters", this); QVBoxLayout *parametersOuterLayout = new QVBoxLayout(m_parametersGroup); parametersOuterLayout->setContentsMargins(6, 6, 6, 6); // 基础区 QWidget *baseWidget = new QWidget(this); m_parametersLayout = new QGridLayout(baseWidget); m_parametersLayout->setHorizontalSpacing(8); m_parametersLayout->setVerticalSpacing(8); m_parametersLayout->setColumnStretch(0, 0); // 复选框列 m_parametersLayout->setColumnStretch(1, 1); // 组件列(会自动扩展) int row = 0; // 1. 创建一个“占位符” (strut),用于永久撑开第0列 QWidget* column0Strut = new QWidget(this); // 2. 将占位符添加到 (row 0, col 0) m_parametersLayout->addWidget(column0Strut, row, 0); // (row 0) Initial pressure ConstantPressure& constantPressureData = m_forecastData.getConstantPressureData(); m_initialPressureComponent = new nmGUIComponentLineEdit( &constantPressureData.getInitialPressure(), true, this ); m_parametersLayout->addWidget(m_initialPressureComponent, row, 1); row++; // (row 1) Abandonment rate m_abandonmentRateCheck = new QCheckBox(this); m_abandonmentRateComponent = new nmGUIComponentLineEdit(&constantPressureData.getAbandonmentRate(), true, this); // 创建 QStackedWidget 来包裹 组件 m_abandonmentStack = new QStackedWidget(this); m_abandonmentSpacer = new QWidget(this); // 使用组件的高度来设置占位符高度 m_abandonmentSpacer->setFixedHeight(m_abandonmentRateComponent->sizeHint().height()); m_abandonmentStack->addWidget(m_abandonmentRateComponent); // 第 0 页 (组件) m_abandonmentStack->addWidget(m_abandonmentSpacer); // 第 1 页 (占位符) m_parametersLayout->addWidget(m_abandonmentRateCheck, row, 0); // 将复选框添加到第 0 列 m_parametersLayout->addWidget(m_abandonmentStack, row, 1); // 将堆栈(内含组件)添加到第 1 列 row++; // (row 2) Maximum rate m_maximumRateCheck = new QCheckBox(this); m_maximumRateComponent = new nmGUIComponentLineEdit(&m_forecastData.getConstantPressureData().getMaximumRate(), true, this); // 创建 QStackedWidget 来包裹组件 m_maximumStack = new QStackedWidget(this); m_maximumSpacer = new QWidget(this); m_maximumSpacer->setFixedHeight(m_maximumRateComponent->sizeHint().height()); m_maximumStack->addWidget(m_maximumRateComponent); // 第 0 页 (组件) m_maximumStack->addWidget(m_maximumSpacer); // 第 1 页 (占位符) m_parametersLayout->addWidget(m_maximumRateCheck, row, 0); // 将复选框添加到第 0 列 m_parametersLayout->addWidget(m_maximumStack, row, 1); // 将堆栈(内含组件)添加到第 1 列 row++; // (row 3) Forecast duration m_forecastDurationComponent = new nmGUIComponentLineEdit(&m_forecastData.getConstantPressureData().getForecastDuration(), true, this); m_parametersLayout->addWidget(m_forecastDurationComponent, row, 1); row++; // 设置占位符的固定宽度,使其等于复选框的宽度 int chkBoxWidth = m_abandonmentRateCheck->minimumSizeHint().width(); column0Strut->setFixedWidth(chkBoxWidth); // (row 4) Producing pressure m_producingPressureComponent = new nmGUIComponentLineEdit(&m_forecastData.getConstantPressureData().getProducingPressure(), true, this); m_parametersLayout->addWidget(m_producingPressureComponent, row, 1); row++; // ========== 扩展区:固定位置添加所有扩展组件 ========== // --- 1. 先构建所有扩展页面 --- // (row 5) Ramp 扩展组件 (原row 5) m_extRamp = buildExtRampPage(); // (row 6) Multiple Pressures 表格 (原row 6) m_extMultiP = buildExtMultiPressurePage(); m_extMultiP->setMinimumHeight(80); // (row 7) Multiple Rates 表格 m_extMultiR = buildExtMultiRatePage(); m_extMultiR->setMinimumHeight(80); // --- 2. 创建一个空页面--- m_extEmpty = buildExtEmptyPage(""); // --- 3. 创建 QStackedWidget 并添加所有页面 --- m_extStackedWidget = new QStackedWidget(this); m_extStackedWidget->addWidget(m_extEmpty); // 页面 0 m_extStackedWidget->addWidget(m_extRamp); // 页面 1 m_extStackedWidget->addWidget(m_extMultiP); // 页面 2 m_extStackedWidget->addWidget(m_extMultiR); // 页面 3 // --- 4. 将 QStackedWidget 添加到布局的固定位置 (row 5) --- m_parametersLayout->addWidget(m_extStackedWidget, row, 1); row++; // row 变为 6 parametersOuterLayout->addWidget(baseWidget); //parametersOuterLayout->addWidget(m_extStackedWidget); // ========== Buttons ========== m_buttonLayout = new QHBoxLayout(); m_loadDataButton = new QPushButton("Load data", this); m_generateButton = new QPushButton("Generate", this); m_cancelButton = new QPushButton("Cancel", this); m_loadDataButton->setEnabled(false); m_buttonLayout->addWidget(m_loadDataButton); m_buttonLayout->addStretch(); m_buttonLayout->addWidget(m_generateButton); m_buttonLayout->addWidget(m_cancelButton); // ========== Main ========== m_mainLayout->addWidget(m_forecastOptionsGroup); m_mainLayout->addWidget(m_parametersGroup); m_mainLayout->addStretch(); m_mainLayout->addLayout(m_buttonLayout); // 收集所有组件 QList allComponents; allComponents.append(m_initialPressureComponent); allComponents.append(m_abandonmentRateComponent); allComponents.append(m_maximumRateComponent); allComponents.append(m_forecastDurationComponent); allComponents.append(m_producingPressureComponent); allComponents.append(m_producingPressureEndComponent); allComponents.append(m_pressureChangePeriodComponent); // 计算最大标签宽度 int maxLabelWidth = 0; for(int i = 0; i < allComponents.size(); ++i) { if(allComponents.at(i)) { // 获取标签控件(假设是第一个子控件) QLabel* label = allComponents.at(i)->findChild(); if(label) { int w = label->sizeHint().width(); if(w > maxLabelWidth) { maxLabelWidth = w; } } } } // 设置统一的标签宽度(+10 像素余量) for(int i = 0; i < allComponents.size(); ++i) { if(allComponents.at(i)) { QLabel* label = allComponents.at(i)->findChild(); if(label) { label->setMinimumWidth(maxLabelWidth + 10); label->setMaximumWidth(maxLabelWidth + 10); } } } } QWidget* nmWxForecast::buildExtEmptyPage(const QString& text) { QWidget *root = new QWidget(this); QVBoxLayout *v = new QVBoxLayout(root); v->setContentsMargins(0, 0, 0, 0); if(!text.isEmpty()) { QLabel *lbl = new QLabel(text, root); lbl->setAlignment(Qt::AlignCenter); v->addStretch(); v->addWidget(lbl); v->addStretch(); } else { v->addSpacing(1); } root->setMinimumHeight(80); return root; } QWidget* nmWxForecast::buildExtMultiPressurePage() { QWidget *root = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(root); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(8); // 创建表格 m_pressureTableWidget = new QTableWidget(0, 2); m_pressureTableWidget->setHorizontalHeaderLabels(QStringList() << "Duration" << "Pressure"); m_pressureTableWidget->verticalHeader()->setVisible(false); m_pressureTableWidget->horizontalHeader()->setResizeMode(QHeaderView::Stretch); // 表头下的“单位”行 m_pressureTableWidget->insertRow(0); m_pressureTableWidget->setRowHeight(0, 24); // 设置一个合适的高度 PressureRow tempDefaults; // 用于获取单位列表 QStringList durationUnits = tempDefaults.duration.getListUnitSelections(); QStringList pressureUnits = tempDefaults.pressure.getListUnitSelections(); // --- 第0列:Duration 单位下拉框 --- m_pDurationUnitCombo = new QComboBox(m_pressureTableWidget); if(!durationUnits.isEmpty()) { m_pDurationUnitCombo->addItems(durationUnits); } m_pDurationUnitCombo->setCurrentIndex(m_pDurationUnitCombo->findText(m_currentDurationUnit)); // 设置当前单位 // 设置样式 m_pDurationUnitCombo->setEditable(true); m_pDurationUnitCombo->lineEdit()->setAlignment(Qt::AlignCenter); m_pDurationUnitCombo->lineEdit()->setReadOnly(true); m_pDurationUnitCombo->setStyleSheet( "QComboBox { border: none; background: transparent; padding-left: 4px; }" "QComboBox::drop-down { border: none; }" ); m_pressureTableWidget->setCellWidget(0, 0, m_pDurationUnitCombo); // --- 第1列:Pressure 单位下拉框 --- m_pPressureUnitCombo = new QComboBox(m_pressureTableWidget); if(!pressureUnits.isEmpty()) { m_pPressureUnitCombo->addItems(pressureUnits); } m_pPressureUnitCombo->setCurrentIndex(m_pPressureUnitCombo->findText(m_currentPressureUnit)); // 设置当前单位 // 设置样式 m_pPressureUnitCombo->setEditable(true); m_pPressureUnitCombo->lineEdit()->setAlignment(Qt::AlignCenter); m_pPressureUnitCombo->lineEdit()->setReadOnly(true); m_pPressureUnitCombo->setStyleSheet( "QComboBox { border: none; background: transparent; padding-left: 4px; }" "QComboBox::drop-down { border: none; }" ); m_pressureTableWidget->setCellWidget(0, 1, m_pPressureUnitCombo); layout->addWidget(m_pressureTableWidget); return root; } QTableWidgetItem* nmWxForecast::makeCenteredEmptyItem() { QTableWidgetItem* it = new QTableWidgetItem(); it->setTextAlignment(Qt::AlignCenter); return it; } QWidget* nmWxForecast::buildExtMultiRatePage() { QWidget *root = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(root); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(8); // 创建表格 m_rateTableWidget = new QTableWidget(0, 2); // 根据截图设置表头 m_rateTableWidget->setHorizontalHeaderLabels(QStringList() << "Duration" << "Qo"); m_rateTableWidget->verticalHeader()->setVisible(false); m_rateTableWidget->horizontalHeader()->setResizeMode(QHeaderView::Stretch); // 表头下的“单位”行 m_rateTableWidget->insertRow(0); m_rateTableWidget->setRowHeight(0, 24); FlowRow tempDefaults; // 使用 FlowRow 获取单位 QStringList durationUnits = tempDefaults.duration.getListUnitSelections(); QStringList rateUnits = tempDefaults.q0.getListUnitSelections(); // 获取速率单位 // --- 第0列:Duration 单位下拉框 --- m_rDurationUnitCombo = new QComboBox(m_rateTableWidget); if(!durationUnits.isEmpty()) { m_rDurationUnitCombo->addItems(durationUnits); } m_rDurationUnitCombo->setCurrentIndex(m_rDurationUnitCombo->findText(m_currentDurationUnit)); m_rDurationUnitCombo->setEditable(true); m_rDurationUnitCombo->lineEdit()->setAlignment(Qt::AlignCenter); m_rDurationUnitCombo->lineEdit()->setReadOnly(true); m_rDurationUnitCombo->setStyleSheet( "QComboBox { border: none; background: transparent; padding-left: 4px; }" "QComboBox::drop-down { border: none; }" ); m_rateTableWidget->setCellWidget(0, 0, m_rDurationUnitCombo); // --- 第1列:Rate (Qo) 单位下拉框 --- m_rRateUnitCombo = new QComboBox(m_rateTableWidget); if(!rateUnits.isEmpty()) { m_rRateUnitCombo->addItems(rateUnits); } m_rRateUnitCombo->setCurrentIndex(m_rRateUnitCombo->findText(m_currentRateUnit)); // 设置当前速率单位 m_rRateUnitCombo->setEditable(true); m_rRateUnitCombo->lineEdit()->setAlignment(Qt::AlignCenter); m_rRateUnitCombo->lineEdit()->setReadOnly(true); m_rRateUnitCombo->setStyleSheet( "QComboBox { border: none; background: transparent; padding-left: 4px; }" "QComboBox::drop-down { border: none; }" ); m_rateTableWidget->setCellWidget(0, 1, m_rRateUnitCombo); layout->addWidget(m_rateTableWidget); return root; } QWidget* nmWxForecast::buildExtRampPage() { QWidget *root = new QWidget(this); root->setContentsMargins(0, 0, 0, 0); QVBoxLayout *layout = new QVBoxLayout(root); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(8); // 与基础区一致 // Producing pressure at end m_producingPressureEndComponent = new nmGUIComponentLineEdit( &m_forecastData.getDecliningPressureData().getProducingPressureEnd(), true, root ); layout->addWidget(m_producingPressureEndComponent); // Pressure change period m_pressureChangePeriodComponent = new nmGUIComponentLineEdit( &m_forecastData.getDecliningPressureData().getPressureChangePeriod(), true, root ); layout->addWidget(m_pressureChangePeriodComponent); layout->addStretch(); // 底部弹性空间 root->setMinimumHeight(80); return root; } void nmWxForecast::setupConnections() { // Forecast option connections connect(m_constantPressureRadio, SIGNAL(clicked()), this, SLOT(onConstantPressureSelected())); connect(m_decliningPressureRadio, SIGNAL(clicked()), this, SLOT(onDecliningPressureSelected())); connect(m_multiplePressuresRadio, SIGNAL(clicked()), this, SLOT(onMultiplePressuresSelected())); connect(m_multipleRatesRadio, SIGNAL(clicked()), this, SLOT(onMultipleRatesSelected())); connect(m_usePastHistoryCheck, SIGNAL(toggled(bool)), this, SLOT(onUsePastHistoryToggled(bool))); connect(m_surfacePressuresCheck, SIGNAL(toggled(bool)), this, SLOT(onSurfacePressuresToggled(bool))); // 复选框联动 connect(m_abandonmentRateCheck, SIGNAL(toggled(bool)), this, SLOT(onAbandonmentRateCheckToggled(bool))); connect(m_maximumRateCheck, SIGNAL(toggled(bool)), this, SLOT(onMaximumRateCheckToggled(bool))); // Buttons connect(m_loadDataButton, SIGNAL(clicked()), this, SLOT(onLoadDataClicked())); connect(m_generateButton, SIGNAL(clicked()), this, SLOT(onGenerateClicked())); connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(onCancelClicked())); // Pressure Table if(m_pressureTableWidget) { connect(m_pressureTableWidget, SIGNAL(cellChanged(int, int)), this, SLOT(onPressureCellChanged(int, int))); } if(m_pDurationUnitCombo) { connect(m_pDurationUnitCombo, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(onSharedDurationUnitChanged(const QString &))); } if(m_pPressureUnitCombo) { connect(m_pPressureUnitCombo, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(onPressureUnitChanged(const QString &))); } // Rate Table Connections if(m_rateTableWidget) { connect(m_rateTableWidget, SIGNAL(cellChanged(int, int)), this, SLOT(onRateCellChanged(int, int))); } if(m_rDurationUnitCombo) { connect(m_rDurationUnitCombo, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(onSharedDurationUnitChanged(const QString &))); } if(m_rRateUnitCombo) { connect(m_rRateUnitCombo, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(onRateUnitChanged(const QString &))); } } void nmWxForecast::onAbandonmentRateCheckToggled(bool checked) { if(m_abandonmentRateComponent) { m_abandonmentRateComponent->setEnabled(checked); } // 同步到数据模型 nmDataForecast::ForecastOption option = m_forecastData.getForecastOption(); // Get current option if(option == nmDataForecast::ConstantPressureOption) { m_forecastData.getConstantPressureData().setIsAbandonmentRate(checked); } else if(option == nmDataForecast::DecliningPressureOption) { m_forecastData.getDecliningPressureData().setIsAbandonmentRate(checked); } else if(option == nmDataForecast::MultiplePressuresOption) { m_forecastData.getMultiplePressuresData().setIsAbandonmentRate(checked); } } void nmWxForecast::onMaximumRateCheckToggled(bool checked) { if(m_maximumRateComponent) { m_maximumRateComponent->setEnabled(checked); } // 同步到数据模型 nmDataForecast::ForecastOption option = m_forecastData.getForecastOption(); // Get current option if(option == nmDataForecast::ConstantPressureOption) { m_forecastData.getConstantPressureData().setIsMaximumRate(checked); } else if(option == nmDataForecast::DecliningPressureOption) { m_forecastData.getDecliningPressureData().setIsMaximumRate(checked); } else if(option == nmDataForecast::MultiplePressuresOption) { m_forecastData.getMultiplePressuresData().setIsMaximumRate(checked); } } void nmWxForecast::onConstantPressureSelected() { updateDataFromUI(); // 保存当前界面数据 m_forecastData.setForecastOption(nmDataForecast::ConstantPressureOption); updateParametersForOption(); } void nmWxForecast::onDecliningPressureSelected() { updateDataFromUI(); // 保存当前界面数据 m_forecastData.setForecastOption(nmDataForecast::DecliningPressureOption); updateParametersForOption(); } void nmWxForecast::onMultiplePressuresSelected() { updateDataFromUI(); // 保存当前界面数据 m_forecastData.setForecastOption(nmDataForecast::MultiplePressuresOption); updateParametersForOption(); } void nmWxForecast::onMultipleRatesSelected() { updateDataFromUI(); // 保存当前界面数据 m_forecastData.setForecastOption(nmDataForecast::MultipleRatesOption); updateParametersForOption(); } void nmWxForecast::onUsePastHistoryToggled(bool checked) { Q_UNUSED(checked); } void nmWxForecast::onSurfacePressuresToggled(bool checked) { Q_UNUSED(checked); } void nmWxForecast::updateParametersForOption() { this->setUpdatesEnabled(false); const nmDataForecast::ForecastOption option = m_forecastData.getForecastOption(); // 默认显示所有基本组件 if (m_abandonmentRateCheck) m_abandonmentRateCheck->setVisible(true); if (m_abandonmentStack) m_abandonmentStack->setCurrentIndex(0); if (m_maximumRateCheck) m_maximumRateCheck->setVisible(true); if (m_maximumStack) m_maximumStack->setCurrentIndex(0); if (m_forecastDurationComponent) m_forecastDurationComponent->setVisible(true); if (m_producingPressureComponent) m_producingPressureComponent->setVisible(true); // 更新各组件的数据源指针并控制显示 if(option == nmDataForecast::ConstantPressureOption) { ConstantPressure& cpData = m_forecastData.getConstantPressureData(); if(m_initialPressureComponent) m_initialPressureComponent->setOriginValue(&cpData.getInitialPressure()); if(m_abandonmentRateComponent) m_abandonmentRateComponent->setOriginValue(&cpData.getAbandonmentRate()); if(m_maximumRateComponent) m_maximumRateComponent->setOriginValue(&cpData.getMaximumRate()); if(m_forecastDurationComponent) m_forecastDurationComponent->setOriginValue(&cpData.getForecastDuration()); if(m_producingPressureComponent) m_producingPressureComponent->setOriginValue(&cpData.getProducingPressure()); if (m_extStackedWidget) m_extStackedWidget->setCurrentWidget(m_extEmpty); } else if(option == nmDataForecast::DecliningPressureOption) { DecliningPressure& dpData = m_forecastData.getDecliningPressureData(); if(m_initialPressureComponent) m_initialPressureComponent->setOriginValue(&dpData.getInitialPressure()); if(m_abandonmentRateComponent) m_abandonmentRateComponent->setOriginValue(&dpData.getAbandonmentRate()); if(m_maximumRateComponent) m_maximumRateComponent->setOriginValue(&dpData.getMaximumRate()); if(m_forecastDurationComponent) m_forecastDurationComponent->setOriginValue(&dpData.getForecastDuration()); if(m_producingPressureComponent) m_producingPressureComponent->setOriginValue(&dpData.getProducingPressureStart()); if (m_extStackedWidget) m_extStackedWidget->setCurrentWidget(m_extRamp); } else if(option == nmDataForecast::MultiplePressuresOption) { MultiplePressures& mpData = m_forecastData.getMultiplePressuresData(); if(m_initialPressureComponent) m_initialPressureComponent->setOriginValue(&mpData.getInitialPressure()); if(m_abandonmentRateComponent) m_abandonmentRateComponent->setOriginValue(&mpData.getAbandonmentRate()); if(m_maximumRateComponent) m_maximumRateComponent->setOriginValue(&mpData.getMaximumRate()); if(m_abandonmentStack) m_abandonmentStack->setCurrentIndex(0); if(m_maximumStack) m_maximumStack->setCurrentIndex(0); // 隐藏"Duration"和"Pressure"输入框 if(m_forecastDurationComponent) m_forecastDurationComponent->setVisible(false); if(m_producingPressureComponent) m_producingPressureComponent->setVisible(false); if (m_extStackedWidget) m_extStackedWidget->setCurrentWidget(m_extMultiP); } else if(option == nmDataForecast::MultipleRatesOption) { MultipleRates& mrData = m_forecastData.getMultipleRatesData(); if(m_initialPressureComponent) m_initialPressureComponent->setOriginValue(&mrData.getInitialPressure()); if (m_abandonmentRateCheck) m_abandonmentRateCheck->setVisible(false); if (m_abandonmentStack) m_abandonmentStack->setCurrentIndex(1); if (m_maximumRateCheck) m_maximumRateCheck->setVisible(false); if (m_maximumStack) m_maximumStack->setCurrentIndex(1); if (m_forecastDurationComponent) m_forecastDurationComponent->setVisible(false); if (m_producingPressureComponent) m_producingPressureComponent->setVisible(false); if (m_extStackedWidget) m_extStackedWidget->setCurrentWidget(m_extMultiR); } // 更新界面显示 updateUIFromData(); this->setUpdatesEnabled(true); this->update(); } void nmWxForecast::updateUIFromData() { const nmDataForecast::ForecastOption option = m_forecastData.getForecastOption(); // 更新复选框状态 // 更新复选框状态 if(option == nmDataForecast::ConstantPressureOption) { const ConstantPressure& cpData = m_forecastData.getConstantPressureData(); bool isAbandoned = cpData.getIsAbandonmentRate(); bool isMaxRate = cpData.getIsMaximumRate(); m_abandonmentRateCheck->setChecked(isAbandoned); m_maximumRateCheck->setChecked(isMaxRate); // 手动设置初始状态 if (m_abandonmentRateComponent) m_abandonmentRateComponent->setEnabled(isAbandoned); if (m_maximumRateComponent) m_maximumRateComponent->setEnabled(isMaxRate); } else if(option == nmDataForecast::DecliningPressureOption) { const DecliningPressure& dpData = m_forecastData.getDecliningPressureData(); bool isAbandoned = dpData.getIsAbandonmentRate(); bool isMaxRate = dpData.getIsMaximumRate(); m_abandonmentRateCheck->setChecked(isAbandoned); m_maximumRateCheck->setChecked(isMaxRate); // 手动设置初始状态 if (m_abandonmentRateComponent) m_abandonmentRateComponent->setEnabled(isAbandoned); if (m_maximumRateComponent) m_maximumRateComponent->setEnabled(isMaxRate); } else if(option == nmDataForecast::MultiplePressuresOption) { const MultiplePressures& mpData = m_forecastData.getMultiplePressuresData(); bool isAbandoned = mpData.getIsAbandonmentRate(); bool isMaxRate = mpData.getIsMaximumRate(); m_abandonmentRateCheck->setChecked(isAbandoned); m_maximumRateCheck->setChecked(isMaxRate); // 手动设置初始状态 if (m_abandonmentRateComponent) m_abandonmentRateComponent->setEnabled(isAbandoned); if (m_maximumRateComponent) m_maximumRateComponent->setEnabled(isMaxRate); } // 刷新所有组件的显示值 if(m_initialPressureComponent) m_initialPressureComponent->updateValueFromOrigin(); if(m_abandonmentRateComponent) m_abandonmentRateComponent->updateValueFromOrigin(); if(m_maximumRateComponent) m_maximumRateComponent->updateValueFromOrigin(); if(m_forecastDurationComponent) m_forecastDurationComponent->updateValueFromOrigin(); if(m_producingPressureComponent) m_producingPressureComponent->updateValueFromOrigin(); if(option == nmDataForecast::DecliningPressureOption) { if(m_producingPressureEndComponent) m_producingPressureEndComponent->updateValueFromOrigin(); if(m_pressureChangePeriodComponent) m_pressureChangePeriodComponent->updateValueFromOrigin(); } else if(option == nmDataForecast::MultiplePressuresOption) { // 填充压力表格 if(m_pressureTableWidget) { m_pressureTableWidget->blockSignals(true); if(m_pDurationUnitCombo) m_pDurationUnitCombo->blockSignals(true); if(m_pPressureUnitCombo) m_pPressureUnitCombo->blockSignals(true); // 清空数据行 (保留第0行单位行) while(m_pressureTableWidget->rowCount() > 1) { m_pressureTableWidget->removeRow(1); } const QVector& tableData = m_forecastData.getMultiplePressuresData().getPressureTable(); PressureRow tempDefaults; // 用于获取单位列表和默认值 int dataSize = tableData.size(); // 如果数据为空,至少显示1行(使用默认值),否则显示所有数据 int displayRowCount = (dataSize == 0) ? 1 : dataSize; m_pressureTableWidget->setRowCount(1 + displayRowCount + 1); if(m_pDurationUnitCombo) m_pDurationUnitCombo->setCurrentIndex(m_pDurationUnitCombo->findText(m_currentDurationUnit)); if(m_pPressureUnitCombo) m_pPressureUnitCombo->setCurrentIndex(m_pPressureUnitCombo->findText(m_currentPressureUnit)); for(int i = 0; i < displayRowCount; ++i) { // 如果i < dataSize,使用加载的数据;否则 (即 dataSize==0) 使用默认值 const PressureRow& rowData = (i < dataSize) ? tableData.at(i) : tempDefaults; int tableRow = i + 1; // 转换值:从 "数据的基础单位" 转换为 "当前显示的单位" double baseDurValue = rowData.duration.getValue().toDouble(); QString baseDurUnit = rowData.duration.getUnit(); double displayDurValue = tempDefaults.duration.convertValueByUnitType(baseDurValue, baseDurUnit, m_currentDurationUnit); double basePresValue = rowData.pressure.getValue().toDouble(); QString basePresUnit = rowData.pressure.getUnit(); double displayPresValue = tempDefaults.pressure.convertValueByUnitType(basePresValue, basePresUnit, m_currentPressureUnit); QTableWidgetItem* durationItem = new QTableWidgetItem(QString::number(displayDurValue)); durationItem->setTextAlignment(Qt::AlignCenter); m_pressureTableWidget->setItem(tableRow, 0, durationItem); QTableWidgetItem* pressureItem = new QTableWidgetItem(QString::number(displayPresValue)); pressureItem->setTextAlignment(Qt::AlignCenter); m_pressureTableWidget->setItem(tableRow, 1, pressureItem); } if (m_pDurationUnitCombo) m_pDurationUnitCombo->blockSignals(false); if (m_pPressureUnitCombo) m_pPressureUnitCombo->blockSignals(false); m_pressureTableWidget->blockSignals(false); } } else if(option == nmDataForecast::MultipleRatesOption) { // 填充速率表格 if(m_rateTableWidget) { // 锁定信号 m_rateTableWidget->blockSignals(true); if(m_rDurationUnitCombo) m_rDurationUnitCombo->blockSignals(true); if(m_rRateUnitCombo) m_rRateUnitCombo->blockSignals(true); // 清空数据行 (保留第0行单位行) while(m_rateTableWidget->rowCount() > 1) { m_rateTableWidget->removeRow(1); } const QVector& tableData = m_forecastData.getMultipleRatesData().getFlowTable(); FlowRow tempDefaults; // 用于获取单位列表和默认值 int dataSize = tableData.size(); int displayRowCount = (dataSize == 0) ? 1 : dataSize; // 设置行数: 1(单位) + N(数据) + 1(空白) m_rateTableWidget->setRowCount(1 + displayRowCount + 1); // 设置下拉框为当前单位 if(m_rDurationUnitCombo) m_rDurationUnitCombo->setCurrentIndex(m_rDurationUnitCombo->findText(m_currentDurationUnit)); if(m_rRateUnitCombo) m_rRateUnitCombo->setCurrentIndex(m_rRateUnitCombo->findText(m_currentRateUnit)); for(int i = 0; i < displayRowCount; ++i) { // 如果i < dataSize,使用加载的数据;否则 (即 dataSize==0) 使用默认值 const FlowRow& rowData = (i < dataSize) ? tableData.at(i) : tempDefaults; int tableRow = i + 1; // 转换值:从 "数据的基础单位" 转换为 "当前显示的单位" double baseDurValue = rowData.duration.getValue().toDouble(); QString baseDurUnit = rowData.duration.getUnit(); double displayDurValue = tempDefaults.duration.convertValueByUnitType(baseDurValue, baseDurUnit, m_currentDurationUnit); double baseRateValue = rowData.q0.getValue().toDouble(); QString baseRateUnit = rowData.q0.getUnit(); double displayRateValue = tempDefaults.q0.convertValueByUnitType(baseRateValue, baseRateUnit, m_currentRateUnit); QTableWidgetItem* durationItem = new QTableWidgetItem(QString::number(displayDurValue)); durationItem->setTextAlignment(Qt::AlignCenter); m_rateTableWidget->setItem(tableRow, 0, durationItem); QTableWidgetItem* rateItem = new QTableWidgetItem(QString::number(displayRateValue)); rateItem->setTextAlignment(Qt::AlignCenter); m_rateTableWidget->setItem(tableRow, 1, rateItem); } // 解锁信号 if (m_rDurationUnitCombo) m_rDurationUnitCombo->blockSignals(false); if (m_rRateUnitCombo) m_rRateUnitCombo->blockSignals(false); m_rateTableWidget->blockSignals(false); } } } void nmWxForecast::updateDataFromUI() { // 将界面数据写回到数据模型 if(m_initialPressureComponent) m_initialPressureComponent->updateValueToOrigin(); if(m_abandonmentRateComponent) m_abandonmentRateComponent->updateValueToOrigin(); if(m_maximumRateComponent) m_maximumRateComponent->updateValueToOrigin(); if(m_forecastDurationComponent) m_forecastDurationComponent->updateValueToOrigin(); if(m_producingPressureComponent) m_producingPressureComponent->updateValueToOrigin(); const nmDataForecast::ForecastOption option = m_forecastData.getForecastOption(); if(option == nmDataForecast::DecliningPressureOption) { if(m_producingPressureEndComponent) m_producingPressureEndComponent->updateValueToOrigin(); if(m_pressureChangePeriodComponent) m_pressureChangePeriodComponent->updateValueToOrigin(); } else if(option == nmDataForecast::MultiplePressuresOption) { // 从表格保存数据 if(m_pressureTableWidget) { QVector& tableData = m_forecastData.getMultiplePressuresData().getPressureTable(); tableData.clear(); PressureRow tempDefaults; QString baseDurUnit = tempDefaults.duration.getUnit(); QString basePresUnit = tempDefaults.pressure.getUnit(); // 循环到 rowCount() - 1 来跳过最后那个空白行 for(int i = 1; i < m_pressureTableWidget->rowCount() - 1; ++i) { QTableWidgetItem* durationItem = m_pressureTableWidget->item(i, 0); QTableWidgetItem* pressureItem = m_pressureTableWidget->item(i, 1); bool durationEmpty = (durationItem == NULL || durationItem->text().trimmed().isEmpty()); bool pressureEmpty = (pressureItem == NULL || pressureItem->text().trimmed().isEmpty()); if(durationEmpty && pressureEmpty) { continue; } PressureRow newRow; // newRow 包含基础单位 (hr, psia) if(!durationEmpty) { double displayVal = durationItem->text().toDouble(); // 转换值:从 "当前显示的单位" 转换回 "数据的基础单位" double storageVal = tempDefaults.duration.convertValueByUnitType(displayVal, m_currentDurationUnit, baseDurUnit); newRow.duration.setValue(storageVal); } if(!pressureEmpty) { double displayVal = pressureItem->text().toDouble(); double storageVal = tempDefaults.pressure.convertValueByUnitType(displayVal, m_currentPressureUnit, basePresUnit); newRow.pressure.setValue(storageVal); } tableData.append(newRow); } } } else if (option == nmDataForecast::MultipleRatesOption) { // 从速率表格保存数据 if(m_rateTableWidget) { // 获取数据模型的引用 QVector& tableData = m_forecastData.getMultipleRatesData().getFlowTable(); tableData.clear(); FlowRow tempDefaults; // 获取数据模型的基础单位 QString baseDurUnit = tempDefaults.duration.getUnit(); QString baseRateUnit = tempDefaults.q0.getUnit(); // 循环到 rowCount() - 1 来跳过最后那个空白行 for(int i = 1; i < m_rateTableWidget->rowCount() - 1; ++i) { QTableWidgetItem* durationItem = m_rateTableWidget->item(i, 0); QTableWidgetItem* rateItem = m_rateTableWidget->item(i, 1); bool durationEmpty = (durationItem == NULL || durationItem->text().trimmed().isEmpty()); bool rateEmpty = (rateItem == NULL || rateItem->text().trimmed().isEmpty()); // 如果两个单元格都为空,则跳过该行 if(durationEmpty && rateEmpty) { continue; } FlowRow newRow; // newRow 将使用基础单位 if(!durationEmpty) { double displayVal = durationItem->text().toDouble(); // 转换值:从 "当前显示的单位" 转换回 "数据的基础单位" double storageVal = tempDefaults.duration.convertValueByUnitType(displayVal, m_currentDurationUnit, baseDurUnit); newRow.duration.setValue(storageVal); } if(!rateEmpty) { double displayVal = rateItem->text().toDouble(); double storageVal = tempDefaults.q0.convertValueByUnitType(displayVal, m_currentRateUnit, baseRateUnit); newRow.q0.setValue(storageVal); } tableData.append(newRow); } } } } void nmWxForecast::onLoadDataClicked() { // TODO: Implement load data functionality } void nmWxForecast::onGenerateClicked() { // 确保最新的界面数据写回到模型 updateDataFromUI(); // 保存数据到数据中心 saveDataToManager(); // TODO: 使用数据中心的数据进行预测计算 accept(); } void nmWxForecast::onCancelClicked() { reject(); } void nmWxForecast::onPressureCellChanged(int row, int column) { Q_UNUSED(column); if (!m_pressureTableWidget) return; if (row == 0) return; // 忽略单位行 // 让被编辑行的两列都居中 QTableWidgetItem* it0 = m_pressureTableWidget->item(row, 0); if (it0) it0->setTextAlignment(Qt::AlignCenter); QTableWidgetItem* it1 = m_pressureTableWidget->item(row, 1); if (it1) it1->setTextAlignment(Qt::AlignCenter); // 如果编辑的是最后一行,追加一个新的空白行,并预置居中 item int lastRow = m_pressureTableWidget->rowCount() - 1; if (row == lastRow) { m_pressureTableWidget->blockSignals(true); int newRow = m_pressureTableWidget->rowCount(); m_pressureTableWidget->insertRow(newRow); // 给新行两列预置空 item,这样用户一输入就居中 m_pressureTableWidget->setItem(newRow, 0, makeCenteredEmptyItem()); m_pressureTableWidget->setItem(newRow, 1, makeCenteredEmptyItem()); m_pressureTableWidget->blockSignals(false); } } void nmWxForecast::onPressureUnitChanged(const QString& unit) { if(unit == m_currentPressureUnit) return; // 单位没变 QString oldUnit = m_currentPressureUnit; m_currentPressureUnit = unit; // 锁定信号 m_pressureTableWidget->blockSignals(true); PressureRow tempDefaults; // 用于获取转换函数 // 遍历所有数据行 (从 1 到 rowCount - 2) for(int i = 1; i < m_pressureTableWidget->rowCount() - 1; ++i) { QTableWidgetItem* dataItem = m_pressureTableWidget->item(i, 1); // 第1列 if(dataItem && !dataItem->text().isEmpty()) { double oldDisplayValue = dataItem->text().toDouble(); double newDisplayValue = tempDefaults.pressure.convertValueByUnitType(oldDisplayValue, oldUnit, unit); dataItem->setText(QString::number(newDisplayValue)); } } // 解锁信号 m_pressureTableWidget->blockSignals(false); } void nmWxForecast::onRateCellChanged(int row, int column) { Q_UNUSED(column); if (!m_rateTableWidget) return; if (row == 0) return; // 忽略单位行 // 让被编辑行的两列都居中 QTableWidgetItem* it0 = m_rateTableWidget->item(row, 0); if (it0) it0->setTextAlignment(Qt::AlignCenter); QTableWidgetItem* it1 = m_rateTableWidget->item(row, 1); if (it1) it1->setTextAlignment(Qt::AlignCenter); // 若编辑的是最后一行,则在末尾追加一个新的空白行,并预置居中 item int lastRow = m_rateTableWidget->rowCount() - 1; if (row == lastRow) { m_rateTableWidget->blockSignals(true); int newRow = m_rateTableWidget->rowCount(); m_rateTableWidget->insertRow(newRow); m_rateTableWidget->setItem(newRow, 0, makeCenteredEmptyItem()); m_rateTableWidget->setItem(newRow, 1, makeCenteredEmptyItem()); m_rateTableWidget->blockSignals(false); } } void nmWxForecast::onSharedDurationUnitChanged(const QString& unit) { // 检查单位是否真的改变了 if(unit.isEmpty() || unit == m_currentDurationUnit) return; QString oldUnit = m_currentDurationUnit; m_currentDurationUnit = unit; // 更新全局的当前单位 // --- 1. 更新 Pressure 表格 --- if(m_pressureTableWidget) { // 锁定信号,防止在setText时触发 cellChanged m_pressureTableWidget->blockSignals(true); PressureRow tempDefaults; // 用于获取转换函数 // 遍历所有数据行 (从 1 到 rowCount - 2,跳过单位行和最后空白行) for(int i = 1; i < m_pressureTableWidget->rowCount() - 1; ++i) { QTableWidgetItem* dataItem = m_pressureTableWidget->item(i, 0); // 第0列 (Duration) if(dataItem && !dataItem->text().isEmpty()) { double oldDisplayValue = dataItem->text().toDouble(); // 转换值 double newDisplayValue = tempDefaults.duration.convertValueByUnitType(oldDisplayValue, oldUnit, unit); dataItem->setText(QString::number(newDisplayValue)); } } m_pressureTableWidget->blockSignals(false); } // --- 2. 更新 Rate 表格 --- if(m_rateTableWidget) { // 锁定信号 m_rateTableWidget->blockSignals(true); FlowRow tempDefaults; // 用于获取转换函数 // 遍历所有数据行 for(int i = 1; i < m_rateTableWidget->rowCount() - 1; ++i) { QTableWidgetItem* dataItem = m_rateTableWidget->item(i, 0); // 第0列 (Duration) if(dataItem && !dataItem->text().isEmpty()) { double oldDisplayValue = dataItem->text().toDouble(); // 转换值 double newDisplayValue = tempDefaults.duration.convertValueByUnitType(oldDisplayValue, oldUnit, unit); dataItem->setText(QString::number(newDisplayValue)); } } m_rateTableWidget->blockSignals(false); } if(m_pDurationUnitCombo) { m_pDurationUnitCombo->blockSignals(true); m_pDurationUnitCombo->setCurrentIndex(m_pDurationUnitCombo->findText(unit)); m_pDurationUnitCombo->blockSignals(false); } if(m_rDurationUnitCombo) { m_rDurationUnitCombo->blockSignals(true); m_rDurationUnitCombo->setCurrentIndex(m_rDurationUnitCombo->findText(unit)); m_rDurationUnitCombo->blockSignals(false); } } void nmWxForecast::onRateUnitChanged(const QString& unit) { if(unit == m_currentRateUnit) return; // 单位没变 QString oldUnit = m_currentRateUnit; m_currentRateUnit = unit; // 锁定信号 m_rateTableWidget->blockSignals(true); FlowRow tempDefaults; // 用于获取转换函数 // 遍历所有数据行 (从 1 到 rowCount - 2) for(int i = 1; i < m_rateTableWidget->rowCount() - 1; ++i) { QTableWidgetItem* dataItem = m_rateTableWidget->item(i, 1); // 第1列 if(dataItem && !dataItem->text().isEmpty()) { double oldDisplayValue = dataItem->text().toDouble(); // 使用 q0 (FlowRate) 进行单位转换 double newDisplayValue = tempDefaults.q0.convertValueByUnitType(oldDisplayValue, oldUnit, unit); dataItem->setText(QString::number(newDisplayValue)); } } // 解锁信号 m_rateTableWidget->blockSignals(false); }