diff --git a/Include/nmNum/nmPlot/nmObjPointWell.h b/Include/nmNum/nmPlot/nmObjPointWell.h index f3c73ba3..2d7a68c3 100644 --- a/Include/nmNum/nmPlot/nmObjPointWell.h +++ b/Include/nmNum/nmPlot/nmObjPointWell.h @@ -74,6 +74,9 @@ class NM_PLOT_EXPORT nmObjPointWell : public nmObjPoint { // 删除当前对象里的参数对象 virtual void removeData() override; + /** @brief 删除当前井对应的数值数据并解除绑定。 */ + bool removeBoundData(); + void setGraphicBinder(nmPlotGraphicBinder* pBinder) { m_pGraphicBinder = pBinder; } nmPlotGraphicBinder* getGraphicBinder() const { return m_pGraphicBinder; } // 标记井数据已由Binder绑定,图元只保留井类型 diff --git a/Include/nmNum/nmSubWnd/nmSubWndUtils.h b/Include/nmNum/nmSubWnd/nmSubWndUtils.h index fc43283b..1b243711 100644 --- a/Include/nmNum/nmSubWnd/nmSubWndUtils.h +++ b/Include/nmNum/nmSubWnd/nmSubWndUtils.h @@ -2,6 +2,7 @@ #include #include +#include #include #include "nmSubWnd_global.h" @@ -134,7 +135,7 @@ public: const ZxMainWindow* pMainWnd); /// @brief 获取缓存的TabWidget指针,确保弹窗与MDI切换使用同一个容器,指针比较才能正确 - ZxTabWidget* getTabWidget() const { return m_pTabWx; } + ZxTabWidget* getTabWidget() const; /// @brief 建立井列表变化与PEBI参数面板的刷新连接 /// @param pDataManager 数据管理器 @@ -146,6 +147,8 @@ public: // 声明处理 ZxTabWidget 切换的槽函数 // 信号 currentChanged(int) 传递一个 int 参数 void slotHandleTabChange(int index); + // 当前监听的页签容器销毁后清除受保护指针 + void slotHandleTabWidgetDestroyed(QObject* pObject); // 外层MDI成果窗口切换后,同步当前流动段分析窗口和左侧数值解面板 void slotHandleMdiSubWindowActivated(QMdiSubWindow* pSubWindow); // 关闭成果或流动段分析后,释放对应的数据管理器 @@ -173,6 +176,8 @@ public: // 监听外层MDI成果窗口切换,区别于成果窗口内部的页签切换 void connectMdiActivation(iSubWndFitting* pSubWndF); + // 统一切换页签监听,确保先解除旧连接再保存新容器 + void setObservedTabWidget(ZxTabWidget* pTabWx); // 单独关闭流动段分析页签时,移除仍引用其数据的画布图元 void detachFittingPlot(iSubWndFitting* pSubWndF); // 关闭外层成果窗口时,提前解除其Map图元对分析数据的引用 @@ -185,7 +190,7 @@ public: // 存储当前操作的是哪一个窗体 ZxMainWindow* m_pMainWnd; - ZxTabWidget* m_pTabWx; + QPointer m_pTabWx; // destroyed信号异步处理时,通过QObject地址取回析构前保存的分析窗口key QMap m_mapFittingKeys; diff --git a/Src/nmNum/nmPlot/nmObjPointWell.cpp b/Src/nmNum/nmPlot/nmObjPointWell.cpp index 6cac8f18..29b4c234 100644 --- a/Src/nmNum/nmPlot/nmObjPointWell.cpp +++ b/Src/nmNum/nmPlot/nmObjPointWell.cpp @@ -495,9 +495,13 @@ void nmObjPointWell::paintBack(QPainter* painter, const ZxPaintParam& param) void nmObjPointWell::removeData() { - if (m_pGraphicBinder) { + removeBoundData(); +} + +bool nmObjPointWell::removeBoundData() +{ + return m_pGraphicBinder == nullptr || m_pGraphicBinder->removeWellBinding(this); - } } void nmObjPointWell::markWellDataBound(NM_WELL_MODEL wellType) diff --git a/Src/nmNum/nmPlot/nmPlotGraphicBinder.cpp b/Src/nmNum/nmPlot/nmPlotGraphicBinder.cpp index 9d5480a6..96e42110 100644 --- a/Src/nmNum/nmPlot/nmPlotGraphicBinder.cpp +++ b/Src/nmNum/nmPlot/nmPlotGraphicBinder.cpp @@ -1619,7 +1619,7 @@ void nmPlotGraphicBinder::updateWellDataMapping( static_cast(pGraphic)->update(); } -// 删除井图元前,先由数据层确认删除成功,再断开信号并清理映射。 +// 删除井图元前,先校验可删除性并解除绑定,再由数据层销毁井对象。 bool nmPlotGraphicBinder::removeWellBinding(nmObjPointWell* pGraphic) { if (!pGraphic) { @@ -1627,15 +1627,24 @@ bool nmPlotGraphicBinder::removeWellBinding(nmObjPointWell* pGraphic) } nmDataWellBase* pData = m_mapWellData.value(pGraphic, nullptr); - if (pData) { - if (!m_pDataMgr || !m_pDataMgr->removeWell(pData)) { - // 主井等被数据层拒绝的情况必须完整保留现有图元绑定。 - return false; - } + if (!pData) { + _disconnectWell(pGraphic); + m_mapWellData.remove(pGraphic); + return true; } + // removeWell()成功后会立即销毁数据对象,因此必须先解除全部连接和映射。 + if (!m_pDataMgr || !m_pDataMgr->canRemoveWell(pData)) { + return false; + } _disconnectWell(pGraphic); m_mapWellData.remove(pGraphic); + if (!m_pDataMgr->removeWell(pData)) { + // 删除未发生时恢复完整绑定,避免图元与数据状态分叉。 + m_mapWellData.insert(pGraphic, pData); + _connectWell(pGraphic, pData); + return false; + } return true; } diff --git a/Src/nmNum/nmSubWnd/nmSubWndMain.cpp b/Src/nmNum/nmSubWnd/nmSubWndMain.cpp index 48c3e9c6..65487f02 100644 --- a/Src/nmNum/nmSubWnd/nmSubWndMain.cpp +++ b/Src/nmNum/nmSubWnd/nmSubWndMain.cpp @@ -1161,16 +1161,27 @@ void nmSubWndMain::updateSelectedWells(QList wellObjList) // setp 4,执行删除操作 for(int i = 0; i < vDeleteWellPlotList.count(); ++i) { nmObjPointWell* pWellPlot = vDeleteWellPlotList[i]; + if(pWellPlot == nullptr) { + continue; + } // 选择井列表只能移除普通数值井;固定主井始终保留在本次分析中。 if(m_pWxPlot->graphicBinder() && m_pWxPlot->graphicBinder()->isPrimaryWellGraphic(pWellPlot)) continue; - // 删除对应的参数类 - pWellPlot->removeData(); + // 数据删除失败时保留图元,避免画布和DataManager出现半套状态。 + if(!pWellPlot->removeBoundData()) { + continue; + } - // 实际从画布上删除图元 - m_pWxPlot->m_pPlot->removeObjByName(pWellPlot->getName()); + // 按准确对象删除,不能按井名删除,否则同名井可能移除错误图元。 + const int nObjCount = m_pWxPlot->m_pPlot->getObjCount(); + for(int nObjIndex = 0; nObjIndex < nObjCount; ++nObjIndex) { + if(m_pWxPlot->m_pPlot->getObjByIndex(nObjIndex) == pWellPlot) { + m_pWxPlot->m_pPlot->removeObjByIndex(nObjIndex); + break; + } + } } // setp 5,将vAddWellPlotList中的井数据,构建图元,添加到画布上 diff --git a/Src/nmNum/nmSubWnd/nmSubWndUtils.cpp b/Src/nmNum/nmSubWnd/nmSubWndUtils.cpp index cd7e1f19..da71e834 100644 --- a/Src/nmNum/nmSubWnd/nmSubWndUtils.cpp +++ b/Src/nmNum/nmSubWnd/nmSubWndUtils.cpp @@ -118,10 +118,40 @@ nmSubWndUtils::nmSubWndUtils() nmSubWndUtils::~nmSubWndUtils() { + setObservedTabWidget(nullptr); delete m_pNumericalSaveSession; m_pNumericalSaveSession = nullptr; } +ZxTabWidget* nmSubWndUtils::getTabWidget() const +{ + return m_pTabWx.data(); +} + +void nmSubWndUtils::setObservedTabWidget(ZxTabWidget* pTabWx) +{ + ZxTabWidget* pOldTabWx = m_pTabWx.data(); + if(pOldTabWx == pTabWx) { + return; + } + + if(pOldTabWx != nullptr) { + disconnect(pOldTabWx, SIGNAL(currentChanged(int)), + this, SLOT(slotHandleTabChange(int))); + disconnect(pOldTabWx, SIGNAL(destroyed(QObject*)), + this, SLOT(slotHandleTabWidgetDestroyed(QObject*))); + } + + m_pTabWx = pTabWx; + if(pTabWx != nullptr) { + connect(pTabWx, SIGNAL(currentChanged(int)), + this, SLOT(slotHandleTabChange(int)), Qt::UniqueConnection); + connect(pTabWx, SIGNAL(destroyed(QObject*)), + this, SLOT(slotHandleTabWidgetDestroyed(QObject*)), + Qt::UniqueConnection); + } +} + iSubWnd* nmSubWndUtils::createSubWnd(const iRibbonXmlCmd* pCmdInfo, \ const ZxMainWindow* pMainWnd, \ bool& bCreatedNewOne) @@ -288,11 +318,7 @@ bool nmSubWndUtils::runCmdBySpecial(const iRibbonXmlCmd* pCmdInfo, \ 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))); - } + pUtilsInstance->setObservedTabWidget(pTabWx); } } @@ -976,11 +1002,7 @@ bool nmSubWndUtils::loadRsts(iSubWnd* pSubWnd, \ 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))); - } + pUtilsInstance->setObservedTabWidget(pTabWx); } } @@ -1180,13 +1202,15 @@ bool iWxBase::loadFromByteArr(QByteArray& v) void nmSubWndUtils::slotHandleTabChange(int index) { - // 找到索引对应的流动段分析窗体 - if(m_pTabWx == nullptr) { + // 只处理当前监听容器的信号。旧容器的排队信号不能操作新成果页签。 + ZxTabWidget* pSignalTabWx = qobject_cast(sender()); + QPointer pTabWx = m_pTabWx; + if(pTabWx.isNull() || pSignalTabWx != pTabWx.data() || index < 0) { return; } - if(index >= 0 && index < m_pTabWx->count()) { - iSubWnd* pSubWnd = dynamic_cast(m_pTabWx->widget(index)); + if(index < pTabWx->count()) { + iSubWnd* pSubWnd = dynamic_cast(pTabWx->widget(index)); if(pSubWnd == nullptr) { return; @@ -1213,7 +1237,7 @@ void nmSubWndUtils::slotHandleTabChange(int index) } // 如果地图和网格窗体存在,则更新 - QVector vecSubWnds = pMainWnd->getAllSubWndsOf(m_pTabWx); + QVector vecSubWnds = pMainWnd->getAllSubWndsOf(pTabWx.data()); foreach(iSubWnd* pSub, vecSubWnds) { nmSubWndMain* pMap = dynamic_cast(pSub); @@ -1247,6 +1271,12 @@ void nmSubWndUtils::slotHandleTabChange(int index) } } +void nmSubWndUtils::slotHandleTabWidgetDestroyed(QObject* pObject) +{ + Q_UNUSED(pObject); + m_pTabWx = nullptr; +} + // 监听外层MDI成果窗口切换 void nmSubWndUtils::connectMdiActivation(iSubWndFitting* pSubWndF) { @@ -1382,6 +1412,11 @@ void nmSubWndUtils::slotHandleMdiClosing(bool& bAllowClose) // 用户确认关闭成果窗口后再清理Map,避免取消关闭时误清当前显示。 ZxMdiSubWindow* pMdiChild = qobject_cast(sender()); if(pMdiChild != nullptr) { + if(m_pMainWnd != nullptr && + m_pMainWnd->getTabWxOf(pMdiChild) == m_pTabWx.data()) { + // 页签内部对象开始析构前停止监听,避免析构期currentChanged进入槽函数。 + setObservedTabWidget(nullptr); + } detachMdiFittingPlots(pMdiChild); } } @@ -1395,6 +1430,11 @@ void nmSubWndUtils::slotHandleMdiDestroyed(QObject* pObject) // 切换外层MDI成果窗口后,同步当前流动段分析窗口 void nmSubWndUtils::slotHandleMdiSubWindowActivated(QMdiSubWindow* pSubWindow) { + if(pSubWindow == nullptr) { + setObservedTabWidget(nullptr); + return; + } + ZxMdiSubWindow* pMdiChild = qobject_cast(pSubWindow); if(pMdiChild == nullptr || m_pMainWnd == nullptr) { return; @@ -1407,14 +1447,11 @@ void nmSubWndUtils::slotHandleMdiSubWindowActivated(QMdiSubWindow* pSubWindow) // 每个成果窗口内部都有自己的页签容器。 // 切换外层成果后,保存当前容器并继续监听该成果内部的页签切换。 - m_pTabWx = pTabWx; - connect(m_pTabWx, SIGNAL(currentChanged(int)), - this, SLOT(slotHandleTabChange(int)), - Qt::UniqueConnection); + setObservedTabWidget(pTabWx); // 找到当前成果中的流动段分析窗口。 // 窗口ID为3004,后续的数据中心和左侧面板都属于这个分析窗口。 - QVector vecSubWnds = m_pMainWnd->getAllSubWndsOf(m_pTabWx, "3004", false); + QVector vecSubWnds = m_pMainWnd->getAllSubWndsOf(pTabWx, "3004", false); if(vecSubWnds.isEmpty()) { return; }