fix(nmNum): 修复井删除及页签销毁时的崩溃

develop
lh 1 week ago
parent 553ae6a488
commit 78d5f934cd

@ -74,6 +74,9 @@ class NM_PLOT_EXPORT nmObjPointWell : public nmObjPoint {
// 删除当前对象里的参数对象 // 删除当前对象里的参数对象
virtual void removeData() override; virtual void removeData() override;
/** @brief 删除当前井对应的数值数据并解除绑定。 */
bool removeBoundData();
void setGraphicBinder(nmPlotGraphicBinder* pBinder) { m_pGraphicBinder = pBinder; } void setGraphicBinder(nmPlotGraphicBinder* pBinder) { m_pGraphicBinder = pBinder; }
nmPlotGraphicBinder* getGraphicBinder() const { return m_pGraphicBinder; } nmPlotGraphicBinder* getGraphicBinder() const { return m_pGraphicBinder; }
// 标记井数据已由Binder绑定,图元只保留井类型 // 标记井数据已由Binder绑定,图元只保留井类型

@ -2,6 +2,7 @@
#include <QMap> #include <QMap>
#include <QObject> #include <QObject>
#include <QPointer>
#include <QSet> #include <QSet>
#include "nmSubWnd_global.h" #include "nmSubWnd_global.h"
@ -134,7 +135,7 @@ public:
const ZxMainWindow* pMainWnd); const ZxMainWindow* pMainWnd);
/// @brief 获取缓存的TabWidget指针,确保弹窗与MDI切换使用同一个容器,指针比较才能正确 /// @brief 获取缓存的TabWidget指针,确保弹窗与MDI切换使用同一个容器,指针比较才能正确
ZxTabWidget* getTabWidget() const { return m_pTabWx; } ZxTabWidget* getTabWidget() const;
/// @brief 建立井列表变化与PEBI参数面板的刷新连接 /// @brief 建立井列表变化与PEBI参数面板的刷新连接
/// @param pDataManager 数据管理器 /// @param pDataManager 数据管理器
@ -146,6 +147,8 @@ public:
// 声明处理 ZxTabWidget 切换的槽函数 // 声明处理 ZxTabWidget 切换的槽函数
// 信号 currentChanged(int) 传递一个 int 参数 // 信号 currentChanged(int) 传递一个 int 参数
void slotHandleTabChange(int index); void slotHandleTabChange(int index);
// 当前监听的页签容器销毁后清除受保护指针
void slotHandleTabWidgetDestroyed(QObject* pObject);
// 外层MDI成果窗口切换后,同步当前流动段分析窗口和左侧数值解面板 // 外层MDI成果窗口切换后,同步当前流动段分析窗口和左侧数值解面板
void slotHandleMdiSubWindowActivated(QMdiSubWindow* pSubWindow); void slotHandleMdiSubWindowActivated(QMdiSubWindow* pSubWindow);
// 关闭成果或流动段分析后,释放对应的数据管理器 // 关闭成果或流动段分析后,释放对应的数据管理器
@ -173,6 +176,8 @@ public:
// 监听外层MDI成果窗口切换,区别于成果窗口内部的页签切换 // 监听外层MDI成果窗口切换,区别于成果窗口内部的页签切换
void connectMdiActivation(iSubWndFitting* pSubWndF); void connectMdiActivation(iSubWndFitting* pSubWndF);
// 统一切换页签监听,确保先解除旧连接再保存新容器
void setObservedTabWidget(ZxTabWidget* pTabWx);
// 单独关闭流动段分析页签时,移除仍引用其数据的画布图元 // 单独关闭流动段分析页签时,移除仍引用其数据的画布图元
void detachFittingPlot(iSubWndFitting* pSubWndF); void detachFittingPlot(iSubWndFitting* pSubWndF);
// 关闭外层成果窗口时,提前解除其Map图元对分析数据的引用 // 关闭外层成果窗口时,提前解除其Map图元对分析数据的引用
@ -185,7 +190,7 @@ public:
// 存储当前操作的是哪一个窗体 // 存储当前操作的是哪一个窗体
ZxMainWindow* m_pMainWnd; ZxMainWindow* m_pMainWnd;
ZxTabWidget* m_pTabWx; QPointer<ZxTabWidget> m_pTabWx;
// destroyed信号异步处理时,通过QObject地址取回析构前保存的分析窗口key // destroyed信号异步处理时,通过QObject地址取回析构前保存的分析窗口key
QMap<QObject*, iSubWndFitting*> m_mapFittingKeys; QMap<QObject*, iSubWndFitting*> m_mapFittingKeys;

@ -495,9 +495,13 @@ void nmObjPointWell::paintBack(QPainter* painter, const ZxPaintParam& param)
void nmObjPointWell::removeData() void nmObjPointWell::removeData()
{ {
if (m_pGraphicBinder) { removeBoundData();
m_pGraphicBinder->removeWellBinding(this);
} }
bool nmObjPointWell::removeBoundData()
{
return m_pGraphicBinder == nullptr ||
m_pGraphicBinder->removeWellBinding(this);
} }
void nmObjPointWell::markWellDataBound(NM_WELL_MODEL wellType) void nmObjPointWell::markWellDataBound(NM_WELL_MODEL wellType)

@ -1619,7 +1619,7 @@ void nmPlotGraphicBinder::updateWellDataMapping(
static_cast<nmObjBase*>(pGraphic)->update(); static_cast<nmObjBase*>(pGraphic)->update();
} }
// 删除井图元前,先由数据层确认删除成功,再断开信号并清理映射。 // 删除井图元前,先校验可删除性并解除绑定,再由数据层销毁井对象。
bool nmPlotGraphicBinder::removeWellBinding(nmObjPointWell* pGraphic) bool nmPlotGraphicBinder::removeWellBinding(nmObjPointWell* pGraphic)
{ {
if (!pGraphic) { if (!pGraphic) {
@ -1627,15 +1627,24 @@ bool nmPlotGraphicBinder::removeWellBinding(nmObjPointWell* pGraphic)
} }
nmDataWellBase* pData = m_mapWellData.value(pGraphic, nullptr); nmDataWellBase* pData = m_mapWellData.value(pGraphic, nullptr);
if (pData) { if (!pData) {
if (!m_pDataMgr || !m_pDataMgr->removeWell(pData)) { _disconnectWell(pGraphic);
// 主井等被数据层拒绝的情况必须完整保留现有图元绑定。 m_mapWellData.remove(pGraphic);
return false; return true;
}
} }
// removeWell()成功后会立即销毁数据对象,因此必须先解除全部连接和映射。
if (!m_pDataMgr || !m_pDataMgr->canRemoveWell(pData)) {
return false;
}
_disconnectWell(pGraphic); _disconnectWell(pGraphic);
m_mapWellData.remove(pGraphic); m_mapWellData.remove(pGraphic);
if (!m_pDataMgr->removeWell(pData)) {
// 删除未发生时恢复完整绑定,避免图元与数据状态分叉。
m_mapWellData.insert(pGraphic, pData);
_connectWell(pGraphic, pData);
return false;
}
return true; return true;
} }

@ -1161,16 +1161,27 @@ void nmSubWndMain::updateSelectedWells(QList<ZxDataWell *> wellObjList)
// setp 4,执行删除操作 // setp 4,执行删除操作
for(int i = 0; i < vDeleteWellPlotList.count(); ++i) { for(int i = 0; i < vDeleteWellPlotList.count(); ++i) {
nmObjPointWell* pWellPlot = vDeleteWellPlotList[i]; nmObjPointWell* pWellPlot = vDeleteWellPlotList[i];
if(pWellPlot == nullptr) {
continue;
}
// 选择井列表只能移除普通数值井;固定主井始终保留在本次分析中。 // 选择井列表只能移除普通数值井;固定主井始终保留在本次分析中。
if(m_pWxPlot->graphicBinder() && if(m_pWxPlot->graphicBinder() &&
m_pWxPlot->graphicBinder()->isPrimaryWellGraphic(pWellPlot)) m_pWxPlot->graphicBinder()->isPrimaryWellGraphic(pWellPlot))
continue; continue;
// 删除对应的参数类 // 数据删除失败时保留图元,避免画布和DataManager出现半套状态。
pWellPlot->removeData(); 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中的井数据,构建图元,添加到画布上 // setp 5,将vAddWellPlotList中的井数据,构建图元,添加到画布上

@ -118,10 +118,40 @@ nmSubWndUtils::nmSubWndUtils()
nmSubWndUtils::~nmSubWndUtils() nmSubWndUtils::~nmSubWndUtils()
{ {
setObservedTabWidget(nullptr);
delete m_pNumericalSaveSession; delete m_pNumericalSaveSession;
m_pNumericalSaveSession = nullptr; 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, \ iSubWnd* nmSubWndUtils::createSubWnd(const iRibbonXmlCmd* pCmdInfo, \
const ZxMainWindow* pMainWnd, \ const ZxMainWindow* pMainWnd, \
bool& bCreatedNewOne) bool& bCreatedNewOne)
@ -288,11 +318,7 @@ bool nmSubWndUtils::runCmdBySpecial(const iRibbonXmlCmd* pCmdInfo, \
ZxTabWidget* pTabWx = pMainWnd1->getCurTabWx(); ZxTabWidget* pTabWx = pMainWnd1->getCurTabWx();
if(nullptr != pTabWx) { if(nullptr != pTabWx) {
if(pUtilsInstance->m_pTabWx == nullptr || pUtilsInstance->m_pTabWx != pTabWx) { pUtilsInstance->setObservedTabWidget(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)));
}
} }
} }
@ -976,11 +1002,7 @@ bool nmSubWndUtils::loadRsts(iSubWnd* pSubWnd, \
ZxTabWidget* pTabWx = pMainWnd1->getCurTabWx(); ZxTabWidget* pTabWx = pMainWnd1->getCurTabWx();
if(nullptr != pTabWx) { if(nullptr != pTabWx) {
if(pUtilsInstance->m_pTabWx == nullptr || pUtilsInstance->m_pTabWx != pTabWx) { pUtilsInstance->setObservedTabWidget(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)));
}
} }
} }
@ -1180,13 +1202,15 @@ bool iWxBase::loadFromByteArr(QByteArray& v)
void nmSubWndUtils::slotHandleTabChange(int index) void nmSubWndUtils::slotHandleTabChange(int index)
{ {
// 找到索引对应的流动段分析窗体 // 只处理当前监听容器的信号。旧容器的排队信号不能操作新成果页签。
if(m_pTabWx == nullptr) { ZxTabWidget* pSignalTabWx = qobject_cast<ZxTabWidget*>(sender());
QPointer<ZxTabWidget> pTabWx = m_pTabWx;
if(pTabWx.isNull() || pSignalTabWx != pTabWx.data() || index < 0) {
return; return;
} }
if(index >= 0 && index < m_pTabWx->count()) { if(index < pTabWx->count()) {
iSubWnd* pSubWnd = dynamic_cast<iSubWnd*>(m_pTabWx->widget(index)); iSubWnd* pSubWnd = dynamic_cast<iSubWnd*>(pTabWx->widget(index));
if(pSubWnd == nullptr) { if(pSubWnd == nullptr) {
return; return;
@ -1213,7 +1237,7 @@ void nmSubWndUtils::slotHandleTabChange(int index)
} }
// 如果地图和网格窗体存在,则更新 // 如果地图和网格窗体存在,则更新
QVector<iSubWnd*> vecSubWnds = pMainWnd->getAllSubWndsOf(m_pTabWx); QVector<iSubWnd*> vecSubWnds = pMainWnd->getAllSubWndsOf(pTabWx.data());
foreach(iSubWnd* pSub, vecSubWnds) { foreach(iSubWnd* pSub, vecSubWnds) {
nmSubWndMain* pMap = dynamic_cast<nmSubWndMain*>(pSub); nmSubWndMain* pMap = dynamic_cast<nmSubWndMain*>(pSub);
@ -1247,6 +1271,12 @@ void nmSubWndUtils::slotHandleTabChange(int index)
} }
} }
void nmSubWndUtils::slotHandleTabWidgetDestroyed(QObject* pObject)
{
Q_UNUSED(pObject);
m_pTabWx = nullptr;
}
// 监听外层MDI成果窗口切换 // 监听外层MDI成果窗口切换
void nmSubWndUtils::connectMdiActivation(iSubWndFitting* pSubWndF) void nmSubWndUtils::connectMdiActivation(iSubWndFitting* pSubWndF)
{ {
@ -1382,6 +1412,11 @@ void nmSubWndUtils::slotHandleMdiClosing(bool& bAllowClose)
// 用户确认关闭成果窗口后再清理Map,避免取消关闭时误清当前显示。 // 用户确认关闭成果窗口后再清理Map,避免取消关闭时误清当前显示。
ZxMdiSubWindow* pMdiChild = qobject_cast<ZxMdiSubWindow*>(sender()); ZxMdiSubWindow* pMdiChild = qobject_cast<ZxMdiSubWindow*>(sender());
if(pMdiChild != nullptr) { if(pMdiChild != nullptr) {
if(m_pMainWnd != nullptr &&
m_pMainWnd->getTabWxOf(pMdiChild) == m_pTabWx.data()) {
// 页签内部对象开始析构前停止监听,避免析构期currentChanged进入槽函数。
setObservedTabWidget(nullptr);
}
detachMdiFittingPlots(pMdiChild); detachMdiFittingPlots(pMdiChild);
} }
} }
@ -1395,6 +1430,11 @@ void nmSubWndUtils::slotHandleMdiDestroyed(QObject* pObject)
// 切换外层MDI成果窗口后,同步当前流动段分析窗口 // 切换外层MDI成果窗口后,同步当前流动段分析窗口
void nmSubWndUtils::slotHandleMdiSubWindowActivated(QMdiSubWindow* pSubWindow) void nmSubWndUtils::slotHandleMdiSubWindowActivated(QMdiSubWindow* pSubWindow)
{ {
if(pSubWindow == nullptr) {
setObservedTabWidget(nullptr);
return;
}
ZxMdiSubWindow* pMdiChild = qobject_cast<ZxMdiSubWindow*>(pSubWindow); ZxMdiSubWindow* pMdiChild = qobject_cast<ZxMdiSubWindow*>(pSubWindow);
if(pMdiChild == nullptr || m_pMainWnd == nullptr) { if(pMdiChild == nullptr || m_pMainWnd == nullptr) {
return; return;
@ -1407,14 +1447,11 @@ void nmSubWndUtils::slotHandleMdiSubWindowActivated(QMdiSubWindow* pSubWindow)
// 每个成果窗口内部都有自己的页签容器。 // 每个成果窗口内部都有自己的页签容器。
// 切换外层成果后,保存当前容器并继续监听该成果内部的页签切换。 // 切换外层成果后,保存当前容器并继续监听该成果内部的页签切换。
m_pTabWx = pTabWx; setObservedTabWidget(pTabWx);
connect(m_pTabWx, SIGNAL(currentChanged(int)),
this, SLOT(slotHandleTabChange(int)),
Qt::UniqueConnection);
// 找到当前成果中的流动段分析窗口。 // 找到当前成果中的流动段分析窗口。
// 窗口ID为3004,后续的数据中心和左侧面板都属于这个分析窗口。 // 窗口ID为3004,后续的数据中心和左侧面板都属于这个分析窗口。
QVector<iSubWnd*> vecSubWnds = m_pMainWnd->getAllSubWndsOf(m_pTabWx, "3004", false); QVector<iSubWnd*> vecSubWnds = m_pMainWnd->getAllSubWndsOf(pTabWx, "3004", false);
if(vecSubWnds.isEmpty()) { if(vecSubWnds.isEmpty()) {
return; return;
} }

Loading…
Cancel
Save