///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2020 Equinor ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RimCorrelationReportPlot.h" #include "RiaPreferences.h" #include "RiaQDateTimeTools.h" #include "RiaTimeTTools.h" #include "Summary/RiaSummaryCurveDefinition.h" #include "RimAbstractCorrelationPlot.h" #include "RimCorrelationMatrixPlot.h" #include "RimCorrelationPlot.h" #include "RimCorrelationPlotCollection.h" #include "RimEnsembleCurveSet.h" #include "RimEnsembleCurveSetCollection.h" #include "RimEnsembleStatistics.h" #include "RimParameterResultCrossPlot.h" #include "RimRegularLegendConfig.h" #include "RimSummaryEnsemble.h" #include "RimSummaryPlot.h" #include "RimSummaryTimeAxisProperties.h" #include "RimTimeAxisAnnotation.h" #include "RiuInterfaceToViewWindow.h" #include "RimViewWindow.h" #include "RiuPlotWidget.h" #include "RiuQwtPlotWidget.h" #include "DockAreaTitleBar.h" #include "DockAreaWidget.h" #include "DockManager.h" #include "DockWidget.h" #include "cafAssert.h" #include "cafPdmPointer.h" #include "cafPdmUiCheckBoxEditor.h" #include "cafPdmUiTreeOrdering.h" #include "cafSelectionManager.h" #include "qwt_picker_machine.h" #include "qwt_plot.h" #include "qwt_plot_picker.h" #include "qwt_text.h" #include #include #include #include #include static const char* DOCK_LAYOUT_REGISTRY_KEY = "CorrelationReportPlot/defaultDockLayout"; static const cvf::Color3f TRACKING_ANNOTATION_COLOR = cvf::Color3f( 0.6f, 0.4f, 0.08f ); //-------------------------------------------------------------------------------------------------- /// Thin wrapper around CDockManager that implements RiuInterfaceToViewWindow so that /// activeViewWindow() / viewWindowFromWidget() can resolve the owning RimViewWindow. //-------------------------------------------------------------------------------------------------- class RiuCorrelationReportPlotWidget : public QFrame, public RiuInterfaceToViewWindow { public: RiuCorrelationReportPlotWidget( RimViewWindow* viewWindow, QWidget* parent = nullptr ) : QFrame( parent ) , m_viewWindow( viewWindow ) { setLayout( new QVBoxLayout ); layout()->setContentsMargins( 0, 0, 0, 0 ); layout()->setSpacing( 0 ); } RimViewWindow* ownerViewWindow() const override { return m_viewWindow; } private: RimViewWindow* m_viewWindow; }; //-------------------------------------------------------------------------------------------------- /// Local picker for time tracking readout — draws a dashed vertical line following the mouse cursor. /// Manages only its own annotation so the static selected-time annotation is preserved. //-------------------------------------------------------------------------------------------------- class TimeReadoutPicker : public QwtPlotPicker { public: TimeReadoutPicker( RimSummaryPlot* summaryPlot, QwtPlot* plot ) : QwtPlotPicker( plot->canvas() ) , m_summaryPlot( summaryPlot ) { setTrackerMode( QwtPlotPicker::AlwaysOn ); plot->canvas()->setMouseTracking( true ); setStateMachine( new QwtPickerTrackerMachine ); } QwtText trackerText( const QPoint& screenPixelCoordinates ) const override { if ( !plot() || !m_summaryPlot ) return {}; const auto timeTValue = RiaTimeTTools::fromDouble( invTransform( screenPixelCoordinates ).x() ); auto* timeAxisProps = m_summaryPlot->timeAxisProperties(); if ( timeAxisProps ) { removeTrackingAnnotation( timeAxisProps ); auto* anno = RimTimeAxisAnnotation::createTimeAnnotation( timeTValue, TRACKING_ANNOTATION_COLOR ); anno->setPenStyle( Qt::DashLine ); timeAxisProps->appendAnnotation( anno ); } m_summaryPlot->updateAnnotationsInPlotWidget(); m_summaryPlot->updatePlotWidgetFromAxisRanges(); return {}; } void widgetLeaveEvent( QEvent* ) override { if ( !m_summaryPlot ) return; auto* timeAxisProps = m_summaryPlot->timeAxisProperties(); if ( timeAxisProps && removeTrackingAnnotation( timeAxisProps ) ) { m_summaryPlot->updateAnnotationsInPlotWidget(); m_summaryPlot->updatePlotWidgetFromAxisRanges(); } } private: static bool removeTrackingAnnotation( RimSummaryTimeAxisProperties* timeAxisProps ) { if ( !timeAxisProps ) return false; for ( auto* anno : timeAxisProps->annotations() ) { // Identify the tracking annotation by its pen style, which is unique among time axis annotations. This way we don't interfere // with the selected-time annotation, which is also on the time axis. if ( anno->penStyle() == Qt::DashLine ) { timeAxisProps->removeAnnotation( dynamic_cast( anno ) ); return true; } } return false; } caf::PdmPointer m_summaryPlot; }; namespace { // Ensures the correlation report plot is selected in CAF whenever a context menu event is dispatched // anywhere within the dock manager — so feature availability checks in RiuContextMenuLauncher // see the correct item (mirrors what RiuMultiPlotPage::contextMenuEvent did explicitly). class SelectionFixerOnContextMenu : public QObject { public: SelectionFixerOnContextMenu( caf::PdmObject* item, QObject* parent ) : QObject( parent ) , m_item( item ) { } bool eventFilter( QObject*, QEvent* event ) override { if ( event->type() == QEvent::ContextMenu ) caf::SelectionManager::instance()->setSelectedItem( m_item ); return false; // never consume the event } private: caf::PdmObject* m_item; }; } // namespace //================================================================================================== // // // //================================================================================================== CAF_PDM_SOURCE_INIT( RimCorrelationReportPlot, "CorrelationReportPlot" ); //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimCorrelationReportPlot::RimCorrelationReportPlot() { CAF_PDM_InitObject( "Correlation Report Plot", ":/CorrelationReportPlot16x16.png" ); setDeletable( true ); CAF_PDM_InitFieldNoDefault( &m_name, "PlotWindowTitle", "Title" ); m_name.registerGetMethod( this, &RimCorrelationReportPlot::createDescription ); CAF_PDM_InitFieldNoDefault( &m_correlationMatrixPlot, "MatrixPlot", "Matrix Plot" ); CAF_PDM_InitFieldNoDefault( &m_correlationPlot, "CorrelationPlot", "Correlation Plot" ); CAF_PDM_InitFieldNoDefault( &m_parameterResultCrossPlot, "CrossPlot", "Cross Plot" ); CAF_PDM_InitFieldNoDefault( &m_subTitleFontSize, "SubTitleFontSize", "Sub Plot Title Font Size" ); CAF_PDM_InitFieldNoDefault( &m_labelFontSize, "LabelFontSize", "Label Font Size" ); CAF_PDM_InitFieldNoDefault( &m_axisTitleFontSize, "AxisTitleFontSize", "Axis Title Font Size" ); CAF_PDM_InitFieldNoDefault( &m_axisValueFontSize, "AxisValueFontSize", "Axis Value Font Size" ); CAF_PDM_InitField( &m_showDockTitleBars, "ShowDockTitleBars", false, "Show Title Bars" ); caf::PdmUiNativeCheckBoxEditor::configureFieldForEditor( &m_showDockTitleBars ); CAF_PDM_InitFieldNoDefault( &m_summaryPlot, "SummaryPlot", "Summary Plot" ); CAF_PDM_InitField( &m_dockState, "DockState", QString(), "Dock State" ); m_dockState.uiCapability()->setUiHidden( true ); dockAsPlotWindow(); m_showWindow = true; m_showPlotLegends = false; m_titleFontSize = caf::FontTools::RelativeSize::XLarge; m_subTitleFontSize = caf::FontTools::RelativeSize::Large; m_labelFontSize = caf::FontTools::RelativeSize::Small; m_axisTitleFontSize = caf::FontTools::RelativeSize::Small; m_axisValueFontSize = caf::FontTools::RelativeSize::Small; m_correlationMatrixPlot = new RimCorrelationMatrixPlot; m_correlationMatrixPlot->setLegendsVisible( false ); m_correlationMatrixPlot->setReportSubPlot( true ); m_correlationPlot = new RimCorrelationPlot; m_correlationPlot->setLegendsVisible( false ); m_correlationPlot->setReportSubPlot( true ); m_parameterResultCrossPlot = new RimParameterResultCrossPlot; m_parameterResultCrossPlot->setLegendsVisible( true ); m_parameterResultCrossPlot->setReportSubPlot( true ); m_summaryPlot = new RimSummaryPlot(); m_summaryPlot->setLegendsVisible( false ); auto* curveSet = new RimEnsembleCurveSet(); curveSet->setColorMode( RimEnsembleCurveSet::ColorMode::SINGLE_COLOR ); curveSet->setColor( TRACKING_ANNOTATION_COLOR ); curveSet->statisticsOptions()->enableCurveLabels( false ); m_summaryPlot->ensembleCurveSetCollection()->addCurveSet( curveSet ); m_correlationMatrixPlot->matrixCellSelected.connect( this, &RimCorrelationReportPlot::onDataSelection ); m_correlationPlot->tornadoItemSelected.connect( this, &RimCorrelationReportPlot::onDataSelection ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimCorrelationReportPlot::~RimCorrelationReportPlot() { removeWindowFromDock(); cleanupBeforeClose(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QWidget* RimCorrelationReportPlot::viewWidget() { return m_viewWidget; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimCorrelationReportPlot::description() const { return createDescription(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::zoomAll() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- caf::PdmFieldHandle* RimCorrelationReportPlot::userDescriptionField() { return &m_name; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimCorrelationMatrixPlot* RimCorrelationReportPlot::matrixPlot() const { return m_correlationMatrixPlot(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimCorrelationPlot* RimCorrelationReportPlot::correlationPlot() const { return m_correlationPlot(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimParameterResultCrossPlot* RimCorrelationReportPlot::crossPlot() const { return m_parameterResultCrossPlot(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimCorrelationReportPlot::childPlotsForTextExport() const { return { matrixPlot(), correlationPlot(), crossPlot() }; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int RimCorrelationReportPlot::subTitleFontSize() const { return caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_subTitleFontSize() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int RimCorrelationReportPlot::axisTitleFontSize() const { return caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_axisTitleFontSize() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int RimCorrelationReportPlot::axisValueFontSize() const { return caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_axisValueFontSize() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimCorrelationReportPlot::createPlotWindowTitle() const { return "Correlation Report for " + createDescription(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimCorrelationReportPlot::createDescription() const { QStringList ensembles; for ( auto entry : m_correlationMatrixPlot->curveDefinitions() ) { if ( entry.ensemble() ) { ensembles.push_back( entry.ensemble()->name() ); } } ensembles.removeDuplicates(); QString ensembleNames = ensembles.join( ", " ); QString timeStep = m_correlationMatrixPlot->timeStepString(); return QString( "%1 at %2" ).arg( ensembleNames ).arg( timeStep ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::recreatePlotWidgets() { CAF_ASSERT( m_dockManager ); // Create plot widgets m_correlationMatrixPlot->createPlotWidget( m_dockManager ); m_correlationPlot->createPlotWidget( m_dockManager ); m_parameterResultCrossPlot->createPlotWidget( m_dockManager ); m_summaryPlot->createPlotWidget( m_dockManager ); // Install selection fixer on each plot widget. // Filter runs first (LIFO order) so RiuContextMenuLauncher sees the correct CAF selection. delete m_contextMenuFilter; m_contextMenuFilter = new SelectionFixerOnContextMenu( this, this ); for ( RiuPlotWidget* w : std::initializer_list{ m_correlationMatrixPlot->viewer(), m_correlationPlot->viewer(), m_parameterResultCrossPlot->viewer(), m_summaryPlot->plotWidget() } ) { if ( w ) w->installEventFilter( m_contextMenuFilter ); } // Wrap each plot in a dock widget auto makeDockWidget = [&]( const QString& title, RimPlotWindow* plot, QWidget* widget ) -> ads::CDockWidget* { auto* dock = new ads::CDockWidget( title, m_dockManager ); dock->setWidget( widget, ads::CDockWidget::ForceNoScrollArea ); connect( dock, &ads::CDockWidget::closed, this, [this, plot]() { plot->setShowWindow( false ); updateConnectedEditors(); } ); return dock; }; m_matrixDockWidget = makeDockWidget( "Matrix Plot", m_correlationMatrixPlot(), m_correlationMatrixPlot->viewer() ); m_correlationDockWidget = makeDockWidget( "Correlation Plot", m_correlationPlot(), m_correlationPlot->viewer() ); m_crossPlotDockWidget = makeDockWidget( "Cross Plot", m_parameterResultCrossPlot(), m_parameterResultCrossPlot->viewer() ); m_summaryDockWidget = makeDockWidget( "Summary Plot", m_summaryPlot(), m_summaryPlot->plotWidget() ); // Disable zoom — clicks are used for time step selection, not zooming if ( m_summaryPlot->summaryPlotWidget() ) m_summaryPlot->summaryPlotWidget()->setZoomEnabled( false ); // Connect summary plot click → time step update; add time tracking readout auto* summaryWidget = dynamic_cast( m_summaryPlot->plotWidget() ); if ( summaryWidget ) { connect( summaryWidget, &RiuQwtPlotWidget::plotMousePressedAt, this, &RimCorrelationReportPlot::onSummaryPlotMousePressed ); new TimeReadoutPicker( m_summaryPlot(), summaryWidget->qwtPlot() ); } // Restore saved dock state: project state takes priority, then registry default, then hard-coded layout QByteArray stateToRestore; if ( !m_dockState().isEmpty() ) stateToRestore = QByteArray::fromBase64( m_dockState().toLatin1() ); else { QSettings settings; QVariant v = settings.value( DOCK_LAYOUT_REGISTRY_KEY ); if ( v.isValid() ) stateToRestore = v.toByteArray(); } if ( !stateToRestore.isEmpty() ) { // Add all widgets to the manager first (required before restoreState) m_dockManager->addDockWidget( ads::LeftDockWidgetArea, m_matrixDockWidget ); m_dockManager->addDockWidget( ads::RightDockWidgetArea, m_correlationDockWidget ); m_dockManager->addDockWidget( ads::RightDockWidgetArea, m_crossPlotDockWidget ); m_dockManager->addDockWidget( ads::RightDockWidgetArea, m_summaryDockWidget ); m_dockManager->restoreState( stateToRestore, 1 ); } else { // Hard-coded default: matrix left (full height), corr top-right, cross bottom-right, // summary spanning full width at bottom auto* corrArea = m_dockManager->addDockWidget( ads::CenterDockWidgetArea, m_correlationDockWidget ); m_dockManager->addDockWidget( ads::LeftDockWidgetArea, m_matrixDockWidget ); m_dockManager->addDockWidget( ads::BottomDockWidgetArea, m_crossPlotDockWidget, corrArea ); m_dockManager->addDockWidget( ads::BottomDockWidgetArea, m_summaryDockWidget ); } // Sync initial dock visibility from subplot enabled state m_matrixDockWidget->toggleView( m_correlationMatrixPlot->showWindow() ); m_correlationDockWidget->toggleView( m_correlationPlot->showWindow() ); m_crossPlotDockWidget->toggleView( m_parameterResultCrossPlot->showWindow() ); m_summaryDockWidget->toggleView( m_summaryPlot()->showWindow() ); updateDockTitleBarsVisibility(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::cleanupBeforeClose() { m_correlationMatrixPlot->detachAllCurves(); m_correlationPlot->detachAllCurves(); m_parameterResultCrossPlot->detachAllCurves(); m_summaryPlot->detachAllCurves(); // Dock widget pointers are owned by the dock manager; nullify them before deletion m_matrixDockWidget = nullptr; m_correlationDockWidget = nullptr; m_crossPlotDockWidget = nullptr; m_summaryDockWidget = nullptr; if ( m_dockManager ) { delete m_dockManager; m_dockManager = nullptr; } if ( m_viewWidget ) { m_viewWidget->setParent( nullptr ); delete m_viewWidget; m_viewWidget = nullptr; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::setupBeforeSave() { if ( m_dockManager ) { m_dockState = QString::fromLatin1( m_dockManager->saveState( 1 ).toBase64() ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::doRenderWindowContent( QPaintDevice* paintDevice ) { if ( m_viewWidget ) { m_viewWidget->render( paintDevice ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QWidget* RimCorrelationReportPlot::createViewWidget( QWidget* mainWindowParent /*= nullptr */ ) { auto* wrapper = new RiuCorrelationReportPlotWidget( this, mainWindowParent ); m_viewWidget = wrapper; m_dockManager = new ads::CDockManager( wrapper ); m_dockManager->setStyleSheet( "ads--CDockSplitter::handle { width: 1px; height: 1px; }" ); wrapper->layout()->addWidget( m_dockManager ); recreatePlotWidgets(); return m_viewWidget; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::deleteViewWidget() { cleanupBeforeClose(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::onLoadDataAndUpdate() { updateDockWindowVisibility(); if ( m_showWindow ) { auto cases = m_parameterResultCrossPlot->summaryCasesExcludedByFilter(); m_correlationMatrixPlot->setExcludedSummaryCases( cases ); m_correlationPlot->setExcludedSummaryCases( cases ); m_correlationMatrixPlot->setLabelFontSize( m_labelFontSize() ); m_correlationMatrixPlot->setAxisTitleFontSize( m_axisTitleFontSize() ); m_correlationMatrixPlot->setAxisValueFontSize( m_axisValueFontSize() ); auto timeStep = m_correlationMatrixPlot->timeStep().toSecsSinceEpoch(); bool showOnlyTopNCorrelations = m_correlationMatrixPlot->showTopNCorrelations(); int topNFilterCount = m_correlationMatrixPlot->topNFilterCount(); bool useCaseFilter = m_correlationMatrixPlot->isCaseFilterEnabled(); auto caseFilterDataSource = m_correlationMatrixPlot->caseFilterDataSource(); m_correlationPlot->setTimeStep( timeStep ); m_correlationPlot->setShowOnlyTopNCorrelations( showOnlyTopNCorrelations ); m_correlationPlot->setTopNFilterCount( topNFilterCount ); m_correlationPlot->enableCaseFilter( useCaseFilter ); m_correlationPlot->setCaseFilterDataSource( caseFilterDataSource ); m_correlationPlot->setLabelFontSize( m_labelFontSize() ); m_correlationPlot->setAxisTitleFontSize( m_axisTitleFontSize() ); m_correlationPlot->setAxisValueFontSize( m_axisValueFontSize() ); m_correlationPlot->setShowAbsoluteValues( m_correlationMatrixPlot->showAbsoluteValues() ); m_correlationPlot->setSortByAbsoluteValues( m_correlationMatrixPlot->sortByAbsoluteValues() ); m_parameterResultCrossPlot->setTimeStep( timeStep ); m_parameterResultCrossPlot->enableCaseFilter( useCaseFilter ); m_parameterResultCrossPlot->setCaseFilterDataSource( caseFilterDataSource ); m_parameterResultCrossPlot->setLabelFontSize( m_labelFontSize() ); m_parameterResultCrossPlot->setLegendFontSize( m_legendFontSize() ); m_parameterResultCrossPlot->setAxisTitleFontSize( m_axisTitleFontSize() ); m_parameterResultCrossPlot->setAxisValueFontSize( m_axisValueFontSize() ); m_correlationMatrixPlot->loadDataAndUpdate(); m_correlationPlot->loadDataAndUpdate(); m_parameterResultCrossPlot->loadDataAndUpdate(); if ( m_summaryPlot->showWindow() ) { auto curveSets = m_summaryPlot->ensembleCurveSetCollection()->curveSets(); if ( !curveSets.empty() && !curveSets[0]->summaryEnsemble() ) { auto curveDefs = m_correlationMatrixPlot->curveDefinitions(); if ( !curveDefs.empty() ) { curveSets[0]->setSummaryEnsemble( curveDefs.front().ensemble() ); curveSets[0]->setSummaryAddressY( curveDefs.front().summaryAddressY() ); } } m_summaryPlot->loadDataAndUpdate(); if ( m_summaryPlot->plotWidget() ) m_summaryPlot->plotWidget()->setPlotTitleEnabled( true ); } updateSummaryPlotTimeAnnotation(); } updateLayout(); } //-------------------------------------------------------------------------------------------------- /// Called after PDM fields have been deserialized from the project file. Transient annotations /// (selected-time highlight, mouse-tracking dashed line) are PDM-serialized and may persist across /// save/load; reset them to a clean single highlight at the restored time step. //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::initAfterRead() { // The report sub-plot flag is not serialized, so re-establish it on the deserialized child plots. m_correlationMatrixPlot->setReportSubPlot( true ); m_correlationPlot->setReportSubPlot( true ); m_parameterResultCrossPlot->setReportSubPlot( true ); updateSummaryPlotTimeAnnotation(); } //-------------------------------------------------------------------------------------------------- /// Idempotent: clears any existing time-axis annotations on the summary plot and appends a single /// highlight at the matrix plot's current time step. Safe to call before the widget is created — /// the data model is updated regardless; rendering happens once the widget exists. //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::updateSummaryPlotTimeAnnotation() { if ( !m_summaryPlot ) return; auto* timeAxisProps = m_summaryPlot->timeAxisProperties(); if ( !timeAxisProps ) return; timeAxisProps->removeAllAnnotations(); auto timeStep = m_correlationMatrixPlot->timeStep().toSecsSinceEpoch(); timeAxisProps->appendAnnotation( RimTimeAxisAnnotation::createTimeAnnotation( timeStep, TRACKING_ANNOTATION_COLOR ) ); m_summaryPlot->updateAnnotationsInPlotWidget(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { m_correlationMatrixPlot->uiOrdering( "report", uiOrdering ); auto filterGroup = uiOrdering.addNewGroup( "Filter" ); m_parameterResultCrossPlot->appendFilterFields( *filterGroup ); auto plotGroup = uiOrdering.addNewGroup( "Plot Settings" ); plotGroup->setCollapsedByDefault(); plotGroup->add( &m_titleFontSize ); plotGroup->add( &m_subTitleFontSize ); plotGroup->add( &m_labelFontSize ); plotGroup->add( &m_legendFontSize ); plotGroup->add( &m_axisTitleFontSize ); plotGroup->add( &m_axisValueFontSize ); m_correlationMatrixPlot->legendConfig()->uiOrdering( "ColorsOnly", *plotGroup ); auto layoutGroup = uiOrdering.addNewGroup( "Dock Layout" ); layoutGroup->setCollapsedByDefault(); layoutGroup->add( &m_showDockTitleBars ); layoutGroup->addNewButton( "Save as Default Layout", [this]() { onSaveDefaultDockLayout(); } ); layoutGroup->addNewButton( "Restore Default Layout", [this]() { onRestoreDefaultDockLayout(); }, { .newRow = false } ); uiOrdering.skipRemainingFields( true ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::updateDockTitleBarsVisibility() { if ( !m_dockManager ) return; for ( auto* area : m_dockManager->openedDockAreas() ) area->titleBar()->setVisible( m_showDockTitleBars() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName ) { uiTreeOrdering.add( m_correlationMatrixPlot() ); uiTreeOrdering.add( m_correlationPlot() ); uiTreeOrdering.add( m_parameterResultCrossPlot() ); uiTreeOrdering.add( m_summaryPlot() ); uiTreeOrdering.skipRemainingChildren(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) { if ( changedField == &m_showDockTitleBars ) { updateDockTitleBarsVisibility(); return; } loadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::childFieldChangedByUi( const caf::PdmFieldHandle* changedChildField ) { // childFieldChangedByUi receives the parent field (the PdmChildField in this class), // not the specific field that changed inside the child. Sync dock visibility unconditionally // so toggling showWindow() in the tree view is always reflected immediately. if ( m_matrixDockWidget && changedChildField == &m_correlationMatrixPlot ) m_matrixDockWidget->toggleView( m_correlationMatrixPlot->showWindow() ); else if ( m_correlationDockWidget && changedChildField == &m_correlationPlot ) m_correlationDockWidget->toggleView( m_correlationPlot->showWindow() ); else if ( m_crossPlotDockWidget && changedChildField == &m_parameterResultCrossPlot ) m_crossPlotDockWidget->toggleView( m_parameterResultCrossPlot->showWindow() ); else if ( m_summaryDockWidget && changedChildField == &m_summaryPlot ) m_summaryDockWidget->toggleView( m_summaryPlot->showWindow() ); // Re-showing a dock widget creates a new dock area whose title bar defaults to visible, so re-apply // the configured title bar visibility. updateDockTitleBarsVisibility(); // onLoadDataAndUpdate treats the matrix plot as the source of truth and copies its shared // properties to the others. When the change originated on the correlation or cross plot, // first mirror it back to the matrix plot so the subsequent propagation reflects the change. if ( changedChildField == &m_correlationPlot ) syncSharedPropertiesToMatrixPlotFrom( m_correlationPlot() ); else if ( changedChildField == &m_parameterResultCrossPlot ) syncSharedPropertiesToMatrixPlotFrom( m_parameterResultCrossPlot() ); loadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::syncSharedPropertiesToMatrixPlotFrom( RimAbstractCorrelationPlot* source ) { if ( !source || source == m_correlationMatrixPlot() ) return; // Properties defined on RimAbstractCorrelationPlot — shared by all three plots. m_correlationMatrixPlot->setTimeStep( source->timeStep().toSecsSinceEpoch() ); m_correlationMatrixPlot->enableCaseFilter( source->isCaseFilterEnabled() ); m_correlationMatrixPlot->setCaseFilterDataSource( source->caseFilterDataSource() ); // Properties shared only between matrix plot and correlation (tornado) plot. if ( source == m_correlationPlot() ) { m_correlationMatrixPlot->setShowAbsoluteValues( m_correlationPlot->showAbsoluteValues() ); m_correlationMatrixPlot->setSortByAbsoluteValues( m_correlationPlot->sortByAbsoluteValues() ); m_correlationMatrixPlot->setShowTopNCorrelations( m_correlationPlot->showOnlyTopNCorrelations() ); m_correlationMatrixPlot->setTopNFilterCount( m_correlationPlot->topNFilterCount() ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::doUpdateLayout() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::onDataSelection( const caf::SignalEmitter* emitter, std::pair parameterAndCurveDef ) { auto paramName = parameterAndCurveDef.first; auto curveDef = parameterAndCurveDef.second; m_correlationMatrixPlot->setSelectedParameter( paramName ); m_correlationPlot->setCurveDefinitions( { curveDef } ); m_correlationPlot->setSelectedParameter( paramName ); m_correlationPlot->loadDataAndUpdate(); m_parameterResultCrossPlot->setCurveDefinitions( { curveDef } ); m_parameterResultCrossPlot->setEnsembleParameter( paramName ); m_parameterResultCrossPlot->loadDataAndUpdate(); if ( m_summaryPlot->showWindow() && curveDef.ensemble() ) { auto curveSets = m_summaryPlot->ensembleCurveSetCollection()->curveSets(); if ( !curveSets.empty() ) { curveSets[0]->setSummaryEnsemble( curveDef.ensemble() ); curveSets[0]->setSummaryAddressY( curveDef.summaryAddressY() ); } m_summaryPlot->loadDataAndUpdate(); if ( m_summaryPlot->plotWidget() ) m_summaryPlot->plotWidget()->setPlotTitleEnabled( true ); } updateSummaryPlotTimeAnnotation(); // updateAllRequiredEditors (not just updateConnectedEditors) so the project tree refreshes the // displayed text of the contained plot items when their data source changes. updateAllRequiredEditors(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::onSummaryPlotMousePressed( double xPlotCoordinate ) { auto clickedTime = RiaTimeTTools::fromDouble( xPlotCoordinate ); auto availableSteps = m_correlationMatrixPlot->allAvailableTimeSteps(); if ( availableSteps.empty() ) return; auto it = std::min_element( availableSteps.begin(), availableSteps.end(), [clickedTime]( time_t a, time_t b ) { return std::abs( a - clickedTime ) < std::abs( b - clickedTime ); } ); m_correlationMatrixPlot->setTimeStep( *it ); loadDataAndUpdate(); // The time step is part of the auto-generated titles, so refresh all editors to update the project // tree text and the time step field in the property panel. updateAllRequiredEditors(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::onSaveDefaultDockLayout() { if ( m_dockManager ) { QSettings settings; settings.setValue( DOCK_LAYOUT_REGISTRY_KEY, m_dockManager->saveState( 1 ) ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimCorrelationReportPlot::onRestoreDefaultDockLayout() { if ( !m_dockManager ) return; QSettings settings; QVariant v = settings.value( DOCK_LAYOUT_REGISTRY_KEY ); if ( v.isValid() ) m_dockManager->restoreState( v.toByteArray(), 1 ); }