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/nmWxWellEditor.cpp

774 lines
27 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 "nmWxWellEditor.h"
#include "iUnitWxBinder.h"
#include "mModuleDefines.h"
#include "nmDataHorizontalFracturedWell.h"
#include "nmDataPerforation.h"
#include "nmDataVerticalFracturedWell.h"
#include "nmDataWellBase.h"
#include "nmWellEditorController.h"
#include "nmWellEditorSession.h"
#include "nmWellParameterPanel.h"
#include "nmWxTimeDependentSkin.h"
#include "nmWxWellboreTrajectoryDisplay.h"
#include "ZxBaseUtil.h"
#include <QCheckBox>
#include <QComboBox>
#include <QCoreApplication>
#include <QDialogButtonBox>
#include <QFrame>
#include <QGridLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QIcon>
#include <QKeyEvent>
#include <QLabel>
#include <QLineEdit>
#include <QList>
#include <QPalette>
#include <QPushButton>
#include <QRegExp>
#include <QScrollArea>
#include <QSplitter>
#include <QToolButton>
#include <QVector>
#include <QVBoxLayout>
nmWxWellEditor::nmWxWellEditor(const nmWellEditContext& oContext,
QWidget* pParent)
: iDlgBase(pParent),
m_pSession(new nmWellEditorSession(oContext)),
m_pParameterPanel(nullptr),
m_pTrajectoryDisplay(nullptr),
m_pWellEditorController(nullptr),
m_pWellCodeLineEdit(nullptr),
m_pWellNameLineEdit(nullptr),
m_pWellTypeComboBox(nullptr),
m_pTimeDependentCheckBox(nullptr),
m_pTimeDependentButton(nullptr),
m_pFooterHelpButton(nullptr),
m_pButtonBox(nullptr),
m_pStatusLabel(nullptr),
m_pMainSplitter(nullptr),
m_sPanelValidationMessage(),
m_bInitialized(false),
m_bChangingWellType(false)
{
// 构造阶段只创建静态界面工作副本和轨迹绑定由initialize()完成。
setParaSrcTag(s_Nm_Serie);
createUi();
setWindowTitle(tr("Edit well"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setMinimumSize(940, 600);
resize(1180, 720);
}
nmWxWellEditor::~nmWxWellEditor()
{
// 界面和控制器仍引用工作副本,必须在会话释放井对象前销毁。
delete m_pWellEditorController;
m_pWellEditorController = nullptr;
delete m_pMainSplitter;
m_pMainSplitter = nullptr;
m_pParameterPanel = nullptr;
m_pTrajectoryDisplay = nullptr;
delete m_pSession;
m_pSession = nullptr;
}
bool nmWxWellEditor::initialize(QString& sError)
{
if (m_pSession == nullptr || !m_pSession->initialize(sError))
{
return false;
}
nmDataWellBase* pWell = m_pSession->currentWell();
if (pWell == nullptr)
{
sError = tr("The well working copy is unavailable.");
return false;
}
// 第一步:同步井信息和时间变表皮控件。
m_pWellCodeLineEdit->setText(pWell->getWellCode());
m_pWellNameLineEdit->setText(pWell->getWellName());
int nWellTypeIndex = m_pWellTypeComboBox->findData(
static_cast<int>(pWell->getWellType()));
m_bChangingWellType = true;
m_pWellTypeComboBox->setCurrentIndex(nWellTypeIndex);
m_bChangingWellType = false;
reloadTimeDependentSkinControls(pWell);
// 第二步:创建当前井型字段并将轨迹视图绑定到同一工作副本。
if (!m_pParameterPanel->setWellData(pWell, sError))
{
return false;
}
m_pTrajectoryDisplay->setCurrentWellboreData(pWell);
createController();
m_bInitialized = true;
updateAcceptState();
sError.clear();
return true;
}
nmDataWellBase* nmWxWellEditor::takeModifiedWell()
{
return m_pSession != nullptr
? m_pSession->takeModifiedWell() : nullptr;
}
bool nmWxWellEditor::bindUnitField(
QLineEdit* pLineEdit,
QComboBox* pUnitComboBox,
const QString& sBaseParameterId)
{
return bindUnitPairWxs(pLineEdit,
pUnitComboBox,
sBaseParameterId,
false);
}
void nmWxWellEditor::setUnitBaseValue(QLineEdit* pLineEdit,
double dBaseValue)
{
if (m_pUnitBinder != nullptr)
{
m_pUnitBinder->setBaseValue(pLineEdit, dBaseValue);
}
}
double nmWxWellEditor::unitBaseValue(QLineEdit* pLineEdit) const
{
return m_pUnitBinder != nullptr
? m_pUnitBinder->getBaseValue(pLineEdit) : 0.0;
}
bool nmWxWellEditor::isUnitInputValid(QLineEdit* pLineEdit)
{
return m_pUnitBinder != nullptr &&
m_pUnitBinder->isInputOK(pLineEdit, true, nullptr);
}
QString nmWxWellEditor::currentUnit(QLineEdit* pLineEdit) const
{
return m_pUnitBinder != nullptr
? m_pUnitBinder->getCurrentUnit(pLineEdit) : QString();
}
iUnitGroup* nmWxWellEditor::unitGroup(QLineEdit* pLineEdit) const
{
return m_pUnitBinder != nullptr
? m_pUnitBinder->getUnitGroup(pLineEdit) : nullptr;
}
void nmWxWellEditor::accept()
{
if (!m_bInitialized)
{
return;
}
// 第一步:井名和参数必须全部通过校验,原始井此时仍未修改。
QString sWellName;
QString sError;
if (!validateWellName(sWellName, sError))
{
m_pStatusLabel->setText(sError);
m_pWellNameLineEdit->setFocus();
m_pWellNameLineEdit->selectAll();
updateAcceptState();
return;
}
if (!m_pParameterPanel->commitPendingEdits(sError))
{
m_pStatusLabel->setText(sError);
m_pParameterPanel->focusFirstInvalid();
updateAcceptState();
return;
}
// 第二步只整理最终工作副本真实数据由Binder在窗口返回后替换。
m_pSession->setWellName(sWellName);
nmDataWellBase* pWell = m_pSession->currentWell();
if (nmDataVerticalFracturedWell* pVertical =
dynamic_cast<nmDataVerticalFracturedWell*>(pWell))
{
pVertical->setFracs();
}
if (nmDataHorizontalFracturedWell* pHorizontal =
dynamic_cast<nmDataHorizontalFracturedWell*>(pWell))
{
pHorizontal->setFracs();
}
iDlgBase::accept();
}
void nmWxWellEditor::reject()
{
iDlgBase::reject();
}
void nmWxWellEditor::keyPressEvent(QKeyEvent* pEvent)
{
// 参数输入框按回车只结束编辑,不能绕过完整校验直接关闭窗口。
if (pEvent != nullptr &&
(pEvent->key() == Qt::Key_Enter ||
pEvent->key() == Qt::Key_Return))
{
pEvent->ignore();
return;
}
iDlgBase::keyPressEvent(pEvent);
}
void nmWxWellEditor::slotWellTypeChanged(int nIndex)
{
if (m_bChangingWellType || !m_bInitialized || nIndex < 0)
{
return;
}
NM_WELL_MODEL ePreviousType = m_pSession->currentWellType();
NM_WELL_MODEL eNewType = static_cast<NM_WELL_MODEL>(
m_pWellTypeComboBox->itemData(nIndex).toInt());
if (eNewType == ePreviousType)
{
return;
}
// 第一步:离开当前井型前保存全部有效输入,非法时回退选择。
QString sError;
if (!m_pParameterPanel->commitPendingEdits(sError))
{
m_bChangingWellType = true;
m_pWellTypeComboBox->setCurrentIndex(
m_pWellTypeComboBox->findData(
static_cast<int>(ePreviousType)));
m_bChangingWellType = false;
m_pStatusLabel->setText(sError);
m_pParameterPanel->focusFirstInvalid();
return;
}
// 第二步:恢复目标井型缓存,首次进入时由会话保留该井型默认几何。
if (!m_pSession->switchWellType(eNewType, sError))
{
m_bChangingWellType = true;
m_pWellTypeComboBox->setCurrentIndex(
m_pWellTypeComboBox->findData(
static_cast<int>(ePreviousType)));
m_bChangingWellType = false;
m_pStatusLabel->setText(sError);
return;
}
// 第三步:同步井名,并从目标工作副本恢复井型专属状态。
m_pSession->setWellName(m_pWellNameLineEdit->text());
nmDataWellBase* pWell = m_pSession->currentWell();
reloadTimeDependentSkinControls(pWell);
if (!m_pParameterPanel->setWellData(pWell, sError))
{
m_pStatusLabel->setText(sError);
return;
}
m_pTrajectoryDisplay->setCurrentWellboreData(pWell);
createController();
updateAcceptState();
}
void nmWxWellEditor::slotWellNameEdited(const QString& sWellName)
{
if (m_pSession != nullptr)
{
m_pSession->setWellName(sWellName);
}
updateAcceptState();
}
void nmWxWellEditor::slotTimeDependentToggled(bool bChecked)
{
m_pTimeDependentButton->setEnabled(bChecked);
if (m_pSession != nullptr)
{
m_pSession->setTimeDependentSkin(bChecked);
}
}
void nmWxWellEditor::reloadTimeDependentSkinControls(
nmDataWellBase* pWell)
{
bool bEnabled = pWell != nullptr &&
pWell->isTimeDependentSkin();
// 程序回填不应再次写会话,避免覆盖目标井型缓存。
bool bSignalsWereBlocked =
m_pTimeDependentCheckBox->blockSignals(true);
m_pTimeDependentCheckBox->setChecked(bEnabled);
m_pTimeDependentCheckBox->blockSignals(bSignalsWereBlocked);
m_pTimeDependentButton->setEnabled(bEnabled);
}
void nmWxWellEditor::slotEditTimeDependentSkin()
{
if (!m_bInitialized || m_pSession->currentWell() == nullptr)
{
return;
}
// 第一步:子窗口打开前先提交主窗口输入,保证两处看到同一份数据。
QString sError;
if (!m_pParameterPanel->commitPendingEdits(sError))
{
m_pStatusLabel->setText(sError);
m_pParameterPanel->focusFirstInvalid();
return;
}
// 第二步:旧子窗口会直接修改传入井,打开前保存完整射孔快照。
nmDataWellBase* pWell = m_pSession->currentWell();
QVector<nmDataPerforation> vecOriginalPerforations;
for (int nIndex = 0; nIndex < pWell->getPerforationCount(); ++nIndex)
{
vecOriginalPerforations.append(
pWell->getPerforationCopy(nIndex));
}
nmWxTimeDependentSkin oDialog(pWell, this);
oDialog.setWindowModality(Qt::WindowModal);
if (oDialog.exec() != QDialog::Accepted)
{
// 第三步:取消时恢复数量和内容,保证会话工作副本也具备回滚语义。
while (pWell->getPerforationCount() >
vecOriginalPerforations.size())
{
pWell->removePerforation(
pWell->getPerforationCount() - 1);
}
int nCommonCount = qMin(
pWell->getPerforationCount(),
vecOriginalPerforations.size());
for (int nIndex = 0; nIndex < nCommonCount; ++nIndex)
{
pWell->updatePerforation(
nIndex, vecOriginalPerforations[nIndex]);
}
for (int nIndex = nCommonCount;
nIndex < vecOriginalPerforations.size();
++nIndex)
{
pWell->addPerforation(new nmDataPerforation(
vecOriginalPerforations[nIndex]));
}
}
m_pParameterPanel->reloadFromWell();
}
void nmWxWellEditor::slotPanelValidityChanged(bool bValid)
{
Q_UNUSED(bValid);
updateAcceptState();
}
void nmWxWellEditor::slotPanelValidationMessageChanged(
const QString& sMessage)
{
m_sPanelValidationMessage = sMessage;
updateAcceptState();
}
void nmWxWellEditor::createUi()
{
QVBoxLayout* pMainLayout = new QVBoxLayout(this);
pMainLayout->setContentsMargins(12, 10, 12, 0);
pMainLayout->setSpacing(8);
// 第一步:左侧固定井信息并滚动参数分组,右侧保持常驻轨迹视图。
m_pMainSplitter = new QSplitter(Qt::Horizontal, this);
m_pMainSplitter->setChildrenCollapsible(false);
QWidget* pParameterContainer = new QWidget(m_pMainSplitter);
QVBoxLayout* pParameterLayout = new QVBoxLayout(pParameterContainer);
pParameterLayout->setContentsMargins(0, 0, 0, 0);
pParameterLayout->setSpacing(8);
QScrollArea* pScrollArea = new QScrollArea(pParameterContainer);
pScrollArea->setWidgetResizable(true);
pScrollArea->setFrameShape(QFrame::NoFrame);
m_pParameterPanel = new nmWellParameterPanel(this, pScrollArea);
pParameterLayout->addWidget(
createWellInformationWidget(pParameterContainer));
m_pWellTypeComboBox->addItem(
tr("Vertical well"), static_cast<int>(Vertical_Well));
m_pWellTypeComboBox->addItem(
tr("Fractured vertical well"),
static_cast<int>(Vertical_Fractured_Well));
m_pWellTypeComboBox->addItem(
tr("Multistage fractured horizontal well"),
static_cast<int>(Horizontal_Fractured_Well));
pScrollArea->setWidget(m_pParameterPanel);
pParameterLayout->addWidget(pScrollArea, 1);
// 第二步:右侧轨迹视图占据剩余空间,避免参数区拉伸破坏绘图比例。
m_pTrajectoryDisplay =
new nmWxWellboreTrajectoryDisplay(m_pMainSplitter);
m_pTrajectoryDisplay->set3DViewVisible(false);
m_pMainSplitter->addWidget(pParameterContainer);
m_pMainSplitter->addWidget(m_pTrajectoryDisplay);
m_pMainSplitter->setStretchFactor(0, 0);
m_pMainSplitter->setStretchFactor(1, 1);
QList<int> listSizes;
listSizes << 410 << 730;
m_pMainSplitter->setSizes(listSizes);
// 第三步:底部命令区跨越两列,并集中连接编辑状态信号。
pMainLayout->addWidget(m_pMainSplitter, 1);
pMainLayout->addWidget(createFooter());
connect(m_pWellNameLineEdit, SIGNAL(textChanged(QString)),
this, SLOT(slotWellNameEdited(QString)));
connect(m_pWellTypeComboBox, SIGNAL(currentIndexChanged(int)),
this, SLOT(slotWellTypeChanged(int)));
connect(m_pParameterPanel, SIGNAL(sigValidityChanged(bool)),
this, SLOT(slotPanelValidityChanged(bool)));
connect(m_pParameterPanel,
SIGNAL(sigValidationMessageChanged(QString)),
this, SLOT(slotPanelValidationMessageChanged(QString)));
}
QWidget* nmWxWellEditor::createWellInformationWidget(QWidget* pParent)
{
QGroupBox* pInformationGroup = new QGroupBox(
tr("Well information"), pParent);
QGridLayout* pInformationLayout = new QGridLayout(pInformationGroup);
pInformationLayout->setContentsMargins(8, 15, 8, 10);
pInformationLayout->setHorizontalSpacing(6);
pInformationLayout->setVerticalSpacing(6);
pInformationLayout->setColumnStretch(0, 0);
pInformationLayout->setColumnStretch(1, 8);
pInformationLayout->setColumnStretch(2, 5);
pInformationLayout->setColumnMinimumWidth(
1, nmParameterField::editorMinimumWidth());
pInformationLayout->setColumnMinimumWidth(
2, nmParameterField::unitMinimumWidth());
QLabel* pCodeLabel = new QLabel(tr("Well number"), pInformationGroup);
QLabel* pNameLabel = new QLabel(tr("Well name"), pInformationGroup);
QLabel* pTypeLabel = new QLabel(tr("Well type"), pInformationGroup);
QLabel* pTimeDependentLabel = new QLabel(
tr("Time-dependent skin"), pInformationGroup);
pCodeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
pNameLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
pTypeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
pTimeDependentLabel->setAlignment(
Qt::AlignRight | Qt::AlignVCenter);
m_pWellCodeLineEdit = new QLineEdit(pInformationGroup);
m_pWellNameLineEdit = new QLineEdit(pInformationGroup);
m_pWellTypeComboBox = new QComboBox(pInformationGroup);
m_pWellCodeLineEdit->setReadOnly(true);
m_pWellCodeLineEdit->setToolTip(
tr("Project-level unique well identifier"));
QList<QWidget*> listEditors;
listEditors << m_pWellCodeLineEdit
<< m_pWellNameLineEdit
<< m_pWellTypeComboBox;
for (int nIndex = 0; nIndex < listEditors.size(); ++nIndex)
{
listEditors[nIndex]->setFixedHeight(
nmParameterField::editorHeight());
listEditors[nIndex]->setMinimumWidth(
nmParameterField::editorMinimumWidth());
listEditors[nIndex]->setSizePolicy(
QSizePolicy::Expanding, QSizePolicy::Fixed);
}
// 时间变表皮属于井级配置,开关和详情入口共同放在值列。
QWidget* pTimeDependentEditor = new QWidget(pInformationGroup);
QHBoxLayout* pTimeDependentLayout = new QHBoxLayout(
pTimeDependentEditor);
pTimeDependentLayout->setContentsMargins(0, 0, 0, 0);
pTimeDependentLayout->setSpacing(4);
m_pTimeDependentCheckBox = new QCheckBox(pTimeDependentEditor);
m_pTimeDependentButton = new QToolButton(pTimeDependentEditor);
QString sIconDir = QCoreApplication::applicationDirPath()
.section('/', 0, -2) + "/Res/Icon/";
m_pTimeDependentButton->setIcon(
QIcon(sIconDir + "NmAnalDesign3.png"));
m_pTimeDependentButton->setIconSize(QSize(20, 20));
m_pTimeDependentButton->setFixedSize(26, 26);
m_pTimeDependentButton->setAutoRaise(true);
m_pTimeDependentButton->setToolTip(
tr("Edit time-dependent skin"));
m_pTimeDependentButton->setEnabled(false);
pTimeDependentLayout->addWidget(m_pTimeDependentCheckBox);
pTimeDependentLayout->addWidget(m_pTimeDependentButton);
pTimeDependentLayout->addStretch(1);
pInformationLayout->addWidget(pCodeLabel, 0, 0);
pInformationLayout->addWidget(m_pWellCodeLineEdit, 0, 1);
pInformationLayout->addWidget(pNameLabel, 1, 0);
pInformationLayout->addWidget(m_pWellNameLineEdit, 1, 1);
pInformationLayout->addWidget(pTypeLabel, 2, 0);
pInformationLayout->addWidget(m_pWellTypeComboBox, 2, 1);
pInformationLayout->addWidget(pTimeDependentLabel, 3, 0);
pInformationLayout->addWidget(pTimeDependentEditor, 3, 1);
connect(m_pTimeDependentCheckBox, SIGNAL(toggled(bool)),
this, SLOT(slotTimeDependentToggled(bool)));
connect(m_pTimeDependentButton, SIGNAL(clicked()),
this, SLOT(slotEditTimeDependentSkin()));
return pInformationGroup;
}
QWidget* nmWxWellEditor::createFooter()
{
QWidget* pFooter = new QWidget(this);
QHBoxLayout* pFooterLayout = new QHBoxLayout(pFooter);
pFooterLayout->setContentsMargins(4, 5, 4, 5);
pFooterLayout->setSpacing(8);
// 左侧帮助命令与储层特性对话框保持相同样式和行为。
m_pFooterHelpButton = new QToolButton(pFooter);
m_pFooterHelpButton->setText(tr("Help"));
m_pFooterHelpButton->setIcon(zxLoadIcon("Help"));
m_pFooterHelpButton->setToolButtonStyle(
Qt::ToolButtonTextBesideIcon);
m_pFooterHelpButton->setAutoRaise(true);
// 中间状态区使用主题调色板,仅将错误文字标红。
m_pStatusLabel = new QLabel(pFooter);
m_pStatusLabel->setWordWrap(true);
QPalette oStatusPalette = m_pStatusLabel->palette();
oStatusPalette.setColor(QPalette::WindowText, QColor(Qt::red));
m_pStatusLabel->setPalette(oStatusPalette);
// 右侧使用标准确定和取消角色,维持平台统一交互。
m_pButtonBox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
Qt::Horizontal,
pFooter);
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_pFooterHelpButton);
pFooterLayout->addWidget(m_pStatusLabel, 1);
pFooterLayout->addWidget(m_pButtonBox);
connect(m_pFooterHelpButton, SIGNAL(clicked()),
this, SLOT(slotHelp()));
connect(m_pButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(m_pButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
return pFooter;
}
void nmWxWellEditor::slotHelp()
{
ZxBaseUtil::startHelp();
}
void nmWxWellEditor::createController()
{
// 控制器持有当前工作副本指针,井型切换后必须销毁并重新绑定。
delete m_pWellEditorController;
m_pWellEditorController = nullptr;
if (m_pSession == nullptr || m_pSession->currentWell() == nullptr ||
m_pTrajectoryDisplay == nullptr)
{
return;
}
m_pWellEditorController = new nmWellEditorController(
m_pSession->currentWell(),
m_pTrajectoryDisplay,
this);
setupControllerConnections();
}
void nmWxWellEditor::setupControllerConnections()
{
if (m_pWellEditorController == nullptr ||
m_pTrajectoryDisplay == nullptr)
{
return;
}
// 第一步:工具栏只发出模式请求,由控制器决定具体编辑行为。
connect(m_pTrajectoryDisplay, SIGNAL(requestedEditWellMode(int)),
m_pWellEditorController, SLOT(onRequestEditWellMode(int)));
connect(m_pTrajectoryDisplay,
SIGNAL(requestedEditPerforationsMode(int)),
m_pWellEditorController,
SLOT(onRequestEditPerforationsMode(int)));
connect(m_pTrajectoryDisplay,
SIGNAL(requestedNewPerforationMode(int)),
m_pWellEditorController,
SLOT(onRequestNewPerforationMode(int)));
connect(m_pTrajectoryDisplay,
SIGNAL(requestedDeletePerforationMode(int)),
m_pWellEditorController,
SLOT(onRequestDeletePerforationMode(int)));
connect(m_pTrajectoryDisplay,
SIGNAL(requestedSnapPerforationMode(int)),
m_pWellEditorController,
SLOT(onRequestSnapPerforationMode(int)));
// 第二步:视图产生的几何修改统一交给控制器写入工作副本。
connect(m_pTrajectoryDisplay, SIGNAL(sigClearViewStates()),
m_pWellEditorController, SLOT(slotClearViewStates()));
connect(m_pTrajectoryDisplay, SIGNAL(currentViewChanged(int)),
m_pWellEditorController, SLOT(onViewChanged(int)));
connect(m_pTrajectoryDisplay,
SIGNAL(sigWellboreTrajectoryPointMoved(QPointF)),
m_pWellEditorController,
SLOT(slotWellboreTrajectoryPointMoved(QPointF)));
connect(m_pTrajectoryDisplay,
SIGNAL(sigPerforationDragFinished(nmDataPerforation*,double,double)),
m_pWellEditorController,
SLOT(slotPerforationDragFinished(nmDataPerforation*,double,double)));
connect(m_pTrajectoryDisplay,
SIGNAL(sigTryAddNewPerforation(QPointF,double,double,QLineF)),
m_pWellEditorController,
SLOT(slotTryAddNewPerforation(QPointF,double,double,QLineF)));
connect(m_pTrajectoryDisplay,
SIGNAL(sigTryDeletePerforation(nmDataPerforation*)),
m_pWellEditorController,
SLOT(slotDeletePerforation(nmDataPerforation*)));
connect(m_pTrajectoryDisplay,
SIGNAL(sigFractureGeometryDragFinished(double,double)),
m_pWellEditorController,
SLOT(slotFractureGeometryChanged(double,double)));
connect(m_pTrajectoryDisplay,
SIGNAL(sigRequestFractureRectUpdate(qreal,qreal)),
m_pWellEditorController,
SLOT(slotHandleFractureRectUpdate(qreal,qreal)));
connect(m_pTrajectoryDisplay,
SIGNAL(sigFractureDragFinished(const QMap<nmDataPerforation*,QPair<double,double>>&)),
m_pWellEditorController,
SLOT(slotUpdatePerforations(const QMap<nmDataPerforation*,QPair<double,double>>&)));
connect(m_pTrajectoryDisplay,
SIGNAL(sigFractureHalfLengthChanged(double)),
m_pWellEditorController,
SLOT(slotUpdateFractureHalfLength(double)));
connect(m_pTrajectoryDisplay, SIGNAL(sigWellboreDragFinished(QPointF)),
m_pWellEditorController, SLOT(slotUpdateWellborePos(QPointF)));
connect(m_pTrajectoryDisplay,
SIGNAL(sigFractureGeometryChanged(double,double)),
m_pWellEditorController,
SLOT(slotHFWellFractureGeometryChanged(double,double)));
}
bool nmWxWellEditor::validateWellName(QString& sTrimmedName,
QString& sError) const
{
// 第一步:规范化用户输入并检查空值及文件名规则。
sTrimmedName = m_pWellNameLineEdit != nullptr
? m_pWellNameLineEdit->text().trimmed() : QString();
if (sTrimmedName.isEmpty())
{
sError = tr("Well name cannot be empty.");
return false;
}
if (!isLegalFileName(sTrimmedName))
{
sError = tr("Well name contains invalid file name characters.");
return false;
}
// 第二步:只与上下文中的其他井比较,允许当前井保持原名称。
QStringList listOtherNames = m_pSession != nullptr
? m_pSession->otherWellNames() : QStringList();
for (int nIndex = 0; nIndex < listOtherNames.size(); ++nIndex)
{
if (QString::compare(sTrimmedName,
listOtherNames[nIndex].trimmed(),
Qt::CaseInsensitive) == 0)
{
sError = tr("A well with this name already exists.");
return false;
}
}
sError.clear();
return true;
}
bool nmWxWellEditor::isLegalFileName(const QString& sWellName) const
{
// 第一步:拒绝特殊路径片段、非法字符、控制字符和结尾句点。
if (sWellName == "." || sWellName == ".." ||
sWellName.endsWith('.'))
{
return false;
}
if (sWellName.contains(QRegExp("[<>:\"/\\\\|?*]")))
{
return false;
}
for (int nIndex = 0; nIndex < sWellName.size(); ++nIndex)
{
if (sWellName[nIndex].unicode() < 32)
{
return false;
}
}
// 第二步Windows保留设备名即使带扩展名也不能作为井文件名称。
QString sStem = sWellName.section('.', 0, 0).toUpper();
QRegExp oReserved(
"^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$");
return !oReserved.exactMatch(sStem);
}
void nmWxWellEditor::updateAcceptState()
{
if (m_pButtonBox == nullptr)
{
return;
}
// 第一步:汇总井名和当前参数面板状态,统一控制确认入口。
QString sWellName;
QString sNameError;
bool bNameValid = m_pSession != nullptr &&
validateWellName(sWellName, sNameError);
bool bPanelValid = m_pParameterPanel != nullptr &&
m_pParameterPanel->isInputValid();
QPushButton* pOkButton =
m_pButtonBox->button(QDialogButtonBox::Ok);
if (pOkButton != nullptr)
{
pOkButton->setEnabled(
m_bInitialized && bNameValid && bPanelValid);
}
// 第二步:底部只显示最高优先级错误,避免多处提示互相覆盖。
if (!bNameValid)
{
m_pStatusLabel->setText(sNameError);
}
else if (!bPanelValid)
{
m_pStatusLabel->setText(m_sPanelValidationMessage.isEmpty()
? tr("Please correct the invalid parameter values.")
: m_sPanelValidationMessage);
}
else
{
m_pStatusLabel->clear();
}
}