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

545 lines
18 KiB
C++

#include "nmWxReservoirPropertiesEditor.h"
#include "nmReservoirPropertiesDataSource.h"
#include "nmReservoirPropertiesSession.h"
#include "nmReservoirParameterPage.h"
#include "iUnitGroup.h"
#include "iUnitHelper.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>
#include <QWhatsThis>
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, 520);
}
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)
{
// 第一步:仍由框架绑定器读取参数定义、范围和常规单位组。
if (!bindUnitPairWxs(pLineEdit, pUnitComboBox,
sBaseParameterId, false))
{
return false;
}
// 第二步单位帮助器对单位名执行了不区分大小写的匹配D可能先命中
// 时间组中的d。渗透率以mD这个无歧义单位定位正确单位组
// 但基准单位仍明确保持为求解器使用的D。
if (sBaseParameterId == QString::fromLatin1("K"))
{
iUnitGroup* pPermeabilityGroup =
iUnitHelper::getUnitGroupByUnit(
QString::fromLatin1("mD"));
zxBindWxPair* pBindPair = m_pUnitBinder != nullptr
? m_pUnitBinder->getWxPairOf(pLineEdit) : nullptr;
if (pPermeabilityGroup == nullptr || pBindPair == nullptr ||
pPermeabilityGroup->m_sType.compare(
QString::fromLatin1("permeability"),
Qt::CaseInsensitive) != 0 ||
pPermeabilityGroup->indexOf(
QString::fromLatin1("D")) < 0)
{
return false;
}
// 第三步:替换错误单位组并重建单位选项。后续数值设置、切换、
// 范围校验和基准值读取继续全部通过框架绑定器完成。
pBindPair->isUpdating = true;
pBindPair->pUnitGroup = pPermeabilityGroup;
pBindPair->sBaseUnit = QString::fromLatin1("D");
pUnitComboBox->clear();
pUnitComboBox->addItems(pPermeabilityGroup->getAllUnitNames());
int nBaseUnitIndex = pUnitComboBox->findText(
QString::fromLatin1("D"),
Qt::MatchExactly | Qt::MatchCaseSensitive);
pUnitComboBox->setCurrentIndex(nBaseUnitIndex);
pBindPair->isUpdating = false;
}
return true;
}
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()
{
// 顶部和底部帮助命令共用Qt上下文帮助字段说明和范围来自XML。
QWhatsThis::enterWhatsThisMode();
}
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();
}