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/nmWxReservoirPropertiesEdit...

505 lines
17 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 "nmWxReservoirPropertiesEditor.h"
#include "nmReservoirPropertiesDataSource.h"
#include "nmReservoirPropertiesSession.h"
#include "nmReservoirParameterPage.h"
#include "iUnitGroup.h"
#include "iUnitWxBinder.h"
#include "mModuleDefines.h"
#include "ZxBaseUtil.h"
#include <QAbstractButton>
#include <QButtonGroup>
#include <QDialogButtonBox>
#include <QFont>
#include <QFrame>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
#include <QPalette>
#include <QPushButton>
#include <QStackedWidget>
#include <QToolButton>
#include <QVBoxLayout>
nmWxReservoirPropertiesEditor::nmWxReservoirPropertiesEditor(
nmIReservoirPropertiesDataSource* pDataSource,
QWidget* pParent)
: iDlgBase(pParent),
m_pDataSource(pDataSource),
m_pSession(new nmReservoirPropertiesSession(pDataSource)),
m_pCategoryButtonGroup(nullptr),
m_pStackedWidget(nullptr),
m_pButtonBox(nullptr),
m_pFooterHelpButton(nullptr),
m_pStatusLabel(nullptr),
m_nCurrentCategory(NM_RESERVOIR_PAGE_RESERVOIR),
m_bInitialized(false)
{
for (int nIndex = 0; nIndex < NM_RESERVOIR_PAGE_COUNT; ++nIndex)
{
m_pPages[nIndex] = nullptr;
}
// 单位绑定器必须先切换到数值试井参数系列,再创建任何参数字段。
setParaSrcTag(s_Nm_Serie);
createUi();
setWindowTitle(tr("Reservoir properties settings"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
// 首次打开使用建议尺寸,后续最小宽高均由页面内容和布局自动决定。
resize(880, 480);
}
nmWxReservoirPropertiesEditor::~nmWxReservoirPropertiesEditor()
{
delete m_pSession;
m_pSession = nullptr;
m_pDataSource = nullptr;
}
void nmWxReservoirPropertiesEditor::createUi()
{
// 第一步创建原型中的顶部ribbon和无边框页面堆栈。
QVBoxLayout* pMainLayout = new QVBoxLayout(this);
pMainLayout->setContentsMargins(12, 10, 12, 0);
pMainLayout->setSpacing(0);
pMainLayout->addWidget(createCategoryBar());
m_pStackedWidget = new QStackedWidget(this);
pMainLayout->addWidget(m_pStackedWidget, 1);
// 第二步:底部左侧放帮助与状态,右侧只放确定和取消。
QWidget* pFooterWidget = new QWidget(this);
QHBoxLayout* pFooterLayout = new QHBoxLayout(pFooterWidget);
pFooterLayout->setContentsMargins(4, 5, 4, 5);
pFooterLayout->setSpacing(8);
m_pFooterHelpButton = new QToolButton(pFooterWidget);
m_pFooterHelpButton->setText(tr("Help"));
m_pFooterHelpButton->setIcon(zxLoadIcon("Help"));
m_pFooterHelpButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
m_pFooterHelpButton->setAutoRaise(true);
pFooterLayout->addWidget(m_pFooterHelpButton);
m_pStatusLabel = new QLabel(pFooterWidget);
m_pStatusLabel->setWordWrap(true);
pFooterLayout->addWidget(m_pStatusLabel, 1);
m_pButtonBox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
Qt::Horizontal,
pFooterWidget);
QPushButton* pOkButton = m_pButtonBox->button(QDialogButtonBox::Ok);
QPushButton* pCancelButton =
m_pButtonBox->button(QDialogButtonBox::Cancel);
if (pOkButton != nullptr)
{
pOkButton->setIcon(zxLoadIcon("OK"));
pOkButton->setText(tr("OK"));
}
if (pCancelButton != nullptr)
{
pCancelButton->setIcon(zxLoadIcon("Cancel"));
pCancelButton->setText(tr("Cancel"));
}
pFooterLayout->addWidget(m_pButtonBox);
pMainLayout->addWidget(pFooterWidget);
connect(m_pButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(m_pButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(m_pFooterHelpButton, SIGNAL(clicked()),
this, SLOT(slotHelp()));
}
QWidget* nmWxReservoirPropertiesEditor::createCategoryBar()
{
// 使用无标题分组框承载顶部导航,获得与框架工具栏相近的浅色外边框。
QGroupBox* pCategoryBar = new QGroupBox(this);
// 为边框和内部命令区保留少量间距,窗口拉伸时右侧空白自然扩展。
QHBoxLayout* pBarLayout = new QHBoxLayout(pCategoryBar);
pBarLayout->setContentsMargins(3, 3, 3, 3);
pBarLayout->setSpacing(0);
m_pCategoryButtonGroup = new QButtonGroup(this);
// 第一步:基础组只包含储层参数。
QHBoxLayout* pBasicLayout = nullptr;
QWidget* pBasicGroup = createRibbonGroup(
tr("Basic"), pCategoryBar, pBasicLayout);
pBasicLayout->insertWidget(0, createCategoryButton(
NM_RESERVOIR_PAGE_RESERVOIR,
tr("Reservoir parameters"),
"ResData",
pBasicGroup));
pBarLayout->addWidget(pBasicGroup);
pBarLayout->addWidget(createRibbonSeparator(pCategoryBar));
// 第二步:井筒参数独立成组,避免与储层内部区域对象混在一起。
QHBoxLayout* pWellLayout = nullptr;
QWidget* pWellGroup = createRibbonGroup(
tr("Wells"), pCategoryBar, pWellLayout);
pWellLayout->insertWidget(0, createCategoryButton(
NM_RESERVOIR_PAGE_WELL,
tr("Well parameters"),
"NmWell",
pWellGroup));
pBarLayout->addWidget(pWellGroup);
pBarLayout->addWidget(createRibbonSeparator(pCategoryBar));
// 第三步:四类储层平面对象按用户工作顺序放入同一个几何对象组。
QHBoxLayout* pGeometryLayout = nullptr;
QWidget* pGeometryGroup = createRibbonGroup(
tr("Geometry objects"),
pCategoryBar,
pGeometryLayout);
pGeometryLayout->insertWidget(0, createCategoryButton(
NM_RESERVOIR_PAGE_FAULT,
tr("Faults"),
"NmFault",
pGeometryGroup));
pGeometryLayout->insertWidget(1, createCategoryButton(
NM_RESERVOIR_PAGE_FRACTURE,
tr("Fractures"),
"NmFault",
pGeometryGroup));
pGeometryLayout->insertWidget(2, createCategoryButton(
NM_RESERVOIR_PAGE_COMPOSITE_REGION,
tr("Composite regions"),
"NmRegion",
pGeometryGroup));
pGeometryLayout->insertWidget(3, createCategoryButton(
NM_RESERVOIR_PAGE_REGION_MARK,
tr("Region marks"),
"RegionSelect",
pGeometryGroup));
pBarLayout->addWidget(pGeometryGroup);
pBarLayout->addWidget(createRibbonSeparator(pCategoryBar));
// 第四步:帮助是即时命令,不加入互斥页面按钮组。
QHBoxLayout* pHelpLayout = nullptr;
QWidget* pHelpGroup = createRibbonGroup(
tr("Help"), pCategoryBar, pHelpLayout);
pHelpLayout->insertWidget(0, createHelpButton(pHelpGroup));
pBarLayout->addWidget(pHelpGroup);
pBarLayout->addStretch(1);
connect(m_pCategoryButtonGroup, SIGNAL(buttonClicked(int)),
this, SLOT(slotCategoryButtonClicked(int)));
return pCategoryBar;
}
QWidget* nmWxReservoirPropertiesEditor::createRibbonGroup(
const QString& sCaption,
QWidget* pParent,
QHBoxLayout*& pCommandLayout)
{
QWidget* pGroupWidget = new QWidget(pParent);
// 分组左右保留稳定留白,使按钮不会紧贴相邻分组的竖向分隔线。
QVBoxLayout* pGroupLayout = new QVBoxLayout(pGroupWidget);
pGroupLayout->setContentsMargins(11, 2, 11, 1);
// 按钮区与下方分组标题之间留出间距,避免两行文字贴得过近。
pGroupLayout->setSpacing(4);
QWidget* pCommandWidget = new QWidget(pGroupWidget);
pCommandLayout = new QHBoxLayout(pCommandWidget);
pCommandLayout->setContentsMargins(0, 0, 0, 0);
pCommandLayout->setSpacing(6);
pCommandLayout->addStretch();
pGroupLayout->addWidget(pCommandWidget, 1);
// 分组标题使用主题灰色和下划线,保持框架参考工具栏的层级表现。
QLabel* pCaptionLabel = new QLabel(sCaption, pGroupWidget);
pCaptionLabel->setAlignment(Qt::AlignCenter);
pCaptionLabel->setFixedHeight(16);
QFont oCaptionFont = pCaptionLabel->font();
oCaptionFont.setUnderline(true);
pCaptionLabel->setFont(oCaptionFont);
QPalette oCaptionPalette = pCaptionLabel->palette();
oCaptionPalette.setColor(
QPalette::WindowText,
pGroupWidget->palette().color(QPalette::Mid));
pCaptionLabel->setPalette(oCaptionPalette);
pGroupLayout->addWidget(pCaptionLabel);
return pGroupWidget;
}
QFrame* nmWxReservoirPropertiesEditor::createRibbonSeparator(
QWidget* pParent)
{
// 使用单像素平直竖线避免Sunken双线在端点形成视觉折角。
QFrame* pSeparator = new QFrame(pParent);
pSeparator->setFrameShape(QFrame::VLine);
pSeparator->setFrameShadow(QFrame::Plain);
pSeparator->setLineWidth(1);
QPalette oSeparatorPalette = pSeparator->palette();
oSeparatorPalette.setColor(
QPalette::WindowText,
pParent->palette().color(QPalette::Mid));
pSeparator->setPalette(oSeparatorPalette);
pSeparator->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
return pSeparator;
}
QToolButton* nmWxReservoirPropertiesEditor::createCategoryButton(
nmReservoirPropertiesCategory eCategory,
const QString& sText,
const QString& sIconName,
QWidget* pParent)
{
QToolButton* pButton = new QToolButton(pParent);
pButton->setText(sText);
pButton->setIcon(zxLoadIcon(sIconName));
pButton->setIconSize(QSize(25, 25));
pButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
// 分类按钮仅执行页面切换命令,不保留持续选中状态。
pButton->setCheckable(false);
pButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_pCategoryButtonGroup->addButton(
pButton, static_cast<int>(eCategory));
return pButton;
}
QToolButton* nmWxReservoirPropertiesEditor::createHelpButton(
QWidget* pParent)
{
QToolButton* pButton = new QToolButton(pParent);
pButton->setText(tr("Help"));
pButton->setIcon(zxLoadIcon("Help"));
pButton->setIconSize(QSize(25, 25));
pButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
pButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
connect(pButton, SIGNAL(clicked()), this, SLOT(slotHelp()));
return pButton;
}
bool nmWxReservoirPropertiesEditor::initialize(QString& sError)
{
// 第一步:建立工作副本,失败时不创建不完整的字段控件。
if (m_pSession == nullptr || !m_pSession->initialize(sError))
{
m_bInitialized = false;
return false;
}
// 第二步创建页面并逐页验证XML参数定义和单位绑定。
createPages();
for (int nIndex = 0; nIndex < NM_RESERVOIR_PAGE_COUNT; ++nIndex)
{
if (!m_pPages[nIndex]->reloadObjects(sError))
{
m_bInitialized = false;
return false;
}
}
// 第三步:没有对应对象的分类直接禁用,避免用户进入无操作价值的空页面。
updateCategoryButtonStates();
m_pStackedWidget->setCurrentIndex(NM_RESERVOIR_PAGE_RESERVOIR);
m_bInitialized = true;
validateAllPages();
sError.clear();
return true;
}
bool nmWxReservoirPropertiesEditor::bindUnitField(
QLineEdit* pLineEdit,
QComboBox* pUnitComboBox,
const QString& sBaseParameterId)
{
// 参数XML已统一使用无歧义的mD作为渗透率基准单位所有字段直接复用框架绑定器。
return bindUnitPairWxs(pLineEdit, pUnitComboBox,
sBaseParameterId, false);
}
void nmWxReservoirPropertiesEditor::setUnitBaseValue(
QLineEdit* pLineEdit,
double dBaseValue)
{
if (m_pUnitBinder != nullptr)
{
m_pUnitBinder->setBaseValue(pLineEdit, dBaseValue);
}
}
double nmWxReservoirPropertiesEditor::unitBaseValue(
QLineEdit* pLineEdit) const
{
return m_pUnitBinder != nullptr
? m_pUnitBinder->getBaseValue(pLineEdit) : 0.0;
}
bool nmWxReservoirPropertiesEditor::isUnitInputValid(
QLineEdit* pLineEdit)
{
return m_pUnitBinder != nullptr &&
m_pUnitBinder->isInputOK(pLineEdit, true, nullptr);
}
QString nmWxReservoirPropertiesEditor::currentUnit(
QLineEdit* pLineEdit) const
{
return m_pUnitBinder != nullptr
? m_pUnitBinder->getCurrentUnit(pLineEdit) : QString();
}
iUnitGroup* nmWxReservoirPropertiesEditor::unitGroup(
QLineEdit* pLineEdit) const
{
return m_pUnitBinder != nullptr
? m_pUnitBinder->getUnitGroup(pLineEdit) : nullptr;
}
void nmWxReservoirPropertiesEditor::createPages()
{
if (m_pStackedWidget->count() > 0)
{
return;
}
for (int nIndex = 0; nIndex < NM_RESERVOIR_PAGE_COUNT; ++nIndex)
{
nmReservoirPropertiesCategory eCategory =
static_cast<nmReservoirPropertiesCategory>(nIndex);
m_pPages[nIndex] = new nmReservoirParameterPage(
eCategory,
m_pSession,
this,
m_pStackedWidget);
connect(m_pPages[nIndex], SIGNAL(sigValidityChanged(bool)),
this, SLOT(slotPageValidityChanged(bool)));
m_pStackedWidget->addWidget(m_pPages[nIndex]);
}
}
void nmWxReservoirPropertiesEditor::slotCategoryButtonClicked(
int nCategory)
{
if (!m_bInitialized || nCategory < 0 ||
nCategory >= NM_RESERVOIR_PAGE_COUNT)
{
return;
}
// 分类按钮状态由对象列表决定;此处再次防御程序主动触发的非法切换。
QAbstractButton* pCategoryButton = m_pCategoryButtonGroup != nullptr
? m_pCategoryButtonGroup->button(nCategory) : nullptr;
if (pCategoryButton == nullptr || !pCategoryButton->isEnabled())
{
return;
}
// 离开当前分类前先校验并保存,非法时保持当前页。
if (m_pPages[m_nCurrentCategory] != nullptr &&
!m_pPages[m_nCurrentCategory]->commitPendingEdits())
{
m_pStatusLabel->setText(
tr("Please correct the invalid parameter values."));
return;
}
m_nCurrentCategory = nCategory;
m_pStackedWidget->setCurrentIndex(nCategory);
m_pStatusLabel->clear();
}
void nmWxReservoirPropertiesEditor::updateCategoryButtonStates()
{
if (m_pCategoryButtonGroup == nullptr)
{
return;
}
// 逐类读取页面已经加载的对象列表,页面不存在或对象为空时禁用命令。
for (int nIndex = 0; nIndex < NM_RESERVOIR_PAGE_COUNT; ++nIndex)
{
QAbstractButton* pCategoryButton =
m_pCategoryButtonGroup->button(nIndex);
bool bEnabled = m_pPages[nIndex] != nullptr &&
m_pPages[nIndex]->hasEditableObjects();
if (pCategoryButton != nullptr)
{
pCategoryButton->setEnabled(bEnabled);
}
}
}
void nmWxReservoirPropertiesEditor::slotPageValidityChanged(bool bValid)
{
Q_UNUSED(bValid);
validateAllPages();
}
void nmWxReservoirPropertiesEditor::slotHelp()
{
// 顶部和底部帮助命令统一打开系统帮助文档。
ZxBaseUtil::startHelp();
}
bool nmWxReservoirPropertiesEditor::validateAllPages()
{
bool bAllValid = m_bInitialized;
if (m_bInitialized)
{
for (int nIndex = 0; nIndex < NM_RESERVOIR_PAGE_COUNT; ++nIndex)
{
if (m_pPages[nIndex] != nullptr &&
!m_pPages[nIndex]->isInputValid())
{
bAllValid = false;
break;
}
}
}
QPushButton* pOkButton = m_pButtonBox != nullptr
? m_pButtonBox->button(QDialogButtonBox::Ok) : nullptr;
if (pOkButton != nullptr)
{
pOkButton->setEnabled(bAllValid);
}
return bAllValid;
}
void nmWxReservoirPropertiesEditor::accept()
{
if (!m_bInitialized)
{
return;
}
// 第一步:依次提交各页面当前对象尚未离开焦点的输入。
for (int nIndex = 0; nIndex < NM_RESERVOIR_PAGE_COUNT; ++nIndex)
{
if (m_pPages[nIndex] != nullptr &&
!m_pPages[nIndex]->commitPendingEdits())
{
m_nCurrentCategory = nIndex;
m_pStackedWidget->setCurrentIndex(nIndex);
m_pStatusLabel->setText(
tr("Please correct the invalid parameter values."));
validateAllPages();
return;
}
}
// 第二步:把完整工作副本一次性交给数据源。
QString sError;
if (!m_pSession->commit(sError))
{
m_pStatusLabel->setText(sError);
QMessageBox::warning(this, tr("Reservoir properties"), sError);
return;
}
// 第三步:仅在提交成功后关闭对话框。
iDlgBase::accept();
}
void nmWxReservoirPropertiesEditor::reject()
{
// 工作副本从未提前写入真实数据,取消时直接关闭即可。
iDlgBase::reject();
}