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/nmSubWnd/nmSubWndUtils.cpp

1520 lines
53 KiB
C++

#include "zxLogInstance.h"
#include "iRibbonXmlCmd.h"
#include <QEvent>
#include <QDir>
#include <QFileInfo>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <QTimer>
#include "zxSysUtils.h"
#include "ZxUiBase.h"
#include "ZxMainWindow.h"
#include "ZxMdiSubWindow.h"
#include "ZxTabWidget.h"
#include "TreeWxMain.h"
#include "iDockBaseWx.h"
#include "iModuleHelper.h"
#include "ZxDataWell.h"
#include "ZxDataProject.h"
#include "nmFrameworkTest.h"
#include "iSubWndFitting.h"
#ifdef MY_OWN_GRID
#include "iSubWndGrid.h"
#else
#include "nmSubWndDemo.h"
#endif
#include "nmSubWndUtils.h"
#include "nmSubWndMain.h"
#include "nmSubWndGrid.h"
#include "nmSubWndPostprocessing.h"
#include "nmWxGridDlg.h"
#include "nmWxPostprocessingAnimationWidget.h"
#include "nmGuiPlot.h"
#include "ZxObjBase.h"
#include "nmDataAnalyzeManager.h"
#include "nmDataWellBase.h"
#include "nmDataAnalyzeContext.h"
#include "nmDataAnalyzeContextProvider.h"
#include "nmNumericalSaveSession.h"
#include "nmAttrRegistry.h"
#include "iRibbonXmlTab.h"
#include "nmWxParameterProperty.h"
#include "nmWxParaPropertyPebi.h"
#include "nmWxNumericalDesign.h"
#include "nmDataOutline.h"
#include "iSysParaHelper.h"
#include "mModuleDefines.h"
#include "ZxRstWnd.h"
#include "nmWxChangeAnal.h"
#include "nmDataAxis.h"
namespace {
// 判断指定相态下是否至少有一个PVT曲线结果
bool hasPvtCurve(iSubWndFitting* pSubWndF, PvtFluidType eType, const QStringList& listParas)
{
if(pSubWndF == nullptr) {
return false;
}
for(int i = 0; i < listParas.size(); ++i) {
VecDouble vecX;
QVector<double> vecY;
pSubWndF->getPvtRstOf(eType, listParas[i], vecX, vecY);
if(!vecX.isEmpty()) {
return true;
}
}
return false;
}
bool showNmDockInitializationError(const QString& detail)
{
QMessageBox::warning(nullptr,
QObject::tr("Numerical Panel Initialization Failed"),
QObject::tr("The numerical panels could not be opened.\n\nReason: %1")
.arg(detail));
return false;
}
}
nmSubWndUtils* nmSubWndUtils::getInstance()
{
static nmSubWndUtils s_instance; // 在第一次调用时创建唯一实例
return &s_instance;
}
nmSubWndUtils::nmSubWndUtils()
{
m_pMainWnd = nullptr;
m_pTabWx = nullptr;
m_pNumericalSaveSession = new nmNumericalSaveSession;
// 加载NM模块专用参数定义XMLBin/XmlFiles/ModelParaDefinesNM_cn.xml
{
QString sXmlDir = ZxBaseUtil::getDirOf(s_Dir_Config, zxAppID);
QString sFileNm = QString("%1ModelParaDefinesNM_cn.xml").arg(sXmlDir);
if (!_paraHelper->loadParaDefines(sFileNm, s_Nm_Serie))
{
zxLogInstance::getInstance()->writeLogF("Failed to load NM para defines: " + sFileNm);
}
}
}
nmSubWndUtils::~nmSubWndUtils()
{
delete m_pNumericalSaveSession;
m_pNumericalSaveSession = nullptr;
}
iSubWnd* nmSubWndUtils::createSubWnd(const iRibbonXmlCmd* pCmdInfo, \
const ZxMainWindow* pMainWnd, \
bool& bCreatedNewOne)
{
if (nullptr == pCmdInfo)
{
return nullptr;
}
// TODO 此处是自行控制是否单一模式
bool bOnlyOneForCurTabWx = true;
return makesureSubWnd(pCmdInfo->m_sID, pCmdInfo->m_sExtInfo, \
pMainWnd, bCreatedNewOne, \
bOnlyOneForCurTabWx);
}
iSubWnd* nmSubWndUtils::makesureSubWnd(QString sID, QString sExt, \
const ZxMainWindow* pMainWnd, \
bool& bCreatedNewOne, \
bool bOnlyOneForCurTabWx /*= true*/)
{
Q_ASSERT (nullptr != pMainWnd);
//单一模式,则先查找,如果已经存在,则直接返回
if (bOnlyOneForCurTabWx)
{
ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(pMainWnd);
ZxTabWidget* pTabWx = pMainWnd1->getCurTabWx(); //仅仅在当前TabWx下查找此处可以根据需要调整
if (nullptr != pTabWx)
{
for (int i = 0; i < pTabWx->count(); i++)
{
iSubWnd* pSubWnd = dynamic_cast<iSubWnd*>(pTabWx->widget(i));
if (nullptr != pSubWnd)
{
if (_isSame(sID, pSubWnd->getWndID()))
{
bCreatedNewOne = false;
// TODO 根据需要,是否需要激活当前
if (i != pTabWx->currentIndex())
{
pTabWx->setCurrentIndex(i);
}
return pSubWnd;
}
}
}
}
}
// 创建新的
bCreatedNewOne = true;
iSubWnd* pSubWnd = nullptr;
// Here is your own codes
int nID = sID.toInt();
if(nID == 5118) {
// TODO提示应该先打开一个分析
//QMessageBox::information(pSubWnd, tr("error"), tr("please open an analyze!"));
// 判断油藏参数是否已经初始化
//if(nmDataAnalyzeManager::getCurrentInstance()->getReservoirData() == nullptr) {
// // 初始化油藏参数
// nmDataAnalyzeManager::getCurrentInstance()->createReservoir();
// // 初始化PVT
// nmDataAnalyzeManager::getCurrentInstance()->initPvtParaFromSubFit();
//}
// 1、获取当前窗体下的所有流动段分析
// 选择一个流动段分析里的数据作为当前地图要展示的数据
// TODO2、没有流动段分析
if(nmDataAnalyzeManager::getCurrentFitting() != nullptr) {
nmSubWndMain* pSubWndMain = new nmSubWndMain(NULL, sExt);
pSubWnd = pSubWndMain;
} else {
ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(pMainWnd);
QMessageBox::information(pMainWnd1, tr("No Data"), tr("please open an Fitting and click Numerical button"));
}
} else if(nID == 5114) {
// PEBI GridControl控制参数对应HX_NWTM_GRID_INPUT::GridControl
double dPebiGridControl = 150.0;
bool bGeneratePebiGrid = true;
// 判断是否是加载进来的数据,如果不是加载进来的,则需要添加设置网格信息的对话框
if(!nmDataAnalyzeManager::getCurrentInstance()->m_bIsLoadData) {
// 当前只保留PEBI网格弹窗只需要设置GridControl
nmWxGridDlg dlg(&dPebiGridControl);
if(dlg.exec() == QDialog::Rejected) {
return NULL;
}
} else {
// 更新标志
nmDataAnalyzeManager::getCurrentInstance()->m_bIsLoadData = false;
bGeneratePebiGrid = false;
}
// 创建PEBI网格窗口新建数据时生成网格加载数据时直接展示已加载结果。
nmDataAnalyzeManager* pDataManager =
nmDataAnalyzeManager::getCurrentInstance();
if(pDataManager == nullptr) {
return NULL;
}
if(!bGeneratePebiGrid) {
dPebiGridControl = pDataManager->getPebiGridControl();
}
nmSubWndGrid* pSubWndGrid = new nmSubWndGrid(
NULL, sExt, dPebiGridControl, bGeneratePebiGrid, pDataManager);
pSubWnd = pSubWndGrid;
} else if(nID == 5116) {
// 后处理
nmSubWndPostprocessing* pSubWndPostProcessing = new nmSubWndPostprocessing(NULL, sExt);
pSubWnd = pSubWndPostProcessing;
}
if(NULL != pSubWnd) {
// TODO 此处根据需要设置 改变 标识
pSubWnd->setModified(true);
}
return pSubWnd;
}
bool nmSubWndUtils::runCmdBySpecial(const iRibbonXmlCmd* pCmdInfo, \
const ZxMainWindow* pMainWnd)
{
//nmSubWndUtils* pUtilsInstance = nmSubWndUtils::getInstance();
// 第一次创建TODO或者打开另一个窗体时连接槽函数避免多次调用
//if (pUtilsInstance->m_pMainWnd == nullptr || pUtilsInstance->m_pMainWnd != pMainWnd)
//{
// // 第一次操作时连接tab的槽函数
// ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(pMainWnd);
// if (nullptr != pMainWnd1)
// {
// pUtilsInstance->m_pMainWnd = pMainWnd1;
// ZxTabWidget* pTabWx = pMainWnd1->getCurTabWx();
// if (nullptr != pTabWx){
// disconnect(pTabWx, SIGNAL(currentChanged(int)), pUtilsInstance, SLOT(slotHandleTabChange(int)));
// connect(pTabWx, SIGNAL(currentChanged(int)), pUtilsInstance, SLOT(slotHandleTabChange(int)));
// }
// }
//}
ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(pMainWnd);
nmSubWndUtils* pUtilsInstance = nmSubWndUtils::getInstance();
if(nullptr != pMainWnd1) {
ZxTabWidget* pTabWx = pMainWnd1->getCurTabWx();
if(nullptr != pTabWx) {
if(pUtilsInstance->m_pTabWx == nullptr || pUtilsInstance->m_pTabWx != pTabWx) {
pUtilsInstance->m_pTabWx = pTabWx;
disconnect(pUtilsInstance->m_pTabWx, SIGNAL(currentChanged(int)), pUtilsInstance, SLOT(slotHandleTabChange(int)));
connect(pUtilsInstance->m_pTabWx, SIGNAL(currentChanged(int)), pUtilsInstance, SLOT(slotHandleTabChange(int)));
}
}
}
if(nullptr == pCmdInfo) {
return false;
}
// 20250310 测试Release崩溃问题暂时放开
//#ifdef QT_DEBUG
// 专门提供给数值模块,进行一些框架代码的调试
if (_isSame(pCmdInfo->m_sID, "5999"))
{
nmFrameworkTest::testFrmCodes(pCmdInfo, pMainWnd);
return true;
}
//#endif
return runCmdBySpecial(pCmdInfo->m_sID, pCmdInfo->m_sExtInfo, pMainWnd);
}
bool nmSubWndUtils::runCmdBySpecial(QString sID, QString sExt, \
const ZxMainWindow* pMainWnd)
{
// Here is your own codes
if(_isSame(sID, "12345")) {
QMessageBox::information(nullptr, zxAppDescEN, QObject::tr("I am abc"));
return true;
} if(_isSame(sID, "5130")) {
ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(pMainWnd);
// 创建并显示对话框
nmWxChangeAnal analChangeDlg(nullptr, pMainWnd1);
// 执行对话框并判断返回值
if(analChangeDlg.exec() == QDialog::Accepted) {
}
return true;
} else {
return false;
}
}
bool nmSubWndUtils::isEnableOfID_Common(QString sID, QString sName, \
const bool bLicensed, \
const ZxMainWindow* pMainWnd)
{
// 20250310 测试Release崩溃问题暂时放开
//#ifdef QT_DEBUG
// 专门提供给数值模块,进行一些框架代码的调试
if (_isSame(sID, "5999"))
{
return (true);
}
//#endif
bool b1 = (nullptr != zxCurProject);
// Here is your own codes
if (_isSame(sID, "5101"))
{
return (true);
}
else if (_isSame(sID, "5002")) //或者 _isSame(sName, "PropertyLoad")
{
// <Cmd ID="5002" Bounds="0,1,2,1" Name="PropertyLoad" Alias="属性导入"
// TODO 只有当前窗体为流动分析或者流动段选择的情况下才能点击
bool bOk = false;
ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(pMainWnd);
if (nullptr != pMainWnd1)
{
iSubWnd* pSubWnd = pMainWnd1->getCurSubWnd(); //当前窗体
if (nullptr != pSubWnd)
{
QString sCurID = pSubWnd->getWndID();
bOk = (_isSame(sCurID, "3003") || _isSame(sCurID, "3004")
// || _isSame(sCurID, "6001") //TODO 这是试井设计页面,待定
);
}
}
return bOk;
} else if(_isSame(sName, "NmMap")) {
return (b1 && bLicensed);
} else if(_isSame(sName, "NmAnalChange")) {
return (b1 && bLicensed);
}
else
{
return false;
}
}
bool nmSubWndUtils::dealwithRibbonTab(iRibbonXmlTab* pTab, \
const ZxMainWindow* pMainWnd)
{
return false; //your own codes
}
void nmSubWndUtils::connectWellListSignals(nmDataAnalyzeManager* pDataManager,
nmWxParaPropertyPebi* pParaWx)
{
if (!pDataManager || !pParaWx)
return;
// 使用 Qt::UniqueConnection 防止重复连接
QObject::connect(pDataManager, SIGNAL(sigWellAdded(QString,QString,QStringList)),
pParaWx, SLOT(slotWellAdded(QString,QString,QStringList)),
Qt::UniqueConnection);
QObject::connect(pDataManager, SIGNAL(sigWellRemoved(QString,QString)),
pParaWx, SLOT(slotWellRemoved(QString,QString)),
Qt::UniqueConnection);
// 参数对象新增、删除或改名后重建面板;队列连接避免在图元绘制过程中直接刷新。
QObject::connect(pDataManager, SIGNAL(sigGeometryListChanged()),
pParaWx, SLOT(slotGeometryListChanged()),
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
}
void nmSubWndUtils::setupNumericalDockWidgets(nmDataAnalyzeManager* pDataManager,
nmSubWndMain* pSubWndMain,
iDockBaseWx* pWxDockNm1,
iDockBaseWx* pWxDockNm2)
{
Q_ASSERT(pDataManager != nullptr);
Q_ASSERT(pSubWndMain != nullptr);
Q_ASSERT(pWxDockNm1 != nullptr);
Q_ASSERT(pWxDockNm2 != nullptr);
pWxDockNm1->setWindowTitle(tr("Generate numerical model"));
pWxDockNm2->setWindowTitle(tr("Parameters"));
// 参数面板绑定数据管理器后再重建,确保首次显示使用当前分析数据。
// Dock首次创建时保证时间步配置存在已有成果中的配置保持不变。
if(pDataManager->getTimeStep() == nullptr) {
pDataManager->createTimeStep();
}
nmWxNumericalDesign* pAnalWx = new nmWxNumericalDesign(pDataManager);
nmWxParaPropertyPebi* pParaWx = new nmWxParaPropertyPebi();
pParaWx->initUI();
pParaWx->setDataManager(pDataManager);
pParaWx->rebuildAllParas();
// 保持界面编辑与底层属性变化双向同步。
nmAttrRegistry* pReg = pDataManager->getAttrRegistry();
if (pReg != nullptr)
{
connect(pParaWx, SIGNAL(sigParaChanged(QString, QVariant)),
pReg, SLOT(setValue(QString, QVariant)));
connect(pReg, SIGNAL(sigAttrChanged(QString, QVariant)),
pParaWx, SLOT(slotSetParaValue(QString, QVariant)));
}
// 数值设计面板只发出操作请求,具体处理仍由主窗口完成。
connect(pAnalWx, SIGNAL(sigGenerateClicked()),
pSubWndMain, SLOT(onGenerateButtonClicked()));
connect(pAnalWx, SIGNAL(sigResultWellChanged(QString)),
pSubWndMain, SLOT(onWellSelected(QString)));
connectWellListSignals(pDataManager, pParaWx);
pWxDockNm1->setWidget(pAnalWx);
pWxDockNm2->setWidget(pParaWx);
}
bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
{
iSubWndFitting* pSubWndF = dynamic_cast<iSubWndFitting*>(pSubWnd);
if(pSubWndF == nullptr) {
return showNmDockInitializationError(
tr("The current window is not a flow-segment analysis window."));
}
Q_ASSERT(nullptr != pSubWndF);
iDockBaseWx* pWxDockNm1 = pSubWndF->getNmDockWx(0);
iDockBaseWx* pWxDockNm2 = pSubWndF->getNmDockWx(1);
if(pWxDockNm1 == nullptr || pWxDockNm2 == nullptr) {
return showNmDockInitializationError(
tr("The numerical panel containers have not been created."));
}
Q_ASSERT(nullptr != pWxDockNm1);
Q_ASSERT(nullptr != pWxDockNm2);
if (pWxDockNm1->widget() != nullptr && pWxDockNm2->widget() != nullptr)
{
return true; //已经设定的话,则不再处理
}
// 获取当前井并检查类型是否支持
ZxDataWell* pWellData = zxCurWell;
if(pWellData == nullptr) {
return showNmDockInitializationError(
tr("No current well is available. Please select a well and try again."));
}
QString wellClass = pWellData->getWellClassEn();
// 定义支持的井类型列表
QStringList supportedTypes;
supportedTypes << "VerticalWell" << "VerticalFracturedWell" << "HorizontalMultiFracturedWell";
if (!supportedTypes.contains(wellClass)) {
return showNmDockInitializationError(
tr("The current well type '%1' is not supported. Supported types are Vertical, "
"Vertical Fractured, and Horizontal Multi-Fractured Wells.")
.arg(pWellData->getWellClassCn()));
}
// 进入到这里,说明是新打开的流动段分析,所以需要动态创建数据管理中心
// 根据分析窗口创建唯一的数据管理对象
nmDataAnalyzeManager* pDataManager = nmDataAnalyzeManager::getInstanceByFitting(pSubWndF);
if(pDataManager == nullptr) {
return showNmDockInitializationError(
tr("The numerical data manager could not be created."));
}
Q_ASSERT(nullptr != pDataManager);
// 每个成果窗口只需要绑定一次外层MDI激活信号。
// 后续切换成果窗口时,通过激活事件同步对应的数据中心和左侧面板。
nmSubWndUtils::getInstance()->connectMdiActivation(pSubWndF);
// 设置当前分析窗体
nmDataAnalyzeManager::setCurrentFitting(pSubWndF);
// TODO初始化坐标轴相关信息
nmDataAxis* pAxisData = pDataManager->getAxisData();
if(pAxisData == nullptr) {
pAxisData = new nmDataAxis;
// 将 pAxisData 存入数据中心
pDataManager->setAxisData(pAxisData);
}
// 读取解析解的参数
// createReservoir需要使用PVT初始化后确定的求解器类型
// 1.1 初始化PVT参数
pDataManager->initPvtParaFromSubFit();
// 1.2 初始化油藏参数
pDataManager->createReservoir();
// 1.3 初始化当前井数据
pDataManager->initCurWellData();
// 注册井参数到 nmAttrRegistry必须在 initCurWellData 之后,面板构建之前)
pDataManager->syncWellAttrs();
// 1.4 初始化边界数据;其他项目井只通过 Map 的“选择井”菜单按需加入。
nmDataWellBase* pCurWell = pDataManager->getCurWellData();
if(pCurWell == nullptr) {
return showNmDockInitializationError(
tr("The numerical well data could not be initialized for well '%1' (type: %2). "
"Please check the well type and its pressure, flow-rate, and flow-segment data.")
.arg(pWellData->getName())
.arg(wellClass));
}
Q_ASSERT(nullptr != pCurWell);
double dX = pCurWell->getX().getValue().toDouble();
double dY = pCurWell->getY().getValue().toDouble();
// 解析解相关参数
QMap<QString, QVariant> mapAnaParas;
mapAnaParas.clear();
pSubWndF->getBrotherDockParas(mapAnaParas, false);
// 解析解边界类型
m_Bdy_Type oBdyType = BT_None;
QString sKey = "Bdy";
if(mapAnaParas.contains(sKey)) {
int n = mapAnaParas[sKey].toInt();
if(n <= (int)BT_Rect) {
oBdyType = m_Bdy_Type(n);
}
}
// 边界距离(是井点到四周的直线距离)
if(oBdyType == BT_Rect || oBdyType == BT_Circle) {
// 设置边界数据
if(oBdyType == BT_Rect) {
// 矩形边界
VecDouble vecBdyDiss;
vecBdyDiss.clear();
QStringList list;
list << "ne" << "se" << "we" << "ee";
QStringList missingKeys;
for(int i = 0; i < list.count(); i++) {
QString s = list[i];
if(mapAnaParas.contains(s)) {
vecBdyDiss << mapAnaParas[s].toDouble();
} else {
missingKeys << s;
}
}
if(!missingKeys.isEmpty()) {
return showNmDockInitializationError(
tr("The analytical rectangular-boundary data is incomplete. Missing parameters: %1.")
.arg(missingKeys.join(", ")));
}
double top_dist = vecBdyDiss[0];
double right_dist = vecBdyDiss[1];
double bottom_dist = vecBdyDiss[2];
double left_dist = vecBdyDiss[3];
// 计算四个顶点的坐标
QPointF topLeft(dX - left_dist, dY + top_dist); // 左上角
QPointF topRight(dX + right_dist, dY + top_dist); // 右上角
QPointF bottomRight(dX + right_dist, dY - bottom_dist); // 右下角
QPointF bottomLeft(dX - left_dist, dY - bottom_dist); // 左下角
// 创建边界点集合
QVector<QPointF> vecBoundaryPoints;
vecBoundaryPoints << topLeft << topRight << bottomRight << bottomLeft;
// 创建矩形边界数据对象
nmDataOutline* pOutlineData = pDataManager->createOutline();
if(pOutlineData == nullptr) {
return showNmDockInitializationError(
tr("The rectangular-boundary data could not be created."));
}
Q_ASSERT(pOutlineData);
// 默认名称留空,由界面根据边界类型进行翻译显示。
pOutlineData->setName("");
// 设置边界点
pOutlineData->setOutlinePoints(vecBoundaryPoints);
// 设置边界类型
pOutlineData->setOutlineType(NM_Rect_Outline_Type);
// 设置流型列表假设全部为0
QVector<int> vecFlowTypes(vecBoundaryPoints.size(), 0);
pOutlineData->setFlowTypeList(vecFlowTypes);
} else {
// 圆形边界
QString s = "re";
double dRadius = 0.0;
if(mapAnaParas.contains(s)) {
dRadius = mapAnaParas[s].toDouble();
}
// 创建圆形边界数据对象
nmDataOutline* pOutlineData = pDataManager->createOutline();
if(pOutlineData == nullptr) {
return showNmDockInitializationError(
tr("The circular-boundary data could not be created."));
}
Q_ASSERT(pOutlineData);
// 默认名称留空,由界面根据边界类型进行翻译显示。
pOutlineData->setName("");
// 设置半径
pOutlineData->setRadius(dRadius);
// 设置圆心
pOutlineData->setCenter(QPointF(dX, dY));
// 设置边界类型
pOutlineData->setOutlineType(NM_Data_Outline_Type::NM_Round_Outline_Type);
// 设置流型列表假设全部为0
QVector<int> vecFlowTypes(1, 0);
pOutlineData->setFlowTypeList(vecFlowTypes);
// 创建存储点的向量
QVector<QPointF> vecPoints;
// 放入圆心
vecPoints.append(QPointF(dX, dY));
// 放入半径
vecPoints.append(QPointF(dRadius, dRadius));
// 计算圆上的四个关键点并放入向量(上、右、下、左)
vecPoints.append(QPointF(dX, dY - dRadius));
vecPoints.append(QPointF(dX + dRadius, dY));
vecPoints.append(QPointF(dX, dY + dRadius));
vecPoints.append(QPointF(dX - dRadius, dY));
// 添加到数据中
pOutlineData->setOutlinePoints(vecPoints);
}
} else {
// 解析解未提供边界时,按当前数值井位置创建一次性默认边界。
if(!pDataManager->createDefaultOutlineFromWells()) {
return showNmDockInitializationError(
tr("The default numerical boundary could not be created."));
}
}
// 1.6 创建时间步设置相关数据
pDataManager->createTimeStep();
// 创建敏感性分析数据
pDataManager->createSensitiveData();
// TODO创建nmSubWndMain的作用主要是因为求解函数在这个类里后续应该拆分掉
nmSubWndMain* pSubWndMain = new nmSubWndMain(NULL, "");
ZxMainWindow* pMainWnd = pSubWndF->getMainWindow();
// 设置当前窗口到Map
pSubWndMain->setMainWindow(pMainWnd);
setupNumericalDockWidgets(pDataManager, pSubWndMain,
pWxDockNm1, pWxDockNm2);
return true; //your own codes
}
bool nmSubWndUtils::getAnaDockParas(iSubWnd* pSubWnd)
{
iSubWndFitting* pSubWndF = dynamic_cast<iSubWndFitting*>(pSubWnd);
Q_ASSERT(nullptr != pSubWndF);
if(pSubWndF == nullptr) {
return false;
}
//////////////////////////////////////////////////////////////////
// 如下代码,请根据需要在合适位置借鉴参照编写
QMap<QString, QVariant> mapAnaParas;//当前解析解的参数
mapAnaParas.clear();
pSubWndF->getBrotherDockParas(mapAnaParas, false);
Q_ASSERT(mapAnaParas.count() > 0);
// 解析解边界类型
m_Bdy_Type oBdyType = BT_None;
QString sKey = "Bdy";
if (mapAnaParas.contains(sKey))
{
int n = mapAnaParas[sKey].toInt();
if (n <= (int)BT_Rect)
{
oBdyType = m_Bdy_Type(n);
}
}
// 边界距离(是井点到四周的直线距离)
if (oBdyType == BT_Rect || oBdyType == BT_Circle)
{
VecDouble vecBdyDiss;
vecBdyDiss.clear();
int nCount = (oBdyType == BT_Rect ? 4 : 1);
for (int i = 1; i <= nCount; i++)
{
QString sRealBdyName = getBdyRealNameOf(i, mapAnaParas); //sRealBdyName为map中边界距离key的真实内容
sKey = sRealBdyName;
if (!sKey.isEmpty() && mapAnaParas.contains(sKey))
{
vecBdyDiss << mapAnaParas[sKey].toDouble();
}
else
{
Q_ASSERT (false);//TODO
}
}
// 说明对于BT_RectvecBdyDiss中存放的四个距离数值依次为ne/下se/左we/右ee
// 对于BT_CirclevecBdyDiss中存放的是圆形半径r
}
//////////////////////////////////////////////////////////////////
return true;
}
QString nmSubWndUtils::getBdyRealNameOf(int xIndex, QMap<QString, QVariant>& map)
{
QStringList list;
list << "ne" << "se" << "we" << "ee" << "re";
int n = -1;
for (int i = 0; i < list.count(); i++)
{
QString s = list[i];
if (map.find(s) != map.end())
{
n++;
if (n == xIndex - 1)//TODO -1,从1开始
{
return s;
}
}
}
//m_sError = QString("Bdy name of 'x%1'' not found.").arg(xIndex);
//zxLogRunF(m_sError);
return "";
}
bool nmSubWndUtils::nmCalAndFresh(iSubWnd* pSubWnd, \
const ZxMainWindow* pMainWnd)
{
return false;
}
bool nmSubWndUtils::beginNumericalSaveSession(
iSubWnd* pFirstSubWnd,
const QString& sRstName,
QString* pError)
{
if(pError != NULL) {
pError->clear();
}
ZxDataWell* pDataWell = pFirstSubWnd == nullptr
? nullptr : pFirstSubWnd->getDataWell();
nmSubWndUtils* pUtils = getInstance();
if(pDataWell == nullptr || pUtils == nullptr ||
pUtils->m_pNumericalSaveSession == nullptr) {
if(pError != NULL) *pError = "Cannot resolve numerical save session root.";
return false;
}
QString sResultRootDirectory = ZxBaseUtil::getWellDirOf(
pDataWell->getName(), "Nm");
sResultRootDirectory += sRstName;
// 显式会话从空窗口清单开始,只有本轮实际遍历到的窗口会被发布。
return pUtils->m_pNumericalSaveSession->begin(
QDir::cleanPath(sResultRootDirectory),
sRstName, false, pError);
}
bool nmSubWndUtils::saveWindowGeneration(
iSubWnd* pSubWnd,
const QString& sRstName,
QString* pError)
{
if(pError != NULL) {
pError->clear();
}
iSubWndFitting* pSubWndF = dynamic_cast<iSubWndFitting*>(pSubWnd);
nmDataAnalyzeManager* pDataManager = pSubWndF == nullptr ? nullptr :
nmDataAnalyzeManager::findManagerByFitting(pSubWndF);
nmDataAnalyzeContextProvider* pProvider = nmDataAnalyzeContext::provider();
nmSubWndUtils* pUtils = getInstance();
QString sLogicalWindowDirectory;
if(pDataManager == nullptr || pProvider == nullptr || pUtils == nullptr ||
pUtils->m_pNumericalSaveSession == nullptr ||
!pUtils->m_pNumericalSaveSession->isActive() ||
!pProvider->getSaveResultDir(pSubWndF, sRstName,
sLogicalWindowDirectory)) {
if(pError != NULL) *pError = "Numerical save session is not active.";
return false;
}
const QFileInfo oWindowInfo(QDir::cleanPath(sLogicalWindowDirectory));
if(QDir::cleanPath(oWindowInfo.dir().absolutePath()) !=
QDir::cleanPath(
pUtils->m_pNumericalSaveSession->resultRootDirectory())) {
if(pError != NULL) *pError = "Numerical window belongs to another result root.";
return false;
}
return pUtils->m_pNumericalSaveSession->saveWindowGeneration(
oWindowInfo.fileName(), pDataManager, pError);
}
bool nmSubWndUtils::commitNumericalSaveSession(QString* pError)
{
nmSubWndUtils* pUtils = getInstance();
if(pUtils == nullptr || pUtils->m_pNumericalSaveSession == nullptr ||
!pUtils->m_pNumericalSaveSession->isActive()) {
if(pError != NULL) *pError = "Numerical save session is not active.";
return false;
}
return pUtils->m_pNumericalSaveSession->commit(pError);
}
void nmSubWndUtils::abortNumericalSaveSession()
{
nmSubWndUtils* pUtils = getInstance();
if(pUtils != nullptr && pUtils->m_pNumericalSaveSession != nullptr) {
pUtils->m_pNumericalSaveSession->abort();
}
}
nmNumericalSaveSessionAbortGuard::nmNumericalSaveSessionAbortGuard()
: m_bAbortRequired(true)
{
}
nmNumericalSaveSessionAbortGuard::~nmNumericalSaveSessionAbortGuard()
{
if(m_bAbortRequired) {
nmSubWndUtils::abortNumericalSaveSession();
}
}
void nmNumericalSaveSessionAbortGuard::release()
{
m_bAbortRequired = false;
}
bool nmSubWndUtils::saveRsts(iSubWnd* pSubWnd, \
QString sRstName, \
const ZxMainWindow* pMainWnd)
{
Q_ASSERT(nullptr != pSubWnd);
if(pSubWnd == nullptr) {
return false;
}
const QString sWindowId = pSubWnd->getWndID();
if(sWindowId != "3004" && sWindowId != "5118" &&
sWindowId != "5114") {
return true;
}
ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(pMainWnd);
ZxTabWidget* pTabWx = pSubWnd->getTabWx();
nmSubWndUtils* pUtils = nmSubWndUtils::getInstance();
if(pMainWnd1 == nullptr || pTabWx == nullptr || pUtils == nullptr) {
return false;
}
// 外层框架仍按 develop 的方式逐窗口回调。数值代次必须以实际分析窗口
// 作为保存锚点,不能让先到达的 Map 或网格回调改变成果井和根目录。
QVector<iSubWnd*> vecSubWnds = pMainWnd1->getAllSubWndsOf(
pTabWx, "3004", false);
iSubWnd* pSessionAnchor = vecSubWnds.isEmpty()
? pSubWnd : vecSubWnds.first();
ZxDataWell* pDataWell = pSessionAnchor == nullptr
? nullptr : pSessionAnchor->getDataWell();
if(pDataWell == nullptr) {
return false;
}
QString sResultRootDirectory = ZxBaseUtil::getWellDirOf(
pDataWell->getName(), "Nm");
sResultRootDirectory = QDir::cleanPath(
sResultRootDirectory + sRstName);
if(pUtils->m_setCompletedNumericalSaveRoots.contains(
sResultRootDirectory)) {
return true;
}
// 第一次回调在本模块内收齐当前 Tab 的完整分析窗口集合并整体发布。
QString sError;
if(!beginNumericalSaveSession(pSessionAnchor, sRstName, &sError)) {
qWarning() << sError;
return false;
}
nmNumericalSaveSessionAbortGuard oAbortGuard;
for(int nIndex = 0; nIndex < vecSubWnds.size(); ++nIndex) {
iSubWndFitting* pSubWndF =
dynamic_cast<iSubWndFitting*>(vecSubWnds[nIndex]);
if(pSubWndF == nullptr ||
nmDataAnalyzeManager::findManagerByFitting(pSubWndF) == nullptr) {
continue;
}
if(!saveWindowGeneration(pSubWndF, sRstName, &sError)) {
qWarning() << sError;
return false;
}
}
if(!commitNumericalSaveSession(&sError)) {
qWarning() << sError;
return false;
}
oAbortGuard.release();
// 同一次外层遍历后续还会回调其他窗口,下一轮事件前不重复生成代次。
pUtils->m_setCompletedNumericalSaveRoots.insert(
sResultRootDirectory);
QTimer::singleShot(0, pUtils,
SLOT(slotClearCompletedNumericalSaveRoots()));
return true;
}
void nmSubWndUtils::slotClearCompletedNumericalSaveRoots()
{
m_setCompletedNumericalSaveRoots.clear();
}
bool nmSubWndUtils::loadRsts(iSubWnd* pSubWnd, \
QString sRstName, \
const ZxMainWindow* pMainWnd)
{
Q_ASSERT(nullptr != pSubWnd);
if(pSubWnd == nullptr) {
return false;
}
ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(pMainWnd);
nmSubWndUtils* pUtilsInstance = nmSubWndUtils::getInstance();
if(nullptr != pMainWnd1) {
ZxTabWidget* pTabWx = pMainWnd1->getCurTabWx();
if(nullptr != pTabWx) {
if(pUtilsInstance->m_pTabWx == nullptr || pUtilsInstance->m_pTabWx != pTabWx) {
pUtilsInstance->m_pTabWx = pTabWx;
disconnect(pUtilsInstance->m_pTabWx, SIGNAL(currentChanged(int)), pUtilsInstance, SLOT(slotHandleTabChange(int)));
connect(pUtilsInstance->m_pTabWx, SIGNAL(currentChanged(int)), pUtilsInstance, SLOT(slotHandleTabChange(int)));
}
}
}
// 处理流动段分析窗口数据
if(pSubWnd->getWndID() == "3004") {
iSubWndFitting* pSubWndF = dynamic_cast<iSubWndFitting*>(pSubWnd);
if(pSubWndF == nullptr) {
return false;
}
// 获取当前分析窗体Code
ZxRstWnd* pRstWnd = pSubWndF->getRstUtilWnd();
if(pRstWnd == nullptr) {
return false;
}
QString sRstUtilWndCode = pRstWnd->getCode();
ZxDataWell* pCurrentWell = pSubWndF->getDataWell();
if(pCurrentWell == nullptr) {
return false;
}
// 获取该分析所在的目录
QString sDir = ZxBaseUtil::getWellDirOf(pCurrentWell->getName(), "Nm");
sDir += sRstName + "/" + sRstUtilWndCode;
// v3 的逻辑窗口目录不再直接落盘,只以成果根清单作为加载入口。
const QFileInfo oLogicalWindowInfo(QDir::cleanPath(sDir));
const QString sManifestPath = QDir(
oLogicalWindowInfo.dir().absolutePath()).filePath(
"NumericalSaveManifest.json");
if(!QFileInfo(sManifestPath).isFile()) {
const QString sLegacyJsonPath = QDir(sDir).filePath(
"Results/Numerical_Parameters.json");
if(QFileInfo(sLegacyJsonPath).isFile()) {
QMessageBox::warning(nullptr,
QObject::tr("Unsupported Numerical Result"),
QObject::tr("Numerical project version 2 is not supported. Only version 3 results can be loaded."));
}
return false;
}
bool bManifestContainsWindow = false;
QString sManifestError;
if(!nmNumericalSaveSession::containsPublishedWindow(
oLogicalWindowInfo.dir().absolutePath(),
sRstUtilWndCode, bManifestContainsWindow,
&sManifestError)) {
qWarning() << sManifestError;
QMessageBox::warning(nullptr,
tr("Numerical Result Load Failed"),
tr("The numerical result is incomplete or inconsistent and could not be loaded."));
return false;
}
if(!bManifestContainsWindow) {
// 与 develop 的“目录不存在”语义一致:当前窗口没有数值成果,
// 不创建空 Manager也不尝试从其他窗口或目录扫描推断数据。
return true;
}
// 存在,说明保存了数值解的参数,需要解析文件里的数据到数据中心
// 动态创建数据中心类
nmDataAnalyzeManager* pDataManager = nmDataAnalyzeManager::getInstanceByFitting(pSubWndF);
if(pDataManager == nullptr) {
return false;
}
// 监听外层成果窗口切换,保证重新激活已经打开过的成果时可以同步数据中心。
pUtilsInstance->connectMdiActivation(pSubWndF);
// 设置当前操作的是哪一个分析
nmDataAnalyzeManager::setCurrentFitting(pSubWndF);
// v3 在候选 Manager 中读取 PVT 和全部成果,成功后才替换当前状态。
if(!pDataManager->loadNmResult(sDir)) {
QMessageBox::warning(nullptr,
tr("Numerical Result Load Failed"),
tr("The numerical result is incomplete or inconsistent and could not be loaded."));
return false;
}
// 注册井参数到 nmAttrRegistry必须在 loadNmResult 之后,面板构建之前)
pDataManager->syncWellAttrs();
// 切换左侧参数视图
pSubWndF->swapAnaNmDocks(true);
iDockBaseWx* pWxDockNm1 = pSubWndF->getNmDockWx(0);
iDockBaseWx* pWxDockNm2 = pSubWndF->getNmDockWx(1);
Q_ASSERT(nullptr != pWxDockNm1);
Q_ASSERT(nullptr != pWxDockNm2);
if(pWxDockNm1 == nullptr || pWxDockNm2 == nullptr) {
return false;
}
if(pWxDockNm1->widget() != nullptr &&
pWxDockNm2->widget() != nullptr) {
return true; // 与 develop 一致,已有数值面板不重复创建。
}
// 创建Map暂时
nmSubWndMain* pSubWndMain = new nmSubWndMain(NULL, "");
ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(pMainWnd);
pSubWndMain->setMainWindow(pMainWnd1);
setupNumericalDockWidgets(pDataManager, pSubWndMain,
pWxDockNm1, pWxDockNm2);
// v3 场图只以独立快照是否存在为准,不再依赖旧时间步 Map。
if(pDataManager->hasPebiResultSnapshot()) {
QString sErrorMessage;
// 刷新场图
QWidget* p3DWidget = pSubWndF->getFitSubRstWxOf(FSRT_Nm3D, true, &sErrorMessage);
// 创建 vtkWidget
nmWxPostprocessingAnimationWidget* pVtkWidget = nullptr;
pVtkWidget = new nmWxPostprocessingAnimationWidget(
nullptr, pDataManager);
if(p3DWidget == nullptr || pVtkWidget == nullptr) {
delete pVtkWidget;
return false;
}
// 先清理
// 获取当前布局
QLayout* p3DLayout = p3DWidget->layout();
if(p3DLayout) {
// 移除布局中的所有控件
QLayoutItem* item;
while((item = p3DLayout->takeAt(0))) {
if(item->widget()) {
// 删除控件
delete item->widget();
}
delete item; // 删除布局项
}
// 删除布局
delete p3DLayout;
}
// 确保布局被移除
p3DWidget->setLayout(nullptr);
// 创建一个垂直布局管理器
QVBoxLayout* p3DnewLayout = new QVBoxLayout(p3DWidget);
// 将现有的控件添加到布局中
p3DnewLayout->addWidget(pVtkWidget);
// 设置 widget 的布局
p3DWidget->setLayout(p3DnewLayout);
// 刷新结果参数
// 结果参数界面
QWidget* pRstWidget = pSubWndF->getFitSubRstWxOf(FSRT_NmRst, true, &sErrorMessage);
if(pRstWidget == nullptr) {
return false;
}
// 与左侧共用属性面板类,结果区使用独立实例和只读参数列表。
nmWxParaPropertyPebi* pResultWidget = new nmWxParaPropertyPebi();
pResultWidget->initUI();
pResultWidget->setDataManager(pDataManager);
pResultWidget->rebuildResultParas();
// 先清理
// 获取当前布局
QLayout* pRstLayout = pRstWidget->layout();
if(pRstLayout) {
// 移除布局中的所有控件
QLayoutItem* item;
while((item = pRstLayout->takeAt(0))) {
if(item->widget()) {
// 删除控件
delete item->widget();
}
delete item; // 删除布局项
}
// 删除布局
delete pRstLayout;
}
// 确保布局被移除
pRstWidget->setLayout(nullptr);
// 创建一个垂直布局管理器
QVBoxLayout* pRstnewLayout = new QVBoxLayout(pRstWidget);
// 将现有的控件添加到布局中
pRstnewLayout->addWidget(pResultWidget);
// 设置 widget 的布局
pRstWidget->setLayout(pRstnewLayout);
}
}
//// 请根据需要确定,文件存储的路径
//QString sDir = ZxBaseUtil::getCurWellDirOf("Nm");
////iSubWndFitting* pSubWndF = dynamic_cast<iSubWndFitting*>(pSubWnd);
////Q_ASSERT(nullptr != pSubWndF);
//QString sRstUtilWndCode = pSubWnd->getRstUtilWnd()->getCode();
//QByteArray ba = QByteArray();//这是你读取出来的内容
//ZxUiBase* pWx = nullptr; //your own widget
//if (nullptr != pWx && !ba.isNull())
//{
// // 根据二进制内容更新窗体内容
// // 注意loadFromByteArr是 iWxBasePVT模块中的接口如果需要请参考下面源码进行自行实现
// //pWx->loadFromByteArr(ba);
//}
// 如果需要把二进制内容更新至其它如double数组、map等存入可以参照 ZxBaHelper 提供的接口
// 其它代码
return true;
}
// 如果需要加载某个窗体继承自ZxUiBase并且实现了序列化接口onSerialize/onDeserialize
// 则可以参照如下进行
#ifdef _SCAN_OLD_CODES_
bool iWxBase::saveAsByteArr(QByteArray& v)
{
QString sRoot = this->objectName() + "abc";
ZxXpfDoc xpf(sRoot);
ZxSerializer ser(&xpf, true);
onSerialize(&ser);
QDataStream out(&v, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_8);
if (xpf.save(out))
{
return true;
}
else
{
return false;
}
return true;
}
bool iWxBase::loadFromByteArr(QByteArray& v)
{
QDataStream in(&v, QIODevice::ReadOnly);
// quint32 nVer = ser->getDocDate();
// if (nVer > (quint32)(20180101))
// {
// in.setVersion(QDataStream::Qt_4_8);
// }
// else
{
in.setVersion(QDataStream::Qt_4_8);
}
QString sRoot = this->objectName() + "abc";
ZxXpfDoc xpf(sRoot);
if (!xpf.load(in))
{
zxLogRunW(tr("Failed to load ByteArray"));
return false;
}
ZxSerializer ser(&xpf, false);
onDeserialize(&ser);
return true;
}
#endif
void nmSubWndUtils::slotHandleTabChange(int index)
{
// 找到索引对应的流动段分析窗体
if(m_pTabWx == nullptr) {
return;
}
if(index >= 0 && index < m_pTabWx->count()) {
iSubWnd* pSubWnd = dynamic_cast<iSubWnd*>(m_pTabWx->widget(index));
if(pSubWnd == nullptr) {
return;
}
// 切换的是流动段分析
if(pSubWnd->getWndID() == "3004") {
iSubWndFitting* pSubWndF = dynamic_cast<iSubWndFitting*>(pSubWnd);
if(pSubWndF == nullptr) {
return;
}
// 查找有没有对应的数据中心实例
nmDataAnalyzeManager* pDataManger = nmDataAnalyzeManager::findManagerByFitting(pSubWndF);
if(pDataManger != nullptr && nmDataAnalyzeManager::getCurrentFitting() != pSubWndF) {
// 有对应的实例,并且不是当前分析
nmDataAnalyzeManager::setCurrentFitting(pSubWndF);
ZxMainWindow* pMainWnd = pSubWndF->getMainWindow();
Q_ASSERT(nullptr != pMainWnd);
if(pMainWnd == nullptr) {
return;
}
// 如果地图和网格窗体存在,则更新
QVector<iSubWnd*> vecSubWnds = pMainWnd->getAllSubWndsOf(m_pTabWx);
foreach(iSubWnd* pSub, vecSubWnds) {
nmSubWndMain* pMap = dynamic_cast<nmSubWndMain*>(pSub);
if(nullptr != pMap) {
// 更新地图相关信息
pMap->updateMapByDataManager(pDataManger);
}
// 网格窗口跟随当前分析切换数据来源。
nmSubWndGrid* pSubWndGrid = dynamic_cast<nmSubWndGrid*>(pSub);
if(nullptr != pSubWndGrid) {
// 由网格窗口统一解除旧连接、绑定新分析并处理旧后台任务。
pSubWndGrid->setDataManager(pDataManger);
}
}
}
if(pDataManger != nullptr) {
// 切换分析后必须同步活动数值面板。即使DataManager已经是当前对象
// 结果井下拉框也可能仍指向后创建的另一个分析面板。
iDockBaseWx* pWxDockNm1 = pSubWndF->getNmDockWx(0);
nmWxNumericalDesign* pAnalWx = pWxDockNm1 == nullptr
? nullptr : dynamic_cast<nmWxNumericalDesign*>(pWxDockNm1->widget());
if(pAnalWx != nullptr) {
pAnalWx->activateCurrentInstance();
}
}
}
}
}
// 监听外层MDI成果窗口切换
void nmSubWndUtils::connectMdiActivation(iSubWndFitting* pSubWndF)
{
if(pSubWndF == nullptr) {
return;
}
// 关闭成果窗口或流动段分析页签时都会销毁对应的iSubWndFitting。
// 在Close事件中先解除图元引用队列槽再等子对象完成析构后释放数据管理器。
m_mapFittingKeys.insert(pSubWndF, pSubWndF);
pSubWndF->installEventFilter(this);
connect(pSubWndF, SIGNAL(destroyed(QObject*)),
this, SLOT(slotHandleFittingDestroyed(QObject*)),
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
ZxMdiSubWindow* pMdiChild = pSubWndF->getMdiChild();
ZxMainWindow* pMainWnd = pSubWndF->getMainWindow();
if(pMdiChild != nullptr) {
// 外层成果关闭时先记录状态,避免子窗口析构期间主动遍历半析构画布。
pMdiChild->installEventFilter(this);
connect(pMdiChild, SIGNAL(sigConformClosing(bool&)),
this, SLOT(slotHandleMdiClosing(bool&)),
Qt::UniqueConnection);
connect(pMdiChild, SIGNAL(destroyed(QObject*)),
this, SLOT(slotHandleMdiDestroyed(QObject*)),
Qt::UniqueConnection);
}
if(pMdiChild == nullptr || pMainWnd == nullptr || pMdiChild->mdiArea() == nullptr) {
return;
}
m_pMainWnd = pMainWnd;
// 一个QMdiArea中可以同时存在多个成果窗口。
// 使用UniqueConnection避免每次打开成果时重复绑定同一个激活信号。
connect(pMdiChild->mdiArea(), SIGNAL(subWindowActivated(QMdiSubWindow*)),
this, SLOT(slotHandleMdiSubWindowActivated(QMdiSubWindow*)),
Qt::UniqueConnection);
}
// 在窗口仍完整时处理关闭事件
bool nmSubWndUtils::eventFilter(QObject* pObject, QEvent* pEvent)
{
if(pEvent == nullptr || pEvent->type() != QEvent::Close) {
return QObject::eventFilter(pObject, pEvent);
}
ZxMdiSubWindow* pMdiChild = qobject_cast<ZxMdiSubWindow*>(pObject);
if(pMdiChild != nullptr) {
m_closingMdiChildren.insert(pMdiChild);
return QObject::eventFilter(pObject, pEvent);
}
iSubWndFitting* pSubWndF = m_mapFittingKeys.value(pObject, nullptr);
if(pSubWndF != nullptr && !m_closingMdiChildren.contains(pSubWndF->getMdiChild())) {
detachFittingPlot(pSubWndF);
}
return QObject::eventFilter(pObject, pEvent);
}
// 单独关闭流动段分析页签时,先解除画布图元对数据的引用
void nmSubWndUtils::detachFittingPlot(iSubWndFitting* pSubWndF)
{
nmDataAnalyzeManager* pDataManager = nmDataAnalyzeManager::findManagerByFitting(pSubWndF);
if(pDataManager == nullptr) {
return;
}
nmGuiPlot* pPlot = pDataManager->getPlot();
if(pPlot == nullptr) {
return;
}
// 关闭页签时可能会先触发页签切换导致Map短暂刷新到待关闭分析。
// 以DataManager是否仍绑定Plot判断Map是否实际展示该分析避免只看currentFitting误判。
pPlot->deleteAllPlotObjs();
pDataManager->setPlot(nullptr);
if(nmDataAnalyzeManager::getCurrentFitting() != pSubWndF) {
nmDataAnalyzeManager* pCurManager = nmDataAnalyzeManager::getCurrentInstance();
if(pCurManager != nullptr) {
// 关闭的不是当前分析时清掉待关闭分析图元后恢复当前分析的Map显示。
pCurManager->setPlot(pPlot);
pPlot->setPlotsByDataManger(pCurManager);
}
}
}
// 外层成果窗口关闭时先清理仍绑定到Map上的图元
void nmSubWndUtils::detachMdiFittingPlots(ZxMdiSubWindow* pMdiChild)
{
if(pMdiChild == nullptr || m_pMainWnd == nullptr) {
return;
}
ZxTabWidget* pTabWx = m_pMainWnd->getTabWxOf(pMdiChild);
if(pTabWx == nullptr) {
return;
}
QVector<iSubWnd*> vecSubWnds = m_pMainWnd->getAllSubWndsOf(pTabWx, "3004", false);
// 同一个成果内可能有多个分析只清理仍占用Map Plot的DataManager。
foreach(iSubWnd* pSubWnd, vecSubWnds) {
iSubWndFitting* pSubWndF = dynamic_cast<iSubWndFitting*>(pSubWnd);
nmDataAnalyzeManager* pDataManager = nmDataAnalyzeManager::findManagerByFitting(pSubWndF);
if(pDataManager == nullptr || pDataManager->getPlot() == nullptr) {
continue;
}
nmGuiPlot* pPlot = pDataManager->getPlot();
pPlot->deleteAllPlotObjs();
pDataManager->setPlot(nullptr);
}
}
// 流动段分析窗口销毁后,释放对应的数据管理器
void nmSubWndUtils::slotHandleFittingDestroyed(QObject* pObject)
{
// 队列槽执行时窗口对象已经销毁只使用析构前保存的地址作为静态映射key。
iSubWndFitting* pSubWndF = m_mapFittingKeys.take(pObject);
nmDataAnalyzeManager::removeInstanceByFitting(pSubWndF);
}
// 外层成果取消关闭时移除Close事件过滤器提前保存的状态
void nmSubWndUtils::slotHandleMdiClosing(bool& bAllowClose)
{
if(!bAllowClose) {
m_closingMdiChildren.remove(sender());
return;
}
// 用户确认关闭成果窗口后再清理Map避免取消关闭时误清当前显示。
ZxMdiSubWindow* pMdiChild = qobject_cast<ZxMdiSubWindow*>(sender());
if(pMdiChild != nullptr) {
detachMdiFittingPlots(pMdiChild);
}
}
// 外层成果窗口析构后,移除仅用于生命周期判断的地址
void nmSubWndUtils::slotHandleMdiDestroyed(QObject* pObject)
{
m_closingMdiChildren.remove(pObject);
}
// 切换外层MDI成果窗口后同步当前流动段分析窗口
void nmSubWndUtils::slotHandleMdiSubWindowActivated(QMdiSubWindow* pSubWindow)
{
ZxMdiSubWindow* pMdiChild = qobject_cast<ZxMdiSubWindow*>(pSubWindow);
if(pMdiChild == nullptr || m_pMainWnd == nullptr) {
return;
}
ZxTabWidget* pTabWx = m_pMainWnd->getTabWxOf(pMdiChild);
if(pTabWx == nullptr) {
return;
}
// 每个成果窗口内部都有自己的页签容器。
// 切换外层成果后,保存当前容器并继续监听该成果内部的页签切换。
m_pTabWx = pTabWx;
connect(m_pTabWx, SIGNAL(currentChanged(int)),
this, SLOT(slotHandleTabChange(int)),
Qt::UniqueConnection);
// 找到当前成果中的流动段分析窗口。
// 窗口ID为3004后续的数据中心和左侧面板都属于这个分析窗口。
QVector<iSubWnd*> vecSubWnds = m_pMainWnd->getAllSubWndsOf(m_pTabWx, "3004", false);
if(vecSubWnds.isEmpty()) {
return;
}
// 如果当前选中的分析属于本Tab容器维持用户通过弹窗或Tab点击的选择不变
iSubWndFitting* pCurFitting = nmDataAnalyzeManager::getCurrentFitting();
if(pCurFitting != nullptr) {
foreach(iSubWnd* pSub, vecSubWnds) {
if(pSub == pCurFitting) {
return;
}
}
}
iSubWndFitting* pSubWndF = dynamic_cast<iSubWndFitting*>(vecSubWnds.first());
if(pSubWndF == nullptr || nmDataAnalyzeManager::findManagerByFitting(pSubWndF) == nullptr) {
return;
}
// 先切换数据中心,再刷新左侧面板。
// 下拉框填充函数会读取getCurrentInstance(),顺序不能颠倒。
nmDataAnalyzeManager::setCurrentFitting(pSubWndF);
iDockBaseWx* pWxDockNm1 = pSubWndF->getNmDockWx(0);
if(pWxDockNm1 == nullptr) {
return;
}
nmWxNumericalDesign* pAnalWx = dynamic_cast<nmWxNumericalDesign*>(pWxDockNm1->widget());
if(pAnalWx != nullptr) {
// 已经打开过的成果会复用旧面板,不会再次进入构造函数。
// 主动更新活动面板指针,保证静态通知刷新的是当前成果中的下拉框。
pAnalWx->activateCurrentInstance();
}
}