#include "nmWxPebiResultViewWidget.h" #include "nmDataAnalyzeManager.h" #include "nmFFmpegVideoWriter.h" #include "nmPebiResultAnimationExporter.h" #include "nmPebiResultDisplaySettings.h" #include "nmPebiResultPlaybackController.h" #include "nmPebiResultSceneController.h" #include "nmPebiResultSnapshot.h" #include "nmVTKViewCommandController.h" #include "nmWxPebiResultControlBar.h" #include "nmWxPebiResultAnimationExportDlg.h" #include "nmWxVTKRenderContainerWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { QAtomicInt g_nPebiAnimationExportActive(0); QString resultViewIconPath(const QString& sFileName) { const QString sApplicationDir = QCoreApplication::applicationDirPath(); return sApplicationDir.section('/', 0, -2) + "/Res/Icon/" + sFileName; } bool isResultViewGuiThread() { QCoreApplication* pApplication = QCoreApplication::instance(); return pApplication == nullptr || pApplication->thread() == QThread::currentThread(); } void setActionCheckedWithoutSignal(QAction* pAction, bool bChecked) { if(pAction == nullptr) { return; } const bool bBlocked = pAction->blockSignals(true); pAction->setChecked(bChecked); pAction->blockSignals(bBlocked); } class AnimationExportGuard { public: explicit AnimationExportGuard(nmWxPebiResultControlBar* pControlBar) : m_pControlBar(pControlBar), m_bAcquired(g_nPebiAnimationExportActive.testAndSetOrdered(0, 1)) { if(m_bAcquired && m_pControlBar != nullptr) { m_pControlBar->setExportInProgress(true); } } ~AnimationExportGuard() { release(); } void release() { if(!m_bAcquired) { return; } if(m_pControlBar != nullptr) { m_pControlBar->setExportInProgress(false); } g_nPebiAnimationExportActive.fetchAndStoreOrdered(0); m_bAcquired = false; } bool isAcquired() const { return m_bAcquired; } private: QPointer m_pControlBar; bool m_bAcquired; }; } nmWxPebiResultViewWidget::nmWxPebiResultViewWidget( nmDataAnalyzeManager* pDataManager, QWidget* pParent) : QWidget(pParent), m_pRenderContainer(nullptr), m_pSceneController(nullptr), m_pControlBar(nullptr), m_pToolBar(nullptr), m_pStatusLabel(nullptr), m_pScalarBarAction(nullptr), m_pScaleGridAction(nullptr), m_pWellNamesAction(nullptr), m_bObservedSnapshotCurrent(false), m_bObservedStateValid(false), m_bSynchronizing(false), m_bSyncPending(false) { createUi(); setDataManager(pDataManager); } nmWxPebiResultViewWidget::~nmWxPebiResultViewWidget() { disconnectDataManager(); if(m_pControlBar != nullptr) { m_pControlBar->stopPlayback(); delete m_pControlBar; m_pControlBar = nullptr; } if(m_pSceneController != nullptr) { m_pSceneController->clear(); delete m_pSceneController; m_pSceneController = nullptr; } // 渲染容器、工具栏、状态栏和 QAction 最后由 QWidget 对象树统一释放。 } void nmWxPebiResultViewWidget::setDataManager( nmDataAnalyzeManager* pDataManager) { Q_ASSERT(isResultViewGuiThread()); Q_ASSERT(pDataManager == nullptr || pDataManager->thread() == QThread::currentThread()); if(!isResultViewGuiThread() || (pDataManager != nullptr && pDataManager->thread() != QThread::currentThread())) { // 窗口会同步读取不可变快照引用,不能跨线程直接访问 Manager。 return; } if(m_pDataManager.data() == pDataManager) { // 生产入口可能重复安装同一 Manager;这里只读最新状态,不能清空相机。 synchronizeFromDataManager(); return; } disconnectDataManager(); clearSceneAndControls(tr("No result data.")); m_pDataManager = pDataManager; if(!m_pDataManager.isNull()) { connect(m_pDataManager.data(), SIGNAL(sigPebiResultStateChanged()), this, SLOT(onPebiResultStateChanged())); connect(m_pDataManager.data(), SIGNAL(destroyed(QObject*)), this, SLOT(onDataManagerDestroyed(QObject*))); } synchronizeFromDataManager(); } nmDataAnalyzeManager* nmWxPebiResultViewWidget::getDataManager() const { return m_pDataManager.data(); } void nmWxPebiResultViewWidget::synchronizeFromDataManager() { Q_ASSERT(isResultViewGuiThread()); Q_ASSERT(m_pDataManager.isNull() || m_pDataManager->thread() == QThread::currentThread()); if(!isResultViewGuiThread() || (!m_pDataManager.isNull() && m_pDataManager->thread() != QThread::currentThread())) { return; } if(m_bSynchronizing) { // 同步期间只记录一次待处理请求,当前事务结束后重新读取 Manager 最新值。 m_bSyncPending = true; return; } m_bSynchronizing = true; do { m_bSyncPending = false; synchronizeOnce(); } while(m_bSyncPending); m_bSynchronizing = false; } void nmWxPebiResultViewWidget::onPebiResultStateChanged() { synchronizeFromDataManager(); } void nmWxPebiResultViewWidget::onDataManagerDestroyed(QObject* pObject) { Q_UNUSED(pObject); // destroyed() 到达时 QPointer 可能已经清空,槽内绝不再读取 Manager。 m_pDataManager = nullptr; if(m_bSynchronizing) { // 极端嵌套事件下不得在快照提交栈内递归清场,退出当前事务后再读空状态。 m_bSyncPending = true; return; } clearSceneAndControls(tr("No result data.")); // 记录稳定空状态,后续重复同步空 Manager 时不再清场或切换状态页。 rememberObservedState( QSharedPointer(), QString(), false); } void nmWxPebiResultViewWidget::appendContextMenuActions(QMenu* pMenu) { if(pMenu == nullptr) { return; } pMenu->addAction(m_pScalarBarAction); pMenu->addAction(m_pScaleGridAction); pMenu->addAction(m_pWellNamesAction); if(m_pControlBar != nullptr && m_pControlBar->getExportAnimationAction() != nullptr) { pMenu->addSeparator(); pMenu->addAction(m_pControlBar->getExportAnimationAction()); } } void nmWxPebiResultViewWidget::onControlOperationFailed( const QString& sError) { // 高优先级状态存在时丢弃普通错误,避免其在过期提示解除后突然出现。 if(!m_sBindingError.isEmpty() || (m_bObservedStateValid && !m_bObservedSnapshotCurrent)) { return; } m_sOperationError = sError; refreshStatusBar(); } void nmWxPebiResultViewWidget::onControlOperationSucceeded() { if(m_sOperationError.isEmpty()) { return; } // 这里只清除最低优先级的普通操作错误;刷新函数仍优先显示绑定失败和过期。 m_sOperationError.clear(); refreshStatusBar(); } void nmWxPebiResultViewWidget::onViewportSizeChanged( int nWidth, int nHeight) { if(m_pSceneController != nullptr) { // SceneController 缓存尺寸,即使当前尚未创建结果色标也不会丢失。 m_pSceneController->setViewportSize(nWidth, nHeight); } } void nmWxPebiResultViewWidget::onExportAnimationRequested() { nmPebiResultPlaybackController* pPlaybackController = m_pControlBar == nullptr ? nullptr : m_pControlBar->getPlaybackController(); QString sBackendError; if(m_pSceneController == nullptr || m_pRenderContainer == nullptr || pPlaybackController == nullptr || pPlaybackController->isPlaying() || !m_pSceneController->hasSnapshot() || m_pDisplayedSnapshot.isNull() || !nmFFmpegVideoWriter::isBackendAvailable(&sBackendError)) { if(!sBackendError.isEmpty()) { QMessageBox::warning(this, tr("Export Animation"), sBackendError); } return; } AnimationExportGuard oExportGuard(m_pControlBar); if(!oExportGuard.isAcquired()) { QMessageBox::information( this, tr("Export Animation"), tr("Another result animation export is already in progress.")); return; } const QSharedPointer pFrozenSnapshot = m_pDisplayedSnapshot; const quint64 nFrozenBindingRevision = m_pSceneController->getSnapshotBindingRevision(); const QString sFrozenSnapshotId = m_pSceneController->getCurrentSnapshotId(); const QString sFrozenPropertyId = m_pSceneController->getCurrentPropertyId(); const int nFrameCount = pFrozenSnapshot->getPropertyFrameCount( sFrozenPropertyId); if(nFrozenBindingRevision == 0 || sFrozenSnapshotId.isEmpty() || sFrozenPropertyId.isEmpty() || nFrameCount <= 1 || !m_pSceneController->isPropertyTimeDependent(sFrozenPropertyId)) { return; } QVector vecFrameTimes; vecFrameTimes.reserve(nFrameCount); for(int nFrameIndex = 0; nFrameIndex < nFrameCount; ++nFrameIndex) { double dTimeHours = 0.0; if(!pFrozenSnapshot->getPropertyTimeAt( sFrozenPropertyId, nFrameIndex, dTimeHours)) { QMessageBox::warning( this, tr("Export Animation"), tr("Unable to read the result animation timeline.")); return; } vecFrameTimes.append(dTimeHours); } nmWxPebiResultAnimationExportDlg oSettingsDialog( vecFrameTimes, m_pRenderContainer->getViewportSize(), this); if(oSettingsDialog.exec() != QDialog::Accepted) { return; } // 参数窗口内的文件浏览也会形成嵌套事件循环,开始前重新核对完整显示身份。 if(m_pDisplayedSnapshot.data() != pFrozenSnapshot.data() || m_pSceneController->getSnapshotBindingRevision() != nFrozenBindingRevision || m_pSceneController->getCurrentSnapshotId() != sFrozenSnapshotId || m_pSceneController->getCurrentPropertyId() != sFrozenPropertyId) { QMessageBox::warning( this, tr("Export Animation"), tr("The displayed result changed before export started.")); return; } nmPebiResultDisplaySettings oDisplaySettings; vtkRenderer* pRenderer = m_pRenderContainer->getRenderer(); if(!m_pSceneController->getCurrentDisplaySettings(oDisplaySettings) || pRenderer == nullptr || pRenderer->GetActiveCamera() == nullptr) { QMessageBox::warning( this, tr("Export Animation"), tr("Unable to freeze the current result display.")); return; } nmPebiResultAnimationExporter::Request oRequest; oRequest.m_pSnapshot = pFrozenSnapshot; oRequest.m_sPropertyId = sFrozenPropertyId; oRequest.m_oDisplaySettings = oDisplaySettings; oRequest.m_oExportSettings = oSettingsDialog.getSettings(); oRequest.m_sTargetFilePath = oSettingsDialog.getTargetFilePath(); oRequest.m_bScalarBarVisible = m_pSceneController->isScalarBarVisible(); oRequest.m_bScaleGridVisible = m_pSceneController->isScaleGridVisible(); oRequest.m_bWellNamesVisible = m_pSceneController->areWellNamesVisible(); pRenderer->GetBackground(oRequest.m_dBackground); pRenderer->GetBackground2(oRequest.m_dBackground2); oRequest.m_bGradientBackground = pRenderer->GetGradientBackground() != 0; oRequest.m_pCamera = vtkSmartPointer::New(); oRequest.m_pCamera->DeepCopy(pRenderer->GetActiveCamera()); QProgressDialog oProgressDialog( tr("Exporting result animation..."), tr("Cancel"), 0, oRequest.m_oExportSettings.getOutputFrameCount(), this); oProgressDialog.setWindowTitle(tr("Export Animation")); oProgressDialog.setWindowModality(Qt::ApplicationModal); oProgressDialog.setMinimumDuration(0); oProgressDialog.setAutoClose(false); oProgressDialog.setAutoReset(false); nmPebiResultAnimationExporter oExporter; connect(&oExporter, SIGNAL(progressChanged(int,int)), &oProgressDialog, SLOT(setValue(int))); connect(&oProgressDialog, SIGNAL(canceled()), &oExporter, SLOT(cancel())); oProgressDialog.show(); QString sExportError; const nmPebiResultAnimationExporter::ExportResult eResult = oExporter.exportAnimation(oRequest, &sExportError); oProgressDialog.close(); oExportGuard.release(); if(eResult == nmPebiResultAnimationExporter::Export_Succeeded) { onControlOperationSucceeded(); QMessageBox::information( this, tr("Export Animation"), tr("The result animation was saved to:\n%1") .arg(QDir::toNativeSeparators( oRequest.m_sTargetFilePath))); } else if(eResult == nmPebiResultAnimationExporter::Export_Failed) { QMessageBox::warning( this, tr("Export Animation"), sExportError.isEmpty() ? tr("Unable to export the result animation.") : sExportError); } } void nmWxPebiResultViewWidget::onScalarBarToggled(bool bVisible) { if(m_pSceneController != nullptr && m_pSceneController->hasSnapshot()) { m_pSceneController->setScalarBarVisible(bVisible); } } void nmWxPebiResultViewWidget::onScaleGridToggled(bool bVisible) { if(m_pSceneController != nullptr && m_pSceneController->hasSnapshot()) { m_pSceneController->setScaleGridVisible(bVisible); } } void nmWxPebiResultViewWidget::onWellNamesToggled(bool bVisible) { if(m_pSceneController != nullptr && m_pSceneController->hasSnapshot()) { m_pSceneController->setWellNamesVisible(bVisible); } } void nmWxPebiResultViewWidget::createUi() { QVBoxLayout* pMainLayout = new QVBoxLayout(this); pMainLayout->setContentsMargins(0, 0, 0, 0); pMainLayout->setSpacing(0); QWidget* pRenderArea = new QWidget(this); QHBoxLayout* pRenderLayout = new QHBoxLayout(pRenderArea); pRenderLayout->setContentsMargins(0, 0, 0, 0); pRenderLayout->setSpacing(0); // 通用容器是本窗口唯一创建 QVTKWidget、Renderer 和 RenderWindow 的对象。 m_pRenderContainer = new nmWxVTKRenderContainerWidget(pRenderArea); m_pRenderContainer->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding); m_pRenderContainer->setViewCommandCapabilities( nmVTKViewCommandController::ViewCommand_ZoomIn | nmVTKViewCommandController::ViewCommand_ZoomOut | nmVTKViewCommandController::ViewCommand_Fit | nmVTKViewCommandController::ViewCommand_Top | nmVTKViewCommandController::ViewCommand_Front | nmVTKViewCommandController::ViewCommand_SetRotationCenter | nmVTKViewCommandController::ViewCommand_SaveImage | nmVTKViewCommandController::ViewCommand_CopyImage | nmVTKViewCommandController::ViewCommand_Print | nmVTKViewCommandController::ViewCommand_PrintPreview); m_pSceneController = new nmPebiResultSceneController(m_pRenderContainer); connect(m_pRenderContainer, SIGNAL(viewportSizeChanged(int,int)), this, SLOT(onViewportSizeChanged(int,int))); const QSize oInitialViewportSize = m_pRenderContainer->getViewportSize(); onViewportSizeChanged( oInitialViewportSize.width(), oInitialViewportSize.height()); m_pToolBar = new QToolBar(pRenderArea); m_pToolBar->setOrientation(Qt::Vertical); m_pToolBar->setMovable(false); m_pToolBar->setFloatable(false); m_pToolBar->setIconSize(QSize(32, 32)); m_pToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); m_pToolBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); m_pScalarBarAction = new QAction( QIcon(resultViewIconPath("NmColorScale.png")), tr("Color Scale"), this); m_pScaleGridAction = new QAction( QIcon(resultViewIconPath("NmScaleGrid.png")), tr("Scale Grid"), this); m_pWellNamesAction = new QAction( QIcon(resultViewIconPath("NmShowWellNames.png")), tr("Well Names"), this); QAction* const pBusinessActions[] = { m_pScalarBarAction, m_pScaleGridAction, m_pWellNamesAction }; for(int nIndex = 0; nIndex < 3; ++nIndex) { pBusinessActions[nIndex]->setCheckable(true); pBusinessActions[nIndex]->setToolTip( pBusinessActions[nIndex]->text()); } nmVTKViewCommandController* pCommandController = m_pRenderContainer->getViewCommandController(); if(pCommandController != nullptr) { QAction* pZoomOutAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_ZoomOut); QAction* pFitAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_Fit); QAction* pZoomInAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_ZoomIn); QAction* pTopAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_Top); QAction* pFrontAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_Front); QAction* pPickCenterAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_SetRotationCenter); QAction* pCopyAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_CopyImage); if(pZoomOutAction != nullptr) m_pToolBar->addAction(pZoomOutAction); if(pFitAction != nullptr) m_pToolBar->addAction(pFitAction); if(pZoomInAction != nullptr) m_pToolBar->addAction(pZoomInAction); m_pToolBar->addSeparator(); if(pTopAction != nullptr) m_pToolBar->addAction(pTopAction); if(pFrontAction != nullptr) m_pToolBar->addAction(pFrontAction); if(pPickCenterAction != nullptr) m_pToolBar->addAction(pPickCenterAction); m_pToolBar->addSeparator(); for(int nIndex = 0; nIndex < 3; ++nIndex) { m_pToolBar->addAction(pBusinessActions[nIndex]); } m_pToolBar->addSeparator(); if(pCopyAction != nullptr) m_pToolBar->addAction(pCopyAction); } connect(m_pScalarBarAction, SIGNAL(toggled(bool)), this, SLOT(onScalarBarToggled(bool))); connect(m_pScaleGridAction, SIGNAL(toggled(bool)), this, SLOT(onScaleGridToggled(bool))); connect(m_pWellNamesAction, SIGNAL(toggled(bool)), this, SLOT(onWellNamesToggled(bool))); connect(m_pRenderContainer, SIGNAL(contextMenuAboutToShow(QMenu*)), this, SLOT(appendContextMenuActions(QMenu*))); pRenderLayout->addWidget(m_pRenderContainer, 1); pRenderLayout->addWidget(m_pToolBar); pMainLayout->addWidget(pRenderArea, 1); m_pStatusLabel = new QLabel(this); m_pStatusLabel->setWordWrap(true); m_pStatusLabel->setContentsMargins(8, 4, 8, 4); m_pStatusLabel->hide(); pMainLayout->addWidget(m_pStatusLabel); m_pControlBar = new nmWxPebiResultControlBar( m_pSceneController, this); connect(m_pControlBar, SIGNAL(operationFailed(QString)), this, SLOT(onControlOperationFailed(QString))); connect(m_pControlBar, SIGNAL(operationSucceeded()), this, SLOT(onControlOperationSucceeded())); connect(m_pControlBar, SIGNAL(exportAnimationRequested()), this, SLOT(onExportAnimationRequested())); pMainLayout->addWidget(m_pControlBar); synchronizeBusinessActions(); m_pRenderContainer->showEmptyState(tr("No result data.")); } void nmWxPebiResultViewWidget::synchronizeOnce() { if(m_pDataManager.isNull()) { if(m_bObservedStateValid && m_pObservedSnapshot.isNull()) { return; } clearSceneAndControls(tr("No result data.")); rememberObservedState( QSharedPointer(), QString(), false); return; } const QSharedPointer pSnapshot = m_pDataManager->getPebiResultSnapshot(); const QString sSnapshotId = pSnapshot.isNull() ? QString() : pSnapshot->getSnapshotId(); const bool bCurrent = !pSnapshot.isNull() && m_pDataManager->isPebiResultSnapshotCurrent(); const bool bSameIdentity = m_bObservedStateValid && m_pObservedSnapshot.data() == pSnapshot.data() && m_sObservedSnapshotId == sSnapshotId; const bool bSameState = bSameIdentity && m_bObservedSnapshotCurrent == bCurrent; if(bSameState) { return; } const bool bBecameStale = bSameIdentity && m_bObservedSnapshotCurrent && !bCurrent; m_sOperationError.clear(); if(pSnapshot.isNull()) { clearSceneAndControls(tr("No result data.")); rememberObservedState(pSnapshot, sSnapshotId, false); return; } if(!bSameIdentity) { // 候选提交前停止本窗口 Timer;失败时旧 Scene 和最后压力帧记忆仍保留。 m_pControlBar->stopPlayback(); QString sError; const bool bBound = m_pSceneController->setSnapshot( pSnapshot, &sError); rememberObservedState(pSnapshot, sSnapshotId, bCurrent); if(bBound) { m_pDisplayedSnapshot = pSnapshot; m_sDisplayedSnapshotId = sSnapshotId; m_sBindingError.clear(); // 新成功绑定序号会让 Playback 和 ControlBar 同时重置各自缓存。 m_pControlBar->synchronizeFromScene(); synchronizeBusinessActions(); refreshStatusBar(); return; } m_sBindingError = sError.isEmpty() ? tr("Unable to display the latest result.") : tr("Unable to display the latest result: %1").arg(sError); if(!m_pSceneController->hasSnapshot()) { m_pDisplayedSnapshot.clear(); m_sDisplayedSnapshotId.clear(); } m_pControlBar->synchronizeFromScene(); synchronizeBusinessActions(); if(m_pSceneController->hasSnapshot()) { // 旧画面保持 Ready;这里只显示红色非阻塞提示,不能追加 Render。 refreshStatusBar(); } else { m_pStatusLabel->hide(); m_pRenderContainer->showErrorState(m_sBindingError); } return; } rememberObservedState(pSnapshot, sSnapshotId, bCurrent); if(bBecameStale) { // 只在“当前 -> 过期”的边沿暂停一次;用户之后可以手动重新播放旧结果。 m_pControlBar->stopPlayback(); } refreshStatusBar(); } void nmWxPebiResultViewWidget::clearSceneAndControls( const QString& sEmptyMessage) { if(m_pControlBar != nullptr) { m_pControlBar->stopPlayback(); } if(m_pSceneController != nullptr) { m_pSceneController->clear(); } m_pObservedSnapshot.clear(); m_sObservedSnapshotId.clear(); m_bObservedSnapshotCurrent = false; m_bObservedStateValid = false; m_pDisplayedSnapshot.clear(); m_sDisplayedSnapshotId.clear(); m_sBindingError.clear(); m_sOperationError.clear(); if(m_pControlBar != nullptr) { m_pControlBar->synchronizeFromScene(); } synchronizeBusinessActions(); if(m_pStatusLabel != nullptr) { m_pStatusLabel->hide(); } if(m_pRenderContainer != nullptr) { m_pRenderContainer->showEmptyState(sEmptyMessage); } } void nmWxPebiResultViewWidget::rememberObservedState( const QSharedPointer& pSnapshot, const QString& sSnapshotId, bool bCurrent) { m_pObservedSnapshot = pSnapshot; m_sObservedSnapshotId = sSnapshotId; m_bObservedSnapshotCurrent = bCurrent; m_bObservedStateValid = true; } void nmWxPebiResultViewWidget::synchronizeBusinessActions() { if(m_pSceneController == nullptr) { return; } setActionCheckedWithoutSignal( m_pScalarBarAction, m_pSceneController->isScalarBarVisible()); setActionCheckedWithoutSignal( m_pScaleGridAction, m_pSceneController->isScaleGridVisible()); setActionCheckedWithoutSignal( m_pWellNamesAction, m_pSceneController->areWellNamesVisible()); const bool bEnabled = m_pSceneController->hasSnapshot(); m_pScalarBarAction->setEnabled(bEnabled); m_pScaleGridAction->setEnabled(bEnabled); m_pWellNamesAction->setEnabled(bEnabled); } void nmWxPebiResultViewWidget::refreshStatusBar() { if(m_pStatusLabel == nullptr || m_pSceneController == nullptr || !m_pSceneController->hasSnapshot()) { if(m_pStatusLabel != nullptr) { m_pStatusLabel->hide(); } return; } if(!m_sBindingError.isEmpty()) { m_pStatusLabel->setStyleSheet( "QLabel { color: #a32626; background: #fdecec; } "); m_pStatusLabel->setText(m_sBindingError); m_pStatusLabel->show(); return; } if(m_bObservedStateValid && !m_bObservedSnapshotCurrent) { m_pStatusLabel->setStyleSheet( "QLabel { color: #6b5200; background: #fff4bf; } "); m_pStatusLabel->setText(tr( "Current inputs have changed. Showing the last successful result.")); m_pStatusLabel->show(); return; } if(!m_sOperationError.isEmpty()) { m_pStatusLabel->setStyleSheet( "QLabel { color: #a32626; background: #fdecec; } "); m_pStatusLabel->setText(m_sOperationError); m_pStatusLabel->show(); return; } m_pStatusLabel->hide(); } void nmWxPebiResultViewWidget::disconnectDataManager() { nmDataAnalyzeManager* pDataManager = m_pDataManager.data(); if(pDataManager != nullptr) { disconnect(pDataManager, SIGNAL(sigPebiResultStateChanged()), this, SLOT(onPebiResultStateChanged())); disconnect(pDataManager, SIGNAL(destroyed(QObject*)), this, SLOT(onDataManagerDestroyed(QObject*))); } m_pDataManager = nullptr; }