fix(numerical): 修复切换分析时网格窗口绑定旧数据问题

- 统一切换网格窗口的 DataManager
- 丢弃旧分析的排队信号和过期网格任务结果
- 切换时更新网格参数并清理旧 VTK 视图
feature/refactor-numerical-model-panel-20260824
lh 2 weeks ago
parent 1c4aa7c447
commit 25603b4471

@ -25,6 +25,9 @@ class NM_SUB_WND_EXPORT nmSubWndGrid : public iSubWndBaseFit {
nmDataAnalyzeManager* pDataManager = nullptr);
~nmSubWndGrid();
/** @brief 切换窗口所属数据管理器,并安全处理仍在运行的旧网格任务。 */
void setDataManager(nmDataAnalyzeManager* pDataManager);
// 配置窗体
virtual void configWnd(QString sPrevInfo);
@ -38,6 +41,8 @@ class NM_SUB_WND_EXPORT nmSubWndGrid : public iSubWndBaseFit {
virtual bool onConfirmClosing();
public slots:
/** @brief 只响应当前数据管理器的数据变化,忽略切换前已排队的旧信号。 */
void onDataManagerChanged();
/** @brief 合并连续数据变化请求,并启动本窗口的防抖计时。 */
void updateGrid();
/** @brief 防抖结束后启动或排队一次后台 PEBI 网格任务。 */
@ -61,6 +66,8 @@ class NM_SUB_WND_EXPORT nmSubWndGrid : public iSubWndBaseFit {
void showGridResult();
/** @brief 网格失败时恢复旧网格;没有旧网格则显示明确失败提示。 */
void showGridFailure();
/** @brief 销毁当前 VTK 页,防止切换分析后继续显示旧分析网格。 */
void clearGridResultView();
QStackedLayout* m_mainLayout; ///< 在加载背景和 VTK 结果之间切换的主布局。
nmWxGridVTKContainerWidget* m_vtkWidget; ///< 显示所属分析网格的 VTK 控件。
@ -68,6 +75,7 @@ class NM_SUB_WND_EXPORT nmSubWndGrid : public iSubWndBaseFit {
QTimer* m_pDebounceTimer; ///< 本窗口独立防抖定时器,避免跨窗口刷新。
QPointer<nmDataAnalyzeManager> m_pDataManager; ///< 窗口所属数据管理器,销毁后自动置空。
QPointer<nmCalculationPebiGridTask> m_pGridTask; ///< 当前后台网格任务,完成后自动回收。
QPointer<nmDataAnalyzeManager> m_pGridTaskDataManager; ///< 当前网格任务创建时绑定的数据管理器。
bool m_bGridUpdatePending; ///< 任务运行期间是否又收到网格更新请求。
// PEBI GridControl控制参数对应HX_NWTM_GRID_INPUT::GridControl

@ -64,14 +64,16 @@ nmSubWndGrid::nmSubWndGrid(QWidget *parent,
m_vtkWidget(nullptr),
m_pGridLoadingLabel(nullptr),
m_pDebounceTimer(nullptr),
m_pDataManager(pDataManager),
m_pDataManager(nullptr),
m_pGridTask(nullptr),
m_pGridTaskDataManager(nullptr),
m_bGridUpdatePending(false),
m_dPebiGridControl(pebiGridControl),
m_bGeneratePebiGrid(bGeneratePebiGrid)
{
m_sMdiWndType = s_MdiType_DataAnal;
setWindowTitle(tr("nmSubWndGrid"));
setDataManager(pDataManager);
}
@ -84,6 +86,65 @@ nmSubWndGrid::~nmSubWndGrid()
}
}
void nmSubWndGrid::setDataManager(nmDataAnalyzeManager* pDataManager)
{
nmDataAnalyzeManager* pOldDataManager = m_pDataManager.data();
if(pOldDataManager == pDataManager) {
return;
}
// 第一步:只解除旧分析与本窗口的连接,避免切换多次后旧分析继续触发重建。
if(pOldDataManager != nullptr) {
disconnect(pOldDataManager, SIGNAL(dataChanged()),
this, SLOT(onDataManagerChanged()));
}
m_pDataManager = pDataManager;
if(pDataManager != nullptr) {
connect(pDataManager, SIGNAL(dataChanged()),
this, SLOT(onDataManagerChanged()),
static_cast<Qt::ConnectionType>(
Qt::QueuedConnection | Qt::UniqueConnection));
}
// 构造阶段保留对话框传入的控制参数;窗口创建完成后的分析切换使用新分析自己的值。
if(m_mainLayout != nullptr && pDataManager != nullptr) {
m_dPebiGridControl = pDataManager->getPebiGridControl();
}
if(m_pDebounceTimer != nullptr) {
m_pDebounceTimer->stop();
}
// 第二步:旧 VTK 页不能作为新分析的失败回退页,否则会继续显示上一分析的网格。
clearGridResultView();
if(m_mainLayout == nullptr) {
return;
}
if(pDataManager == nullptr) {
m_bGridUpdatePending = false;
showGridFailure();
return;
}
if(pDataManager->isPebiGridValid()) {
m_bGridUpdatePending = false;
showGridResult();
return;
}
showGridLoading();
if(!m_pGridTask.isNull()) {
// 外部 DLL 不能安全中止;旧任务自然结束后只补做当前分析的最后一次更新。
m_bGridUpdatePending = true;
return;
}
m_bGridUpdatePending = false;
updateGrid();
}
void nmSubWndGrid::configWnd(QString sPrevInfo)
{
iSubWndBaseFit::configWnd(sPrevInfo);
@ -204,6 +265,15 @@ bool nmSubWndGrid::generatePebiGrid()
return true;
}
void nmSubWndGrid::onDataManagerChanged()
{
// 旧分析在断开前可能已经把信号排入主线程队列,发送者不匹配时不得刷新当前分析。
if(sender() != m_pDataManager.data()) {
return;
}
updateGrid();
}
void nmSubWndGrid::updateGrid()
{
// 角色、产量等只影响求解的变化也会发送 dataChanged网格仍有效时无需重划。
@ -226,8 +296,8 @@ void nmSubWndGrid::updateGrid()
void nmSubWndGrid::onDebounceTimeout()
{
// 第一步:同一窗口只允许一个网格线程运行,连续编辑合并为下一次重建
if(!m_pGridTask.isNull() && m_pGridTask->isRunning()) {
// 第一步:任务结束信号回到主线程前仍视为占用,避免覆盖尚未处理的任务指针
if(!m_pGridTask.isNull()) {
m_bGridUpdatePending = true;
return;
}
@ -240,6 +310,7 @@ void nmSubWndGrid::onDebounceTimeout()
// 第二步:把窗口参数写回所属分析方案,再启动绑定同一管理器的后台任务。
nmCalculationPebiGrid::getInstance()->setGridControl(
m_dPebiGridControl, pDataManager);
m_pGridTaskDataManager = pDataManager;
m_pGridTask = new nmCalculationPebiGridTask(pDataManager, this);
connect(m_pGridTask, SIGNAL(pebiGridGenerated(bool)),
this, SLOT(onPebiGridGenerated(bool)), Qt::QueuedConnection);
@ -256,9 +327,18 @@ void nmSubWndGrid::onPebiGridGenerated(bool success)
// 局部结果如果用户在划分期间修改了输入commitResult() 会按版本拒绝。
nmCalculationPebiGridTask* pCompletedTask =
qobject_cast<nmCalculationPebiGridTask*>(sender());
nmDataAnalyzeManager* pDataManager = m_pDataManager.data();
if(success && pCompletedTask != nullptr && pDataManager != nullptr) {
success = pCompletedTask->commitResult(pDataManager);
nmDataAnalyzeManager* pTaskDataManager =
m_pGridTaskDataManager.data();
// 只处理窗口当前持有且仍属于当前分析的任务;切换前启动的旧任务直接丢弃。
if(pCompletedTask != m_pGridTask.data() ||
pTaskDataManager == nullptr ||
pTaskDataManager != m_pDataManager.data()) {
return;
}
if(success) {
success = pCompletedTask->commitResult(pTaskDataManager);
} else {
success = false;
}
@ -275,13 +355,22 @@ void nmSubWndGrid::onPebiGridGenerated(bool success)
void nmSubWndGrid::onPebiGridTaskFinished()
{
// 第一步:只清理由当前窗口持有的任务,避免旧 finished 信号覆盖新任务指针。
if(sender() == m_pGridTask.data()) {
m_pGridTask = nullptr;
if(sender() != m_pGridTask.data()) {
return;
}
m_pGridTask = nullptr;
m_pGridTaskDataManager = nullptr;
// 第二步:运行期间若又有数据变化,重新启动防抖计时,只补做最后一次网格。
if(m_bGridUpdatePending) {
m_bGridUpdatePending = false;
nmDataAnalyzeManager* pDataManager = m_pDataManager.data();
if(pDataManager != nullptr &&
pDataManager->isPebiGridValid() &&
m_vtkWidget == nullptr) {
showGridResult();
return;
}
updateGrid();
}
}
@ -304,11 +393,7 @@ void nmSubWndGrid::showGridResult()
// 网格已经在主线程完整提交,此处一次创建新的 VTK 页,不会向用户暴露
// 没有 RenderWindow 的中间状态。
if(m_vtkWidget != nullptr) {
m_mainLayout->removeWidget(m_vtkWidget);
delete m_vtkWidget;
m_vtkWidget = nullptr;
}
clearGridResultView();
m_vtkWidget = new nmWxGridVTKContainerWidget(
m_pDataManager.data(), centralWidget());
@ -318,6 +403,19 @@ void nmSubWndGrid::showGridResult()
m_mainLayout->setCurrentWidget(m_vtkWidget);
}
void nmSubWndGrid::clearGridResultView()
{
if(m_vtkWidget == nullptr) {
return;
}
if(m_mainLayout != nullptr) {
m_mainLayout->removeWidget(m_vtkWidget);
}
delete m_vtkWidget;
m_vtkWidget = nullptr;
}
void nmSubWndGrid::showGridFailure()
{
if(m_mainLayout == nullptr) {

@ -61,9 +61,8 @@ void nmSubWndSubWxsAnalyzeController::updateWindowsForCurrentAnalyze(ZxMainWindo
nmSubWndGrid* pSubWndGrid = dynamic_cast<nmSubWndGrid*>(pSub);
if(nullptr != pSubWndGrid) {
QObject::disconnect(pManager, SIGNAL(dataChanged()), pSubWndGrid, SLOT(updateGrid()));
QObject::connect(pManager, SIGNAL(dataChanged()), pSubWndGrid, SLOT(updateGrid()), Qt::QueuedConnection);
pSubWndGrid->updateGrid();
// 网格窗口负责切换所属数据管理器,并丢弃切换前任务的过期结果。
pSubWndGrid->setDataManager(pManager);
}
}
}

@ -228,10 +228,6 @@ iSubWnd* nmSubWndUtils::makesureSubWnd(QString sID, QString sExt, \
nmSubWndGrid* pSubWndGrid = new nmSubWndGrid(
NULL, sExt, dPebiGridControl, bGeneratePebiGrid, pDataManager);
disconnect(pDataManager, SIGNAL(dataChanged()),
pSubWndGrid, SLOT(updateGrid()));
connect(pDataManager, SIGNAL(dataChanged()),
pSubWndGrid, SLOT(updateGrid()), Qt::QueuedConnection);
pSubWnd = pSubWndGrid;
} else if(nID == 5116) {
// 后处理
@ -1146,15 +1142,12 @@ void nmSubWndUtils::slotHandleTabChange(int index)
pMap->updateMapByDataManager(pDataManger);
}
// TODO网格更新
// 网格窗口跟随当前分析切换数据来源。
nmSubWndGrid* pSubWndGrid = dynamic_cast<nmSubWndGrid*>(pSub);
if(nullptr != pSubWndGrid) {
// 更新网格
disconnect(pDataManger, SIGNAL(dataChanged()), pSubWndGrid, SLOT(updateGrid()));
connect(pDataManger, SIGNAL(dataChanged()), pSubWndGrid, SLOT(updateGrid()), Qt::QueuedConnection);
// 手动更新
pSubWndGrid->updateGrid();
// 由网格窗口统一解除旧连接、绑定新分析并处理旧后台任务。
pSubWndGrid->setDataManager(pDataManger);
}
}
}

Loading…
Cancel
Save