diff --git a/Include/nmNum/nmData/nmDataAnalyzeManager.h b/Include/nmNum/nmData/nmDataAnalyzeManager.h index f212b20..8df5c8c 100644 --- a/Include/nmNum/nmData/nmDataAnalyzeManager.h +++ b/Include/nmNum/nmData/nmDataAnalyzeManager.h @@ -80,6 +80,8 @@ class NM_DATA_EXPORT nmDataAnalyzeManager : public ZxDataObjectBin Q_OBJECT public: nmDataAnalyzeManager(); + // 释放当前流动段分析独占的数据对象 + ~nmDataAnalyzeManager(); // 分析窗口和数据管理器的映射关系 static QMap s_mapDataAnalManager; @@ -89,6 +91,8 @@ class NM_DATA_EXPORT nmDataAnalyzeManager : public ZxDataObjectBin // 根据流动段分析窗口的指针作为唯一标识 static nmDataAnalyzeManager* getInstanceByFitting(iSubWndFitting* pSubWndF); + // 流动段分析窗口销毁后,移除并释放对应的数据管理器 + static void removeInstanceByFitting(iSubWndFitting* pSubWndF); /** * @brief 根据流动段分析窗口指针(key)查找对应的分析管理器实例(value) diff --git a/Include/nmNum/nmSubWnd/nmSubWndUtils.h b/Include/nmNum/nmSubWnd/nmSubWndUtils.h index ca9a964..a1e8f19 100644 --- a/Include/nmNum/nmSubWnd/nmSubWndUtils.h +++ b/Include/nmNum/nmSubWnd/nmSubWndUtils.h @@ -2,6 +2,7 @@ #include #include +#include #include "nmSubWnd_global.h" class iRibbonXmlCmd; @@ -11,6 +12,7 @@ class iSubWnd; class iDockBaseWx; class ZxTabWidget; class QMdiSubWindow; +class QEvent; class iSubWndFitting; // 本来主要是为数值部分提供SubWnd初始化构造的方法 @@ -116,11 +118,24 @@ public: void slotHandleTabChange(int index); // 外层MDI成果窗口切换后,同步当前流动段分析窗口和左侧数值解面板 void slotHandleMdiSubWindowActivated(QMdiSubWindow* pSubWindow); + // 关闭成果或流动段分析后,释放对应的数据管理器 + void slotHandleFittingDestroyed(QObject* pObject); + // 外层成果取消关闭时,恢复正常的页签清理逻辑 + void slotHandleMdiClosing(bool& bAllowClose); + // 外层成果窗口析构后,移除保存的关闭状态 + void slotHandleMdiDestroyed(QObject* pObject); + + protected: + + // 在窗口仍完整时处理关闭事件,避免析构阶段访问已释放的画布对象 + virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; private: // 监听外层MDI成果窗口切换,区别于成果窗口内部的页签切换 void connectMdiActivation(iSubWndFitting* pSubWndF); + // 单独关闭流动段分析页签时,移除仍引用其数据的画布图元 + void detachFittingPlot(iSubWndFitting* pSubWndF); // 因为边界是可以旋转的,所以真实的顺序并非ee,se,we,ne,根据索引传递,查阅真实的,比如 ee,se,we,ne // xIndex 从1开始,如果矩形,则分别传入,1,2,3,4 如果是圆形,则传入1 @@ -130,4 +145,9 @@ public: ZxMainWindow* m_pMainWnd; ZxTabWidget* m_pTabWx; + + // destroyed信号异步处理时,通过QObject地址取回析构前保存的分析窗口key + QMap m_mapFittingKeys; + // 外层成果窗口关闭时,其画布由自身析构流程释放,不能再次主动遍历 + QSet m_closingMdiChildren; }; diff --git a/Src/nmNum/nmData/nmDataAnalyzeManager.cpp b/Src/nmNum/nmData/nmDataAnalyzeManager.cpp index 1fc172a..07813d5 100644 --- a/Src/nmNum/nmData/nmDataAnalyzeManager.cpp +++ b/Src/nmNum/nmData/nmDataAnalyzeManager.cpp @@ -293,6 +293,59 @@ ZX_DEFINE_DYNAMIC(DataAnalyzeManager, nmDataAnalyzeManager) } // 初始化静态成员 +nmDataAnalyzeManager::~nmDataAnalyzeManager() +{ + // 图元只引用数据,数据统一由DataManager释放。 + // 关闭成果或流动段分析后,清理该分析窗口对应的全部数据。 + // 这两个成员是借用引用或容器内对象的别名,不单独delete。 + m_pCurDataWell = nullptr; + m_pNmGuiPlot = nullptr; + + // 先释放容器中的数据对象,再释放其余独占的单对象数据。 + qDeleteAll(m_vWellData); + m_vWellData.clear(); + qDeleteAll(m_vFaultData); + m_vFaultData.clear(); + qDeleteAll(m_vFractureData); + m_vFractureData.clear(); + qDeleteAll(m_vRegionData); + m_vRegionData.clear(); + qDeleteAll(m_vRegionMarkData); + m_vRegionMarkData.clear(); + qDeleteAll(m_vecLayers); + m_vecLayers.clear(); + + delete m_outlineData; + m_outlineData = nullptr; + delete m_axisData; + m_axisData = nullptr; + delete m_pAutomaticFittingData; + m_pAutomaticFittingData = nullptr; + delete m_reservoirData; + m_reservoirData = nullptr; + delete m_pMeasuringScaleData; + m_pMeasuringScaleData = nullptr; + delete m_pMeasureData; + m_pMeasureData = nullptr; + delete m_pGeoRefData; + m_pGeoRefData = nullptr; + delete m_pForecastData; + m_pForecastData = nullptr; + delete m_pSensitiveData; + m_pSensitiveData = nullptr; + delete m_pDiagnosticData; + m_pDiagnosticData = nullptr; + delete m_pebiPvtPara; + m_pebiPvtPara = nullptr; + delete m_pMixedResults; + m_pMixedResults = nullptr; + delete m_pLayerData; + m_pLayerData = nullptr; + delete m_pTimeStep; + m_pTimeStep = nullptr; + +} + QMap nmDataAnalyzeManager::s_mapDataAnalManager; iSubWndFitting* nmDataAnalyzeManager::s_pCurSubWndFitting = nullptr; @@ -307,6 +360,21 @@ nmDataAnalyzeManager* nmDataAnalyzeManager::getInstanceByFitting(iSubWndFitting* return pInstance; } +void nmDataAnalyzeManager::removeInstanceByFitting(iSubWndFitting* pSubWndF) +{ + if(pSubWndF == nullptr) { + return; + } + + // take()先解除窗口和manager的映射,避免析构期间再次找到待释放对象。 + nmDataAnalyzeManager* pInstance = s_mapDataAnalManager.take(pSubWndF); + if(s_pCurSubWndFitting == pSubWndF) { + s_pCurSubWndFitting = nullptr; + } + + delete pInstance; +} + nmDataAnalyzeManager* nmDataAnalyzeManager::getCurrentInstance() { if(s_mapDataAnalManager.contains(s_pCurSubWndFitting)) { diff --git a/Src/nmNum/nmPlot/nmObjLineCrack.cpp b/Src/nmNum/nmPlot/nmObjLineCrack.cpp index 4227d2a..c825dbd 100644 --- a/Src/nmNum/nmPlot/nmObjLineCrack.cpp +++ b/Src/nmNum/nmPlot/nmObjLineCrack.cpp @@ -32,10 +32,8 @@ nmObjLineCrack::nmObjLineCrack(const QString& sName, \ nmObjLineCrack::~nmObjLineCrack() { - if(m_fractureData != nullptr) { - delete m_fractureData; - m_fractureData = nullptr; - } + // 裂缝数据由DataManager统一释放,图元只保存引用。 + m_fractureData = nullptr; } void nmObjLineCrack::init(const QString& sName, \ diff --git a/Src/nmNum/nmPlot/nmObjPointWell.cpp b/Src/nmNum/nmPlot/nmObjPointWell.cpp index f4e07d0..ff31794 100644 --- a/Src/nmNum/nmPlot/nmObjPointWell.cpp +++ b/Src/nmNum/nmPlot/nmObjPointWell.cpp @@ -69,11 +69,8 @@ nmObjPointWell::nmObjPointWell(const QString& sName, \ nmObjPointWell::~nmObjPointWell() { - if (m_pNmWellData != nullptr) - { - delete m_pNmWellData; - m_pNmWellData = nullptr; - } + // 井数据由DataManager统一释放,图元只保存引用。 + m_pNmWellData = nullptr; } void nmObjPointWell::init(const QString& sName, \ diff --git a/Src/nmNum/nmPlot/nmObjPolygonOutline.cpp b/Src/nmNum/nmPlot/nmObjPolygonOutline.cpp index 1195dc9..e56f461 100644 --- a/Src/nmNum/nmPlot/nmObjPolygonOutline.cpp +++ b/Src/nmNum/nmPlot/nmObjPolygonOutline.cpp @@ -31,10 +31,8 @@ nmObjPolygonOutline::nmObjPolygonOutline(const QString& sName, \ nmObjPolygonOutline::~nmObjPolygonOutline() { - if (m_outlineData != nullptr) { - delete m_outlineData; - m_outlineData = nullptr; - } + // 边界数据由DataManager统一释放,图元只保存引用。 + m_outlineData = nullptr; } void nmObjPolygonOutline::init(const QString& sName, \ diff --git a/Src/nmNum/nmPlot/nmObjRectOutline.cpp b/Src/nmNum/nmPlot/nmObjRectOutline.cpp index a174e5c..95af62e 100644 --- a/Src/nmNum/nmPlot/nmObjRectOutline.cpp +++ b/Src/nmNum/nmPlot/nmObjRectOutline.cpp @@ -35,10 +35,8 @@ nmObjRectOutline::nmObjRectOutline(const QString& sName, \ nmObjRectOutline::~nmObjRectOutline() { - if(m_outlineData != nullptr) { - delete m_outlineData; - m_outlineData = nullptr; - } + // 边界数据由DataManager统一释放,图元只保存引用。 + m_outlineData = nullptr; } void nmObjRectOutline::init(const QString& sName, \ diff --git a/Src/nmNum/nmPlot/nmObjRegion.cpp b/Src/nmNum/nmPlot/nmObjRegion.cpp index 47a582d..298dc4e 100644 --- a/Src/nmNum/nmPlot/nmObjRegion.cpp +++ b/Src/nmNum/nmPlot/nmObjRegion.cpp @@ -34,10 +34,8 @@ nmObjRegion::nmObjRegion(const QString& sName, \ nmObjRegion::~nmObjRegion() { - if(m_regionData != nullptr) { - delete m_regionData; - m_regionData = nullptr; - } + // 复合区数据由DataManager统一释放,图元只保存引用。 + m_regionData = nullptr; if(m_regionMark != nullptr) { m_regionMark = nullptr; diff --git a/Src/nmNum/nmPlot/nmObjRegionMark.cpp b/Src/nmNum/nmPlot/nmObjRegionMark.cpp index 877d668..e3491af 100644 --- a/Src/nmNum/nmPlot/nmObjRegionMark.cpp +++ b/Src/nmNum/nmPlot/nmObjRegionMark.cpp @@ -46,10 +46,8 @@ nmObjRegionMark::nmObjRegionMark(const QString& sName, \ nmObjRegionMark::~nmObjRegionMark() { - if(m_regionMarkData != nullptr) { - delete m_regionMarkData; - m_regionMarkData = nullptr; - } + // 区域标记数据由DataManager统一释放,图元只保存引用。 + m_regionMarkData = nullptr; } QColor nmObjRegionMark::generateColor(int index) diff --git a/Src/nmNum/nmPlot/nmObjRoundOutline.cpp b/Src/nmNum/nmPlot/nmObjRoundOutline.cpp index 21cc955..589ae9a 100644 --- a/Src/nmNum/nmPlot/nmObjRoundOutline.cpp +++ b/Src/nmNum/nmPlot/nmObjRoundOutline.cpp @@ -31,10 +31,8 @@ nmObjRoundOutline::nmObjRoundOutline(const QString& sName, \ nmObjRoundOutline::~nmObjRoundOutline() { - if (m_outlineData != nullptr) { - delete m_outlineData; - m_outlineData = nullptr; - } + // 边界数据由DataManager统一释放,图元只保存引用。 + m_outlineData = nullptr; } void nmObjRoundOutline::init(const QString& sName, \ diff --git a/Src/nmNum/nmSubWnd/nmSubWndUtils.cpp b/Src/nmNum/nmSubWnd/nmSubWndUtils.cpp index fd4932b..10a544c 100644 --- a/Src/nmNum/nmSubWnd/nmSubWndUtils.cpp +++ b/Src/nmNum/nmSubWnd/nmSubWndUtils.cpp @@ -1,6 +1,7 @@ #include "zxLogInstance.h" #include "iRibbonXmlCmd.h" #include +#include #include #include #include "zxSysUtils.h" @@ -1108,8 +1109,27 @@ void nmSubWndUtils::connectMdiActivation(iSubWndFitting* pSubWndF) return; } + // 关闭成果窗口或流动段分析页签时,都会销毁对应的iSubWndFitting。 + // 在Close事件中先解除图元引用,队列槽再等子对象完成析构后释放数据管理器。 + m_mapFittingKeys.insert(pSubWndF, pSubWndF); + pSubWndF->installEventFilter(this); + connect(pSubWndF, SIGNAL(destroyed(QObject*)), + this, SLOT(slotHandleFittingDestroyed(QObject*)), + static_cast(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; } @@ -1122,6 +1142,63 @@ void nmSubWndUtils::connectMdiActivation(iSubWndFitting* pSubWndF) 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(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 && nmDataAnalyzeManager::getCurrentFitting() == pSubWndF) { + nmGuiPlot* pPlot = pDataManager->getPlot(); + if(pPlot != nullptr) { + // 画布图元引用DataManager中的数据,释放数据前先移除仍在显示的图元。 + pPlot->deleteAllPlotObjs(); + } + } +} + +// 流动段分析窗口销毁后,释放对应的数据管理器 +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()); + } +} + +// 外层成果窗口析构后,移除仅用于生命周期判断的地址 +void nmSubWndUtils::slotHandleMdiDestroyed(QObject* pObject) +{ + m_closingMdiChildren.remove(pObject); +} + // 切换外层MDI成果窗口后,同步当前流动段分析窗口 void nmSubWndUtils::slotHandleMdiSubWindowActivated(QMdiSubWindow* pSubWindow) {