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

784 lines
32 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 "nmWxEditWellPlot.h"
#include "nmWxNumericalDesign.h"
#include "nmWxParameterProperty.h"
// 包含所有需要的Qt布局和控件头文件
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QDebug>
#include <QSplitter>
#include <QCheckBox>
#include <QComboBox>
#include <QPushButton>
#include <QKeyEvent>
#include "iWxWellNew.h"
#include "ZxDataWell.h"
#include "zxSysUtils.h"
#include "nmDataWellBase.h"
#include "nmDataVerticalWell.h"
#include "nmDataVerticalFracturedWell.h"
#include "nmDataHorizontalFracturedWell.h"
#include "nmWxWellboreTrajectoryDisplay.h"
#include "nmWxTimeDependentSkin.h"
#include "nmGUIComponentBase.h" // 基础组件类
#include "nmGUIComponentLineEdit.h" // 用于QLineEdit类型的参数
#include "nmGUIComponentComboBox.h" // 用于QComboBox类型的参数
#include "nmWellEditorController.h"
nmWxEditWellPlot::nmWxEditWellPlot(QWidget *parent, ZxDataWell* pZxDataWell, nmDataWellBase* pNmDataWell)
: QDialog(parent)
, m_pMainSplitter(nullptr)
, m_pTopVLayout(nullptr)
, m_pLeftVLayout(nullptr)
, m_pWellNameLineEdit(nullptr)
, m_pWellTypeComboBox(nullptr)
, m_pWellNewWidget(nullptr)
, m_pBottomLeftPanel(nullptr)
, m_pRightPanel(nullptr)
, m_pOverallBottomPanel(nullptr)
, m_pZxDataWell(pZxDataWell)
, m_pNmDataWell(nullptr)
, m_pWellEditorController(nullptr)
, m_bOriginalTimeDependentSkin(false)
{
// 设置新的数据,并创建其深拷贝
if(pNmDataWell) {
// 保存原始状态
m_bOriginalTimeDependentSkin = pNmDataWell->isTimeDependentSkin();
// 返回深拷贝对象。
m_pNmDataWell = pNmDataWell->clone();
// 数据模型变化时,刷新左侧参数面板
connect(m_pNmDataWell, SIGNAL(sigParameterChanged()),
this, SLOT(onWellDataChanged()));
}
// 初始化图标路径
m_sIconDir = QCoreApplication::applicationDirPath();
m_sIconDir = m_sIconDir.section('/', 0, -2); // 获取上一级目录
m_sIconDir += "/Res/Icon/";
this->resize(1121, 633);
// 调用初始化UI的方法
this->initUI();
// 创建控制器
if(m_pNmDataWell && m_pRightPanel) {
m_pWellEditorController = new nmWellEditorController(m_pNmDataWell, m_pRightPanel, this); // 将当前对话框作为父对象
setupControllerConnections(); // 调用连接设置函数
}
}
nmWxEditWellPlot::~nmWxEditWellPlot()
{
// 在析构函数中清理所有动态创建的组件,避免内存泄漏
qDeleteAll(m_listParameterComponents);
m_listParameterComponents.clear(); // 清空列表
}
void nmWxEditWellPlot::initUI()
{
this->initMainLayout();
// 在所有UI元素创建完成后根据井类型初始化底部左侧面板
// 注意m_pBottomLeftGridLayout 必须在这里之前被 createLeftPanel() 创建
updateBottomLeftPanelUI();
}
void nmWxEditWellPlot::initMainLayout()
{
// 创建左侧和右侧面板
QWidget* pLeftContainer = createLeftPanel();
QWidget* pRightPanelWidget = createRightPanel();
// 创建主分割器并添加左右面板
m_pMainSplitter = new QSplitter(Qt::Horizontal, this);
m_pMainSplitter->addWidget(pLeftContainer);
m_pMainSplitter->addWidget(pRightPanelWidget);
// 设置分割器的初始大小比例
QList<int> sizes;
sizes << 373 << 748;
m_pMainSplitter->setSizes(sizes);
// 创建底部面板
QWidget* pBottomPanel = createBottomPanel();
// 创建顶层垂直布局并添加分割器和底部面板
m_pTopVLayout = new QVBoxLayout(this);
m_pTopVLayout->setContentsMargins(0, 0, 0, 0);
m_pTopVLayout->setSpacing(0);
m_pTopVLayout->addWidget(m_pMainSplitter, 1); // 分割器可拉伸
m_pTopVLayout->addWidget(pBottomPanel, 0); // 底部面板固定高度,不拉伸
this->setLayout(m_pTopVLayout); // 将此布局设置为对话框的主布局
}
QWidget* nmWxEditWellPlot::createLeftPanel()
{
//m_pLeftVLayout = new QVBoxLayout();
//m_pLeftVLayout->setContentsMargins(0, 0, 0, 0);
//m_pLeftVLayout->setSpacing(5);
//// 井视图部分 (iWxWellNew) 保持不变
//m_pWellNewWidget = new iWxWellNew(this);
//m_pWellNewWidget->setDataWell(m_pZxDataWell);
//m_pWellNewWidget->setActionMode(DAM_Edit);
//m_pWellNewWidget->initUI();
//m_pWellNewWidget->setWindowFlags(Qt::Widget);
//m_pLeftVLayout->addWidget(m_pWellNewWidget, 1);
//// 1. 创建 m_pBottomLeftPanel (作为 QScrollArea 的容器)
//m_pBottomLeftPanel = new QWidget(this); // 确保它有父对象
//m_pBottomLeftPanel->setStyleSheet("background-color: white;");
//// 2. 在 m_pBottomLeftPanel 内部创建一个垂直布局来容纳 QScrollArea
//QVBoxLayout* pBottomLeftPanelVLayout = new QVBoxLayout(m_pBottomLeftPanel);
//pBottomLeftPanelVLayout->setContentsMargins(0, 0, 0, 0); // 内部布局边距
//pBottomLeftPanelVLayout->setSpacing(0); // 内部布局间距
//// 3. 创建 QScrollArea 并设置其父对象为 m_pBottomLeftPanel
//QScrollArea* pScrollArea = new QScrollArea(m_pBottomLeftPanel);
//pScrollArea->setWidgetResizable(true); // 允许滚动区域调整其内部控件的大小
//// 4. 创建一个 QWidget 作为滚动区域的内容容器
//QWidget* pScrollContent = new QWidget(pScrollArea); // 父对象是 pScrollArea
//// 5. 将 m_pBottomLeftGridLayout 设置为 pScrollContent 的布局
//m_pBottomLeftGridLayout = new QGridLayout(pScrollContent);
//m_pBottomLeftGridLayout->setContentsMargins(10, 10, 10, 10); // 网格布局的边距
//m_pBottomLeftGridLayout->setSpacing(8);
//m_pBottomLeftGridLayout->setColumnStretch(0, 1); // 标签列
//m_pBottomLeftGridLayout->setColumnStretch(1, 2); // 值输入列
//// 6. 将 pScrollContent 设置为滚动区域的内容控件
//pScrollArea->setWidget(pScrollContent);
//// 7. 将滚动区域添加到 m_pBottomLeftPanel 的内部布局中
//pBottomLeftPanelVLayout->addWidget(pScrollArea);
//// 将 m_pBottomLeftPanel (现在包含了滚动区域) 添加到左侧主垂直布局
//m_pLeftVLayout->addWidget(m_pBottomLeftPanel, 1); // 占据垂直布局的1份空间
//// 创建一个 QWidget 来承载左侧的垂直布局
//QWidget* pLeftContainer = new QWidget(this);
//pLeftContainer->setLayout(m_pLeftVLayout);
//pLeftContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
//return pLeftContainer;
m_pLeftVLayout = new QVBoxLayout();
m_pLeftVLayout->setContentsMargins(0, 0, 0, 0);
m_pLeftVLayout->setSpacing(5);
// 1. 创建 m_pBottomLeftPanel (作为 QScrollArea 的容器)
m_pBottomLeftPanel = new QWidget(this); // 确保它有父对象
m_pBottomLeftPanel->setStyleSheet("background-color: white;");
// 2. 在 m_pBottomLeftPanel 内部创建一个垂直布局来容纳 QScrollArea
QVBoxLayout* pBottomLeftPanelVLayout = new QVBoxLayout(m_pBottomLeftPanel);
pBottomLeftPanelVLayout->setContentsMargins(0, 0, 0, 0); // 内部布局边距
pBottomLeftPanelVLayout->setSpacing(0); // 内部布局间距
// 3. 创建 QScrollArea 并设置其父对象为 m_pBottomLeftPanel
QScrollArea* pScrollArea = new QScrollArea(m_pBottomLeftPanel);
pScrollArea->setWidgetResizable(true); // 允许滚动区域调整其内部控件的大小
// 4. 创建一个 QWidget 作为滚动区域的内容容器
QWidget* pScrollContent = new QWidget(pScrollArea); // 父对象是 pScrollArea
// 5. 将 m_pBottomLeftGridLayout 设置为 pScrollContent 的布局
m_pBottomLeftGridLayout = new QGridLayout(pScrollContent);
m_pBottomLeftGridLayout->setContentsMargins(10, 10, 10, 10); // 网格布局的边距
m_pBottomLeftGridLayout->setSpacing(8);
m_pBottomLeftGridLayout->setColumnStretch(0, 1); // 标签列
m_pBottomLeftGridLayout->setColumnStretch(1, 2); // 值输入列
// 6. 将 pScrollContent 设置为滚动区域的内容控件
pScrollArea->setWidget(pScrollContent);
// 7. 将滚动区域添加到 m_pBottomLeftPanel 的内部布局中
pBottomLeftPanelVLayout->addWidget(pScrollArea);
// 将 m_pBottomLeftPanel (现在包含了滚动区域) 添加到左侧主垂直布局
m_pLeftVLayout->addWidget(m_pBottomLeftPanel, 1); // 占据垂直布局的1份空间
// 创建一个 QWidget 来承载左侧的垂直布局
QWidget* pLeftContainer = new QWidget(this);
pLeftContainer->setLayout(m_pLeftVLayout);
pLeftContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
return pLeftContainer;
}
QWidget* nmWxEditWellPlot::createRightPanel()
{
m_pRightPanel = new nmWxWellboreTrajectoryDisplay(this);
m_pRightPanel->setContentsMargins(0, 0, 0, 0); // 可以设置外部边距
m_pRightPanel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// 设置井数据
m_pRightPanel->setCurrentWellboreData(m_pNmDataWell);
return m_pRightPanel;
}
QWidget* nmWxEditWellPlot::createBottomPanel()
{
m_pOverallBottomPanel = new QWidget(this);
m_pOverallBottomPanel->setFixedHeight(80); // 增加高度以容纳两行内容
QVBoxLayout* mainBottomVLayout = new QVBoxLayout(m_pOverallBottomPanel);
mainBottomVLayout->setContentsMargins(5, 5, 5, 5);
mainBottomVLayout->setSpacing(5);
// 第一行:原有的控件
QWidget* hContentContainer = new QWidget(m_pOverallBottomPanel);
QHBoxLayout* bottomContentHLayout = new QHBoxLayout(hContentContainer);
bottomContentHLayout->setContentsMargins(0, 0, 0, 0);
bottomContentHLayout->setSpacing(10);
QCheckBox* myCheckBox = new QCheckBox("Use in multiple well modes", hContentContainer);
QComboBox* myComboBox = new QComboBox(hContentContainer);
myComboBox->addItem("Tested well");
myComboBox->addItem("well_1");
myComboBox->addItem("well_2");
QPushButton* pOKButton = new QPushButton("OK", hContentContainer);
QPushButton* pCancelButton = new QPushButton("Cancel", hContentContainer);
bottomContentHLayout->addWidget(myCheckBox);
bottomContentHLayout->addWidget(myComboBox);
bottomContentHLayout->addStretch();
bottomContentHLayout->addWidget(pOKButton);
bottomContentHLayout->addWidget(pCancelButton);
// 连接按钮到自定义槽函数 (Qt 4 语法)
connect(pOKButton, SIGNAL(clicked()), this, SLOT(onOkClicked()));
connect(pCancelButton, SIGNAL(clicked()), this, SLOT(onCancelClicked()));
// 第二行:时间相关复选框和关联的时钟图标按钮
QWidget* timeContainer = new QWidget(m_pOverallBottomPanel);
QHBoxLayout* pTimeDependentLayout = new QHBoxLayout(timeContainer);
pTimeDependentLayout->setContentsMargins(0, 0, 0, 0);
pTimeDependentLayout->setSpacing(5);
m_pTimeDependentCheck = new QCheckBox(tr("Time dependent"), timeContainer);
m_pTimeDependentCheck->setChecked(m_pNmDataWell->isTimeDependentSkin());
m_pTimeDependentIconButton = new QPushButton(timeContainer);
m_pTimeDependentIconButton->setIcon(QIcon(m_sIconDir + "NmAnalDesign3.png")); // 使用时钟图标
m_pTimeDependentIconButton->setIconSize(QSize(20, 20));
m_pTimeDependentIconButton->setFixedSize(22, 22);
m_pTimeDependentIconButton->setEnabled(m_pNmDataWell->isTimeDependentSkin());
pTimeDependentLayout->addWidget(m_pTimeDependentCheck);
pTimeDependentLayout->addWidget(m_pTimeDependentIconButton);
pTimeDependentLayout->addStretch(); // 添加拉伸器,将它们推到左侧
// 连接复选框状态变化信号到槽函数
connect(m_pTimeDependentCheck, SIGNAL(toggled(bool)),
m_pTimeDependentIconButton, SLOT(setEnabled(bool)));
connect(m_pTimeDependentIconButton, SIGNAL(clicked()),
this, SLOT(onTimeDependentIconClicked()));
connect(m_pTimeDependentCheck, SIGNAL(toggled(bool)),
this, SLOT(onTimeDependentCheckToggled(bool)));
// 将两行内容添加到主垂直布局
mainBottomVLayout->addWidget(hContentContainer);
mainBottomVLayout->addWidget(timeContainer);
return m_pOverallBottomPanel;
}
void nmWxEditWellPlot::setupControllerConnections()
{
if(!m_pWellEditorController || !m_pRightPanel || !m_pNmDataWell) {
return;
}
// --- 1. nmWxWellboreTrajectoryDisplay (视图) 发出的模式请求信号连接到控制器 ---
// 这些信号会在用户点击工具栏按钮时发出
connect(m_pRightPanel, SIGNAL(requestedEditWellMode(int)),
m_pWellEditorController, SLOT(onRequestEditWellMode(int)));
connect(m_pRightPanel, SIGNAL(requestedEditPerforationsMode(int)),
m_pWellEditorController, SLOT(onRequestEditPerforationsMode(int)));
connect(m_pRightPanel, SIGNAL(requestedNewPerforationMode(int)),
m_pWellEditorController, SLOT(onRequestNewPerforationMode(int)));
connect(m_pRightPanel, SIGNAL(requestedDeletePerforationMode(int)),
m_pWellEditorController, SLOT(onRequestDeletePerforationMode(int)));
connect(m_pRightPanel, SIGNAL(requestedSnapPerforationMode(int)),
m_pWellEditorController, SLOT(onRequestSnapPerforationMode(int)));
connect(m_pRightPanel, SIGNAL(sigClearViewStates()),
m_pWellEditorController, SLOT(slotClearViewStates()));
// --- 2. nmWxWellboreTrajectoryDisplay (视图) 发出的视图切换信号连接到控制器 ---
connect(m_pRightPanel, SIGNAL(currentViewChanged(int)),
m_pWellEditorController, SLOT(onViewChanged(int)));
// 井筒点移动信号到控制器 (只在拖动结束时触发)
connect(m_pRightPanel, SIGNAL(sigWellboreTrajectoryPointMoved(QPointF)),
m_pWellEditorController, SLOT(slotWellboreTrajectoryPointMoved(QPointF)));
// 射孔拖动信号到控制器 (只在拖动结束时触发)
connect(m_pRightPanel, SIGNAL(sigPerforationDragFinished(nmDataPerforation*, double, double)),
m_pWellEditorController, SLOT(slotPerforationDragFinished(nmDataPerforation*, double, double)));
// 射孔添加模式,发送给控制器
connect(m_pRightPanel, SIGNAL(sigTryAddNewPerforation(QPointF, double, double, QLineF)),
m_pWellEditorController, SLOT(slotTryAddNewPerforation(QPointF, double, double, QLineF)));
// 射孔删除模式
connect(m_pRightPanel, SIGNAL(sigTryDeletePerforation(nmDataPerforation*)),
m_pWellEditorController, SLOT(slotDeletePerforation(nmDataPerforation*)));
// 更新裂缝俯视位置
connect(m_pRightPanel, SIGNAL(sigFractureGeometryDragFinished(double, double)),
m_pWellEditorController, SLOT(slotFractureGeometryChanged(double, double)));
// 连接截面图下更新裂缝位置的信号槽
connect(m_pRightPanel, SIGNAL(sigRequestFractureRectUpdate(qreal, qreal)),
m_pWellEditorController, SLOT(slotHandleFractureRectUpdate(qreal, qreal)));
// 射孔更新
connect(m_pRightPanel, SIGNAL(sigFractureDragFinished(const QMap<nmDataPerforation*, QPair<double, double>>&)),
m_pWellEditorController, SLOT(slotUpdatePerforations(const QMap<nmDataPerforation*, QPair<double, double>>&)));
// 裂缝半长更新
connect(m_pRightPanel, SIGNAL(sigFractureHalfLengthChanged(double)),
m_pWellEditorController, SLOT(slotUpdateFractureHalfLength(double)));
// 井身位置更新
connect(m_pRightPanel, SIGNAL(sigWellboreDragFinished(QPointF)),
m_pWellEditorController, SLOT(slotUpdateWellborePos(QPointF)));
// 多段压裂水平井截面图裂缝位置更新
connect(m_pRightPanel, SIGNAL(sigFractureGeometryChanged(double, double)),
m_pWellEditorController, SLOT(slotHFWellFractureGeometryChanged(double, double)));
}
void nmWxEditWellPlot::updateBottomLeftPanelUI()
{
if(!m_pNmDataWell || !m_pBottomLeftGridLayout) {
qDebug() << "nmDataWellBase pointer or m_pBottomLeftGridLayout is null in updateBottomLeftPanelUI!";
return;
}
// 1. 清空当前网格布局中的所有控件
// 并从 m_listParameterComponents 中删除并释放所有组件
QLayoutItem *item;
while((item = m_pBottomLeftGridLayout->takeAt(0)) != nullptr) {
if(item->widget()) {
// 从 m_listParameterComponents 中移除并删除对应的组件
nmGUIComponentBase* component = qobject_cast<nmGUIComponentBase*>(item->widget());
if(component) {
m_listParameterComponents.removeOne(component); // 从列表中移除
// component->deleteLater(); // item->widget() 会处理删除
}
item->widget()->deleteLater(); // 删除Qt控件
}
delete item; // 删除布局项
}
m_listParameterComponents.clear(); // 确保列表清空
int currentRow = 0; // 用于跟踪当前添加控件的行数
// 2. 添加井名称的 QLabel 和 QLineEdit
m_pWellNameLineEdit = nullptr;
QLabel* pWellNameLabel = new QLabel(tr("Well Name:"), this);
// 使用成员变量 m_pWellNameLineEdit确保在槽函数中可以访问
m_pWellNameLineEdit = new QLineEdit(this);
m_pWellNameLineEdit->setText(m_pNmDataWell->getWellName());
// 连接编辑框的编辑完成信号到槽函数
connect(m_pWellNameLineEdit, SIGNAL(editingFinished()),
this, SLOT(onWellNameEditingFinished()));
m_pBottomLeftGridLayout->addWidget(pWellNameLabel, currentRow, 0, 1, 1);
m_pBottomLeftGridLayout->addWidget(m_pWellNameLineEdit, currentRow, 1, 1, 1);
currentRow++; // 行号递增
// 3. 创建并添加 QLabel 和 QComboBox
m_pWellTypeComboBox = nullptr;
QLabel* pWellTypeLabel = new QLabel(tr("Well Type:"), this);
m_pWellTypeComboBox = new QComboBox(this);
m_pWellTypeComboBox->addItem(tr("Vertical Well"), NM_WELL_MODEL::Vertical_Well);
m_pWellTypeComboBox->addItem(tr("Vertical Fractured Well"), NM_WELL_MODEL::Vertical_Fractured_Well);
m_pWellTypeComboBox->addItem(tr("Horizontal Fractured Well"), NM_WELL_MODEL::Horizontal_Fractured_Well);
// 重新连接信号槽
connect(m_pWellTypeComboBox, SIGNAL(currentIndexChanged(int)),
this, SLOT(onWellTypeChanged(int)));
// 4. 根据当前 m_pNmDataWell 的类型设置选中项
NM_WELL_MODEL currentType = m_pNmDataWell->getWellType();
int index = m_pWellTypeComboBox->findData(currentType);
if(index != -1) {
m_pWellTypeComboBox->setCurrentIndex(index);
}
// 5. 将 QLabel 和 QComboBox 添加到布局
m_pBottomLeftGridLayout->addWidget(pWellTypeLabel, currentRow, 0, 1, 1);
m_pBottomLeftGridLayout->addWidget(m_pWellTypeComboBox, currentRow, 1, 1, 1);
currentRow++;
// 6. 根据井类型动态添加参数
// 这里使用 dynamic_cast 来判断具体的井类型并访问其特有属性
// 井的通用参数(所有井类型都可能有的)
// Location---位置
// addLineEditComponentToGrid(m_pNmDataWell->getInputWellHead(), currentRow);
// addLineEditComponentToGrid(m_pNmDataWell->getWellHeadX(), currentRow);
// addLineEditComponentToGrid(m_pNmDataWell->getWellHeadY(), currentRow);
addLineEditComponentToGrid(m_pNmDataWell->getX(), currentRow);
addLineEditComponentToGrid(m_pNmDataWell->getY(), currentRow);
// Geometry and properties---几何与属性
addLineEditComponentToGrid(m_pNmDataWell->getDrillFloorElevation(), currentRow);
addLineEditComponentToGrid(m_pNmDataWell->getRadius(), currentRow);
addLineEditComponentToGrid(m_pNmDataWell->getZw(), currentRow);
addLineEditComponentToGrid(m_pNmDataWell->getWellLength(), currentRow);
// addLineEditComponentToGrid(m_pNmDataWell->getRateDependentSkin(), currentRow);
addLineEditComponentToGrid(m_pNmDataWell->getdSdQ(), currentRow);
// Wellbore---井储
addComboBoxComponentToGrid(m_pNmDataWell->getWellboreModel(), currentRow);
addLineEditComponentToGrid(m_pNmDataWell->getWellboreStorage(), currentRow);
addLineEditComponentToGrid(m_pNmDataWell->getFinalWellboreStorage(), currentRow);
addLineEditComponentToGrid(m_pNmDataWell->getCInitialCFinal(), currentRow);
addLineEditComponentToGrid(m_pNmDataWell->getDtChangingStorage(), currentRow);
addLineEditComponentToGrid(m_pNmDataWell->getLeakSkin(), currentRow);
// Bottomhole conditions---井底条件
addLineEditComponentToGrid(m_pNmDataWell->getBottomholeMD(), currentRow);
// 射孔段的表皮系数(如果井有射孔段)
if(m_pNmDataWell->getPerforationCount() > 0) {
for(int i = 0; i < m_pNmDataWell->getPerforationCount(); ++i) {
nmDataPerforation* perforation = m_pNmDataWell->getPerforation(i);
if(perforation) {
addLineEditComponentToGrid(perforation->getName(), currentRow);
addLineEditComponentToGrid(perforation->getMdStart(), currentRow);
addLineEditComponentToGrid(perforation->getMdEnd(), currentRow);
addLineEditComponentToGrid(perforation->getSkin(), currentRow);
}
}
}
// 根据具体的井模型添加特有参数
if(nmDataVerticalFracturedWell * pVFWell = dynamic_cast<nmDataVerticalFracturedWell * >(m_pNmDataWell)) {
// 垂直裂缝井特有参数
addVerticalFracturedWellParameters(pVFWell, currentRow); // 委托给私有辅助函数
} else if(nmDataHorizontalFracturedWell * pHFWell = dynamic_cast<nmDataHorizontalFracturedWell * >(m_pNmDataWell)) {
// 多段压裂水平井特有参数
addHorizontalFracturedWellParameters(pHFWell, currentRow); // 委托给私有辅助函数
} else if(nmDataVerticalWell * pVWell = dynamic_cast<nmDataVerticalWell * >(m_pNmDataWell)) {
// 直井特有参数
addVerticalWellParameters(pVWell, currentRow); // 委托给私有辅助函数
}
// 强制刷新布局和调整面板大小
// m_pBottomLeftGridLayout->update();
// m_pBottomLeftPanel->adjustSize(); // 调整面板大小以适应新内容
}
// 辅助函数:向网格布局中添加 nmGUIComponentLineEdit
void nmWxEditWellPlot::addLineEditComponentToGrid(nmDataAttribute& attribute, int& row)
{
// const_cast 是安全的,因为 nmGUIComponentLineEdit 内部会操作 nmDataAttribute 的非 const 引用,
// 而这个引用在 nmDataWell 对象内部是可修改的。
nmGUIComponentLineEdit* component = new nmGUIComponentLineEdit(const_cast<nmDataAttribute*>(&attribute), true, m_pBottomLeftPanel);
// 设置实时更新数据
component->setIsRealTimeUpdateValue(true);
m_listParameterComponents.append(component); // 添加到管理列表中
m_pBottomLeftGridLayout->addWidget(component, row, 0, 1, 2); // 跨两列
row++; // 行号递增
}
// 辅助函数:向网格布局中添加 nmGUIComponentComboBox
void nmWxEditWellPlot::addComboBoxComponentToGrid(nmDataAttribute& attribute, int& row)
{
nmGUIComponentComboBox* component = new nmGUIComponentComboBox(const_cast<nmDataAttribute*>(&attribute), true, m_pBottomLeftPanel);
m_listParameterComponents.append(component); // 添加到管理列表中
m_pBottomLeftGridLayout->addWidget(component, row, 0, 1, 2); // 跨两列
row++; // 行号递增
}
// 私有辅助函数:添加垂直井特有参数
void nmWxEditWellPlot::addVerticalWellParameters(nmDataVerticalWell* well, int& row)
{
if(!well) return;
// 添加垂直井特有的参数
addLineEditComponentToGrid(well->getPerforationLength(), row);
}
// 私有辅助函数:添加垂直压裂井特有参数
void nmWxEditWellPlot::addVerticalFracturedWellParameters(nmDataVerticalFracturedWell* well, int& row)
{
if(!well) return;
// 添加垂直压裂井特有的参数
addComboBoxComponentToGrid(well->getFractureModel(), row);
addLineEditComponentToGrid(well->getFractureHalfLength(), row);
addLineEditComponentToGrid(well->getFractureHeight(), row);
addLineEditComponentToGrid(well->getFractureMidPointHeight(), row);
addLineEditComponentToGrid(well->getWidth(), row);
addLineEditComponentToGrid(well->getFractureAngle(), row);
addLineEditComponentToGrid(well->getDfc(), row);
}
// 私有辅助函数:添加水平压裂井特有参数
void nmWxEditWellPlot::addHorizontalFracturedWellParameters(nmDataHorizontalFracturedWell* well, int& row)
{
if(!well) return;
// 添加水平压裂井特有的参数
addLineEditComponentToGrid(well->getDrainAngle(), row);
addComboBoxComponentToGrid(well->getModelingType(), row);
addComboBoxComponentToGrid(well->getFractureModel(), row);
addLineEditComponentToGrid(well->getNumberOfFractures(), row);
addLineEditComponentToGrid(well->getFractureHalfLength(), row);
addLineEditComponentToGrid(well->getFractureHeight(), row);
addLineEditComponentToGrid(well->getFractureMidPointHeight(), row);
addLineEditComponentToGrid(well->getWidth(), row);
addLineEditComponentToGrid(well->getFractureAngle(), row);
addLineEditComponentToGrid(well->getStimulatedZonesAroundFracture(), row);
addLineEditComponentToGrid(well->getStimulationRadius(), row);
addLineEditComponentToGrid(well->getPermeabilityMultiplier(), row);
addLineEditComponentToGrid(well->getPorosityMultiplier(), row);
addLineEditComponentToGrid(well->getDfc(), row);
}
nmDataWellBase* nmWxEditWellPlot::getModifiedDataWell()
{
return m_pNmDataWell;
}
void nmWxEditWellPlot::onOkClicked()
{
if(!m_pNmDataWell) {
qDebug() << "nmDataWellBase pointer is null in onOkClicked!";
QDialog::reject();
return;
}
// 应用时间变表皮状态到真实数据
nmDataWellBase* pRealWell = nmDataAnalyzeManager::getCurrentInstance()->getCurWellData();
if(pRealWell && pRealWell->getWellName() == m_pNmDataWell->getWellName()) {
pRealWell->setTimeDependentSkin(m_pNmDataWell->isTimeDependentSkin());
}
nmDataAnalyzeManager* manager = nmDataAnalyzeManager::getCurrentInstance();
NM_WELL_MODEL currentWellType = m_pNmDataWell->getWellType();
// 根据井类型调用对应的update方法
if(currentWellType == NM_WELL_MODEL::Vertical_Well) {
// 转换为垂直井类型
if(auto * verticalWell = dynamic_cast<nmDataVerticalWell * >(m_pNmDataWell)) {
QVector<nmDataVerticalWell> wells;
wells.append(*verticalWell); // 添加井的副本到QVector中
manager->updateVerticalWells(wells);
}
} else if(currentWellType == NM_WELL_MODEL::Vertical_Fractured_Well) {
// 转换为垂直压裂井类型
if(auto * verticalFracturedWell = dynamic_cast<nmDataVerticalFracturedWell * >(m_pNmDataWell)) {
QVector<nmDataVerticalFracturedWell> wells;
wells.append(*verticalFracturedWell); // 添加井的副本到QVector中
manager->updateVerticalFracturedWells(wells);
}
} else if(currentWellType == NM_WELL_MODEL::Horizontal_Fractured_Well) {
// 转换为水平压裂井类型
if(auto * horizontalFracturedWell = dynamic_cast<nmDataHorizontalFracturedWell * >(m_pNmDataWell)) {
QVector<nmDataHorizontalFracturedWell> wells;
wells.append(*horizontalFracturedWell); // 添加井的副本到QVector中
manager->updateHorizontalFracturedWells(wells);
}
} else {
QDialog::reject();
return;
}
// 更新完成后,通知参数界面刷新
nmWxParameterProperty::notifyUpdateTable();
accept();
}
void nmWxEditWellPlot::onCancelClicked()
{
if(m_pNmDataWell) {
nmWxNumericalDesign::notifyTimeDependentSkinChanged(m_pNmDataWell->getWellName(), m_bOriginalTimeDependentSkin);
delete m_pNmDataWell;
m_pNmDataWell = nullptr;
}
reject();
}
void nmWxEditWellPlot::onWellDataChanged()
{
// 重新加载所有参数将模型中的新值显示在UI上
updateBottomLeftPanelUI();
}
void nmWxEditWellPlot::onWellTypeChanged(int index)
{
// 1. 获取选择的井类型枚举值
// 使用 itemData() 方法获取在 addItem() 时关联的枚举值
QVariant selectedData = m_pWellTypeComboBox->itemData(index);
if(!selectedData.isValid()) {
return;
}
NM_WELL_MODEL newWellType = static_cast<NM_WELL_MODEL>(selectedData.toInt());
// 2. 获取当前井的类型
NM_WELL_MODEL currentWellType = m_pNmDataWell->getWellType();
// 3. 如果新旧类型相同,则无需操作
if(newWellType == currentWellType) {
return;
}
// 4. 实现核心逻辑:创建新对象、复制数据、销毁旧对象
// 保存旧数据模型的指针
nmDataWellBase* pOldWellData = m_pNmDataWell;
// 创建一个新的数据模型对象
nmDataWellBase* pNewWellData = nullptr;
if(newWellType == NM_WELL_MODEL::Vertical_Well) {
pNewWellData = new nmDataVerticalWell();
pNewWellData->setWellType(Vertical_Well);
} else if(newWellType == NM_WELL_MODEL::Vertical_Fractured_Well) {
pNewWellData = new nmDataVerticalFracturedWell();
pNewWellData->setWellType(Vertical_Fractured_Well);
} else if(newWellType == NM_WELL_MODEL::Horizontal_Fractured_Well) {
pNewWellData = new nmDataHorizontalFracturedWell();
pNewWellData->setWellType(Horizontal_Fractured_Well);
} else {
return;
}
// 将旧数据中的通用属性复制到新数据中
if(pNewWellData && pOldWellData) {
// 通用几何位置
pNewWellData->setWellName(pOldWellData->getWellName());
pNewWellData->setX(pOldWellData->getX());
pNewWellData->setY(pOldWellData->getY());
pNewWellData->setRadius(pOldWellData->getRadius());
// 历史数据
pNewWellData->setPressurePoints(pOldWellData->getPressurePoints());
pNewWellData->setFlowPoints(pOldWellData->getFlowPoints());
pNewWellData->setIndexF(pOldWellData->getIndexF());
// 结果数据
pNewWellData->setResultPressure(pOldWellData->getResultPressure());
pNewWellData->setResultLogLog(pOldWellData->getResultLogLog());
pNewWellData->setResultSemiLog(pOldWellData->getResultSemiLog());
}
// 对于裂缝类型的井,需要更新裂缝位置
if(newWellType == NM_WELL_MODEL::Vertical_Fractured_Well) {
if(nmDataVerticalFracturedWell * pVFWell = dynamic_cast<nmDataVerticalFracturedWell * >(pNewWellData)) {
pVFWell->setFracs();
}
} else if(newWellType == NM_WELL_MODEL::Horizontal_Fractured_Well) {
if(nmDataHorizontalFracturedWell * pHFWell = dynamic_cast<nmDataHorizontalFracturedWell * >(pNewWellData)) {
pHFWell->setFracs();
}
}
// 5. 更新 m_pNmDataWell 指针
m_pNmDataWell = pNewWellData;
// 6. 重新连接信号:先断开旧连接,再连接新对象
if(pOldWellData) {
disconnect(pOldWellData, SIGNAL(sigParameterChanged()), this, SLOT(onWellDataChanged()));
}
if(m_pNmDataWell) {
connect(m_pNmDataWell, SIGNAL(sigParameterChanged()), this, SLOT(onWellDataChanged()));
}
// 7. 销毁旧控制器并创建新控制器
// 由于控制器是 QObject 的子对象它会被父对象this自动管理内存。
// 但是为了保证逻辑的清晰和安全,我们手动处理。
if(m_pWellEditorController) {
delete m_pWellEditorController;
m_pWellEditorController = nullptr;
}
if(m_pNmDataWell && m_pRightPanel) {
m_pWellEditorController = new nmWellEditorController(m_pNmDataWell, m_pRightPanel, this);
// 重建控制器后,必须重新连接所有信号
setupControllerConnections();
}
// 8. 销毁旧数据对象
if(pOldWellData) {
delete pOldWellData;
}
// 9. 刷新 UI
updateBottomLeftPanelUI();
// 10. 通知 nmWxWellboreTrajectoryDisplay 视图刷新
if(m_pRightPanel) {
m_pRightPanel->setCurrentWellboreData(m_pNmDataWell); // 重新设置数据
}
}
void nmWxEditWellPlot::onWellNameEditingFinished()
{
if(m_pNmDataWell && m_pWellNameLineEdit) {
// 获取编辑框中的文本并更新数据模型
m_pNmDataWell->setWellName(m_pWellNameLineEdit->text());
}
}
void nmWxEditWellPlot::keyPressEvent(QKeyEvent *event)
{
// 如果按下的键是回车键,则忽略它,不调用父类的 keyPressEvent
if(event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
// 事件已被处理,阻止它继续传播
event->ignore();
return;
}
// 对于所有其他按键,调用基类的处理
QDialog::keyPressEvent(event);
}
void nmWxEditWellPlot::onTimeDependentIconClicked()
{
if(!m_pNmDataWell) {
return;
}
nmWxTimeDependentSkin::showForCurrentWell(m_pNmDataWell, this); // 传具体井数据
}
void nmWxEditWellPlot::onTimeDependentCheckToggled(bool checked)
{
if(!m_pNmDataWell) {
return;
}
// 更新数据模型
m_pNmDataWell->setTimeDependentSkin(checked);
nmWxNumericalDesign::notifyTimeDependentSkinChanged(m_pNmDataWell->getWellName(), checked);
}