diff --git a/Include/nmNum/nmData/nmAttrRegistry.h b/Include/nmNum/nmData/nmAttrRegistry.h new file mode 100644 index 0000000..ed636a2 --- /dev/null +++ b/Include/nmNum/nmData/nmAttrRegistry.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "nmData_global.h" + +class nmDataAttribute; + +/// @brief 属性注册表,作为 UI 与数据层的双向同步中介 +/// @note 每个分析窗口(nmDataAnalyzeManager)持有一个实例 +/// name 对齐 XML ParaItem Name(如 "Pi", "K", "h") +/// nmDataReservoir/nmSubWndUtils 不需任何修改 +class NM_DATA_EXPORT nmAttrRegistry : public QObject +{ + Q_OBJECT + +public: + + explicit nmAttrRegistry(QObject* parent = nullptr); + ~nmAttrRegistry(); + + /// @brief 注册属性,name 对齐 XML ParaItem Name + /// @param name 参数名(如 "Pi", "K", "h") + /// @param pAttr nmDataAttribute 指针(不拥有所有权) + void regAttr(QString name, nmDataAttribute* pAttr); + + /// @brief 清除所有注册(重建数据对象时调用) + void clear(); + + /// @brief 批量读取属性值(初始化面板用) + /// @param names 参数名列表 + /// @return name→value 映射,未找到的跳过 + QMap collect(const QStringList& names) const; + +public slots: + + /// @brief UI→Data 方向:按 name dispatch 到 nmDataAttribute::setValue + /// @note 内部设置 m_bSyncing=true 防循环 + void setValue(QString name, QVariant value); + +signals: + + /// @brief Data→UI 方向:属性变更通知,携带 name 和新值 + void sigAttrChanged(QString name, QVariant value); + +private slots: + + /// @brief 监听 nmDataAttribute::sigValueChanged,用 sender() 反查 name + void onAttrValueChanged(); + + /// @brief 监听 nmDataAttribute::destroyed,自动清理映射 + void onAttrDestroyed(QObject* pObj); + +private: + + QMap m_nameToPtr; // name → ptr + QMap m_ptrToName; // ptr → name(sender 反查用) + bool m_bSyncing; // 防循环标记 +}; diff --git a/Include/nmNum/nmData/nmDataAnalyzeManager.h b/Include/nmNum/nmData/nmDataAnalyzeManager.h index 00e08b7..41bff42 100644 --- a/Include/nmNum/nmData/nmDataAnalyzeManager.h +++ b/Include/nmNum/nmData/nmDataAnalyzeManager.h @@ -86,6 +86,7 @@ class ZxDataWell; class iSubWndFitting; class nmDataTimeStepSetting; class nmDataSensitive; +class nmAttrRegistry; class NM_DATA_EXPORT nmDataAnalyzeManager : public ZxDataObjectBin { @@ -541,6 +542,9 @@ class NM_DATA_EXPORT nmDataAnalyzeManager : public ZxDataObjectBin void setLicensePath(const QString& licensePath); QString getLicensePath() const; + // 获取属性注册表 + nmAttrRegistry* getAttrRegistry() const; + signals: void dataChanged(); @@ -641,6 +645,9 @@ class NM_DATA_EXPORT nmDataAnalyzeManager : public ZxDataObjectBin // 时间步设置数据 nmDataTimeStepSetting* m_pTimeStep; + // 属性注册表(普通指针,手动管理,析构函数末尾 delete) + nmAttrRegistry* m_pAttrRegistry; + // 许可证路径 QString m_licensePath; diff --git a/Include/nmNum/nmGUI/nmGridRowUtils.h b/Include/nmNum/nmGUI/nmGridRowUtils.h index 3cb455c..96e32e7 100644 --- a/Include/nmNum/nmGUI/nmGridRowUtils.h +++ b/Include/nmNum/nmGUI/nmGridRowUtils.h @@ -141,14 +141,20 @@ protected: public slots: - /// @brief 参数值改变前的预览回调 - /// @param o1 主要数据(m_oRawValue) - /// @param o2 辅助数据(如下拉填充内容) - virtual void slotValuePrevChanged(QVariant& o1, QVariant& o2) override; - /// @brief 获取指定参数的当前值(供子项内部查询用) /// @param sPara 参数名称 /// @param o 输出值 /// @param bOk 是否成功获取 virtual void slotGetValueOf(QString sPara, QVariant& o, bool& bOk) override; + + /// @brief 激活项切换回调(用户离开当前编辑项时触发) + /// @note 在此时同步上一个编辑项的最终值,避免中间态非法值污染数据层 + virtual void onActiveItemChanged(iGridRowItem* p) override; + +signals: + + /// @brief 参数值变更信号(简化版,供 nmAttrRegistry 同步用) + /// @param name 参数名(来自当前激活项的 Name) + /// @param value 变更后的值 + void sigParaChanged(QString name, QVariant value); }; diff --git a/Include/nmNum/nmSubWxs/nmWxParaPropertyPebi.h b/Include/nmNum/nmSubWxs/nmWxParaPropertyPebi.h index 5ef7ac7..51719a3 100644 --- a/Include/nmNum/nmSubWxs/nmWxParaPropertyPebi.h +++ b/Include/nmNum/nmSubWxs/nmWxParaPropertyPebi.h @@ -55,6 +55,10 @@ public slots: // help msg virtual void slotHelpMsg(QString& s); + /// @brief 设置参数值的槽(供 nmAttrRegistry::sigAttrChanged 连接用) + /// @note setParaValue 是 virtual 函数在 public 区,不能直接作为 SLOT + void slotSetParaValue(QString sPara, QVariant o); + protected: virtual void paintEvent(QPaintEvent*); @@ -70,4 +74,11 @@ protected: // Help QTextEdit* m_pHelpBox; + +signals: + + /// @brief 参数值变更信号(转发 nmGridRowUtils::sigParaChanged) + /// @param name 参数名 + /// @param value 变更后的值 + void sigParaChanged(QString name, QVariant value); }; diff --git a/Src/nmNum/nmData/nmAttrRegistry.cpp b/Src/nmNum/nmData/nmAttrRegistry.cpp new file mode 100644 index 0000000..7463ed9 --- /dev/null +++ b/Src/nmNum/nmData/nmAttrRegistry.cpp @@ -0,0 +1,125 @@ +#include "nmAttrRegistry.h" +#include "nmDataAttribute.h" + +nmAttrRegistry::nmAttrRegistry(QObject* parent) + : QObject(parent) + , m_bSyncing(false) +{ +} + +nmAttrRegistry::~nmAttrRegistry() +{ + // 映射中的指针不拥有所有权,不需 delete + // nmDataAttribute 的 destroyed 信号已自动清理映射 + m_nameToPtr.clear(); + m_ptrToName.clear(); +} + +void nmAttrRegistry::regAttr(QString name, nmDataAttribute* pAttr) +{ + if (name.isEmpty() || pAttr == nullptr) { + return; + } + + // 如果同名已注册,先断开旧连接 + if (m_nameToPtr.contains(name)) { + nmDataAttribute* pOld = m_nameToPtr[name]; + if (pOld != nullptr && pOld != pAttr) { + disconnect(pOld, SIGNAL(sigValueChanged()), this, SLOT(onAttrValueChanged())); + disconnect(pOld, SIGNAL(destroyed(QObject*)), this, SLOT(onAttrDestroyed(QObject*))); + m_ptrToName.remove(pOld); + } + } + + // 如果同指针已注册(换名),先清理旧名 + if (m_ptrToName.contains(pAttr)) { + QString oldName = m_ptrToName[pAttr]; + if (oldName != name) { + m_nameToPtr.remove(oldName); + } + } + + m_nameToPtr[name] = pAttr; + m_ptrToName[pAttr] = name; + + // 连接 nmDataAttribute 的信号 + connect(pAttr, SIGNAL(sigValueChanged()), + this, SLOT(onAttrValueChanged())); + connect(pAttr, SIGNAL(destroyed(QObject*)), + this, SLOT(onAttrDestroyed(QObject*))); +} + +void nmAttrRegistry::clear() +{ + // 断开所有 nmDataAttribute 的信号连接 + QMap::iterator iter = m_ptrToName.begin(); + for (; iter != m_ptrToName.end(); iter++) { + nmDataAttribute* pAttr = iter.key(); + if (pAttr != nullptr) { + disconnect(pAttr, SIGNAL(sigValueChanged()), this, SLOT(onAttrValueChanged())); + disconnect(pAttr, SIGNAL(destroyed(QObject*)), this, SLOT(onAttrDestroyed(QObject*))); + } + } + + m_nameToPtr.clear(); + m_ptrToName.clear(); +} + +QMap nmAttrRegistry::collect(const QStringList& names) const +{ + QMap mapResult; + for (int i = 0; i < names.size(); i++) { + QString name = names[i]; + nmDataAttribute* pAttr = m_nameToPtr.value(name, nullptr); + if (pAttr != nullptr) { + mapResult[name] = pAttr->getValue(); + } + } + return mapResult; +} + +void nmAttrRegistry::setValue(QString name, QVariant value) +{ + nmDataAttribute* pAttr = m_nameToPtr.value(name, nullptr); + if (pAttr == nullptr) { + return; + } + + m_bSyncing = true; + pAttr->setValue(value); // → nmDataAttribute 发 sigValueChanged + // → onAttrValueChanged 检测 m_bSyncing=true → 跳过 sigAttrChanged + m_bSyncing = false; +} + +void nmAttrRegistry::onAttrValueChanged() +{ + nmDataAttribute* pAttr = qobject_cast(sender()); + if (pAttr == nullptr) { + return; + } + + // 防循环:UI→Data 链路中跳过 + if (m_bSyncing) { + return; + } + + QString name = m_ptrToName.value(pAttr, ""); + if (name.isEmpty()) { + return; + } + + emit sigAttrChanged(name, pAttr->getValue()); +} + +void nmAttrRegistry::onAttrDestroyed(QObject* pObj) +{ + // nmDataAttribute 析构时发出 destroyed,此时对象已部分销毁 + // 只用指针值做 key,不访问任何成员 + nmDataAttribute* pAttr = static_cast(pObj); + + QString name = m_ptrToName.value(pAttr, ""); + if (!name.isEmpty()) { + m_nameToPtr.remove(name); + } + m_ptrToName.remove(pAttr); +} diff --git a/Src/nmNum/nmData/nmDataAnalyzeManager.cpp b/Src/nmNum/nmData/nmDataAnalyzeManager.cpp index 19ca06f..7ffadb6 100644 --- a/Src/nmNum/nmData/nmDataAnalyzeManager.cpp +++ b/Src/nmNum/nmData/nmDataAnalyzeManager.cpp @@ -1,5 +1,7 @@ #include "nmDataAnalyzeManager.h" +#include "nmAttrRegistry.h" + #include "nmDataAnalyzeContext.h" #include "nmDataAnalyzeContextProvider.h" #include "nmDataPlotContext.h" @@ -390,6 +392,10 @@ ZX_DEFINE_DYNAMIC(DataAnalyzeManager, nmDataAnalyzeManager) //m_pFlowSegmentData = nullptr; m_pForecastData = nullptr; m_pSensitiveData = nullptr; + + // 属性注册表 + m_pAttrRegistry = new nmAttrRegistry(); + m_backgroundImageInfo.bIsVisible = false; m_dScalarRangeP[0] = 0.0; m_dScalarRangeP[1] = 0.0; @@ -476,6 +482,9 @@ nmDataAnalyzeManager::~nmDataAnalyzeManager() delete m_pTimeStep; m_pTimeStep = nullptr; + // 最后 delete 属性注册表(确保数据对象先销毁,nmAttrRegistry 能接收 destroyed 信号清理映射) + delete m_pAttrRegistry; + m_pAttrRegistry = nullptr; } QMap nmDataAnalyzeManager::s_mapDataAnalManager; @@ -1289,6 +1298,17 @@ void nmDataAnalyzeManager::createReservoir() } m_reservoirData = new nmDataReservoir; + // 注册储层属性到 nmAttrRegistry(name 对齐 XML ParaItem Name) + m_pAttrRegistry->clear(); + m_pAttrRegistry->regAttr("h", &m_reservoirData->getThickness()); + m_pAttrRegistry->regAttr("Pi", &m_reservoirData->getInitialPressure()); + m_pAttrRegistry->regAttr("K", &m_reservoirData->getPermeability()); + m_pAttrRegistry->regAttr("phi", &m_reservoirData->getPorosity()); + m_pAttrRegistry->regAttr("Cti", &m_reservoirData->getCt()); + m_pAttrRegistry->regAttr("Cf", &m_reservoirData->getCf()); + m_pAttrRegistry->regAttr("Soi", &m_reservoirData->getSoi()); + m_pAttrRegistry->regAttr("Swi", &m_reservoirData->getSwi()); + // 按相态填充PVT字段和压缩系数 populateReservoirByPhase(m_reservoirData, eType, mapPvtValues, dCf); @@ -1319,6 +1339,11 @@ nmDataReservoir* nmDataAnalyzeManager::getReservoirData() const return m_reservoirData; } +nmAttrRegistry* nmDataAnalyzeManager::getAttrRegistry() const +{ + return m_pAttrRegistry; +} + nmDataReservoir nmDataAnalyzeManager::getReservoirDataCopy() const { if(m_reservoirData) { diff --git a/Src/nmNum/nmGUI/nmGridRowUtils.cpp b/Src/nmNum/nmGUI/nmGridRowUtils.cpp index 6bc9291..c4a6da7 100644 --- a/Src/nmNum/nmGUI/nmGridRowUtils.cpp +++ b/Src/nmNum/nmGUI/nmGridRowUtils.cpp @@ -141,9 +141,15 @@ void nmGridRowUtils::bindItems() // 槽函数 // ================================================================ -void nmGridRowUtils::slotValuePrevChanged(QVariant& o1, QVariant& o2) +void nmGridRowUtils::onActiveItemChanged(iGridRowItem* p) { - iGridRowUtils::slotValuePrevChanged(o1, o2); + // 基类调用前,m_pItemSel 仍是上一个编辑项 + // 在此时同步其最终值,避免中间态非法值污染数据层 + if (m_pItemSel && m_pItemSel != p) { + emit sigParaChanged(m_pItemSel->getName(), m_pItemSel->getCurValue()); + } + + iGridRowUtils::onActiveItemChanged(p); } void nmGridRowUtils::slotGetValueOf(QString sPara, QVariant& o, bool& bOk) diff --git a/Src/nmNum/nmSubWnd/nmSubWndUtils.cpp b/Src/nmNum/nmSubWnd/nmSubWndUtils.cpp index afc47ef..ba4d85b 100644 --- a/Src/nmNum/nmSubWnd/nmSubWndUtils.cpp +++ b/Src/nmNum/nmSubWnd/nmSubWndUtils.cpp @@ -39,7 +39,7 @@ #include "ZxObjBase.h" #include "nmDataAnalyzeManager.h" -#include "nmDataReservoir.h" +#include "nmAttrRegistry.h" #include "iRibbonXmlTab.h" #include "nmWxParameterProperty.h" #include "nmWxParaPropertyPebi.h" @@ -592,22 +592,24 @@ bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd) pParaWx->refreshUIs(listParas); - // 从数据中心批量读取储层参数初始化UI - nmDataReservoir* pRes = pDataManager->getReservoirData(); - if (pRes) + // 通过 nmAttrRegistry 批量读取储层参数初始化UI(替代逐个 getter 调用) + nmAttrRegistry* pReg = pDataManager->getAttrRegistry(); + if (pReg != nullptr) { - QMap mapInit; - mapInit["h"] = pRes->getThickness().getValue(); - mapInit["Pi"] = pRes->getInitialPressure().getValue(); - mapInit["K"] = pRes->getPermeability().getValue(); - mapInit["phi"] = pRes->getPorosity().getValue(); - mapInit["Cti"] = pRes->getCt().getValue(); - mapInit["Cf"] = pRes->getCf().getValue(); - mapInit["Soi"] = pRes->getSoi().getValue(); - mapInit["Swi"] = pRes->getSwi().getValue(); + QMap mapInit = pReg->collect(listParas); pParaWx->restoreOldParas(mapInit); } + // 双向同步连接:UI→Data + if (pReg != nullptr) + { + connect(pParaWx, SIGNAL(sigParaChanged(QString, QVariant)), + pReg, SLOT(setValue(QString, QVariant))); + // Data→UI + connect(pReg, SIGNAL(sigAttrChanged(QString, QVariant)), + pParaWx, SLOT(slotSetParaValue(QString, QVariant))); + } + // 建立求解调用连接 connect(pAnalWx, SIGNAL(sigGenerateClicked()), pSubWndMain, SLOT(onGenerateButtonClicked())); diff --git a/Src/nmNum/nmSubWxs/nmWxParaPropertyPebi.cpp b/Src/nmNum/nmSubWxs/nmWxParaPropertyPebi.cpp index 822b63e..4c6bfad 100644 --- a/Src/nmNum/nmSubWxs/nmWxParaPropertyPebi.cpp +++ b/Src/nmNum/nmSubWxs/nmWxParaPropertyPebi.cpp @@ -110,6 +110,9 @@ void nmWxParaPropertyPebi::refreshUIs(QStringList& listParas, bool bReserveOlds m_pGridItemUtils->setParaSrcTag(s_Nm_Serie); connect(m_pGridItemUtils, SIGNAL(sigHelpMsg(QString&)), this, SLOT(slotHelpMsg(QString&))); + // 信号转发:nmGridRowUtils::sigParaChanged → nmWxParaPropertyPebi::sigParaChanged + connect(m_pGridItemUtils, SIGNAL(sigParaChanged(QString, QVariant)), + this, SIGNAL(sigParaChanged(QString, QVariant))); m_pMainLayout->addWidget(m_pGridItemUtils); m_pGridItemUtils->buildRowUtils(listParas, false, this); @@ -170,6 +173,11 @@ bool nmWxParaPropertyPebi::setParaValue(QString sPara, QVariant o) return false; } +void nmWxParaPropertyPebi::slotSetParaValue(QString sPara, QVariant o) +{ + setParaValue(sPara, o); +} + bool nmWxParaPropertyPebi::getParaValue(QString sPara, QVariant& o) { if (nullptr != m_pGridItemUtils)