Files
ResInsight/ApplicationLibCode/ProjectDataModel/CorrelationPlots/RimCorrelationReportPlot.cpp
T
Magne Sjaastad 0d13f49a87 #15168: Simplify correlation report plot names in project tree
Name the summary correlation report 'Summary Correlation: ...' and give report sub-plots fixed names, harmonized with the RFT correlation report. Plot titles are unchanged. Also refresh the project tree and avoid duplicate history curve sets after reload in the summary report, and re-dock floating sub-plots when restoring the RFT report layout.
2026-11-13 11:11:49 +01:00

967 lines
41 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// 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 <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimCorrelationReportPlot.h"
#include "RiaPreferences.h"
#include "RiaQDateTimeTools.h"
#include "RiaTimeTTools.h"
#include "Summary/RiaSummaryCurveDefinition.h"
#include "Summary/RiaSummaryPlotTools.h"
#include "RifEclipseSummaryAddressDefines.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 "RimPlotAxisAnnotation.h"
#include "RimPlotAxisProperties.h"
#include "RimRegularLegendConfig.h"
#include "RimSummaryEnsemble.h"
#include "RimSummaryPlot.h"
#include "RimSummaryTimeAxisProperties.h"
#include "RimTimeAxisAnnotation.h"
#include "Tools/RimPlotAxisTools.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 <QContextMenuEvent>
#include <QFrame>
#include <QSettings>
#include <QStringList>
#include <QVBoxLayout>
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 plotPos = invTransform( screenPixelCoordinates );
const auto timeTValue = RiaTimeTTools::fromDouble( plotPos.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 );
}
if ( auto* valueAxis = dynamic_cast<RimPlotAxisProperties*>( m_summaryPlot->axisPropertiesForPlotAxis( RiuPlotAxis::defaultLeft() ) ) )
{
removeValueAnnotation( valueAxis );
auto* valueAnno = new RimPlotAxisAnnotation();
valueAnno->setAnnotationType( RimPlotAxisAnnotation::AnnotationType::LINE );
valueAnno->setValue( plotPos.y() );
valueAnno->setName(
RimPlotAxisTools::axisValueText( plotPos.y(), valueAxis->scaleFactor(), valueAxis->decimalCount(), valueAxis->numberFormat() ) );
valueAnno->setPenStyle( Qt::DashLine );
valueAnno->setColor( TRACKING_ANNOTATION_COLOR );
valueAnno->setAlignment( RiaDefines::TextAlignment::LEFT );
valueAxis->appendAnnotation( valueAnno );
m_valueAnnotation = valueAnno;
}
m_summaryPlot->updateAnnotationsInPlotWidget();
m_summaryPlot->updatePlotWidgetFromAxisRanges();
return {};
}
void widgetLeaveEvent( QEvent* ) override
{
if ( !m_summaryPlot ) return;
bool changed = false;
auto* timeAxisProps = m_summaryPlot->timeAxisProperties();
if ( timeAxisProps && removeTrackingAnnotation( timeAxisProps ) ) changed = true;
if ( auto* valueAxis = dynamic_cast<RimPlotAxisProperties*>( m_summaryPlot->axisPropertiesForPlotAxis( RiuPlotAxis::defaultLeft() ) ) )
{
if ( removeValueAnnotation( valueAxis ) ) changed = true;
}
if ( changed )
{
m_summaryPlot->updateAnnotationsInPlotWidget();
m_summaryPlot->updatePlotWidgetFromAxisRanges();
}
}
private:
bool removeValueAnnotation( RimPlotAxisProperties* valueAxis ) const
{
if ( m_valueAnnotation.isNull() ) return false;
valueAxis->removeAnnotation( m_valueAnnotation );
m_valueAnnotation = nullptr;
return true;
}
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<RimTimeAxisAnnotation*>( anno ) );
return true;
}
}
return false;
}
caf::PdmPointer<RimSummaryPlot> m_summaryPlot;
mutable caf::PdmPointer<RimPlotAxisAnnotation> m_valueAnnotation;
};
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 );
for ( RimViewWindow* subPlot :
std::vector<RimViewWindow*>{ m_correlationMatrixPlot(), m_correlationPlot(), m_parameterResultCrossPlot(), m_summaryPlot() } )
{
subPlot->setContainingViewWindow( this );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
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<RimPlot*> 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();
QString vectorText;
if ( const auto addresses = m_correlationPlot->addresses(); addresses.size() == 1 )
{
vectorText = QString::fromStdString( addresses.begin()->uiText() ) + ", ";
}
return QString( "Summary Correlation: %1%2 at %3" ).arg( vectorText ).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<RiuPlotWidget*>{ 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<RiuQwtPlotWidget*>( 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() );
}
}
if ( !curveSets.empty() && curveSets[0]->summaryEnsemble() )
{
updateSummaryPlotHistoryCurve( curveSets[0]->summaryEnsemble(), curveSets[0]->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 );
for ( RimViewWindow* subPlot :
std::vector<RimViewWindow*>{ m_correlationMatrixPlot(), m_correlationPlot(), m_parameterResultCrossPlot(), m_summaryPlot() } )
{
subPlot->setContainingViewWindow( this );
}
updateSummaryPlotTimeAnnotation();
}
//--------------------------------------------------------------------------------------------------
/// Keeps a second ensemble curve set in the summary plot showing the history vector matching the
/// ensemble address, if the ensemble has one.
//--------------------------------------------------------------------------------------------------
void RimCorrelationReportPlot::updateSummaryPlotHistoryCurve( RimSummaryEnsemble* ensemble, const RifEclipseSummaryAddress& address )
{
auto* collection = m_summaryPlot->ensembleCurveSetCollection();
// A history curve set restored from a project file is not referenced by m_historyCurveSet. Treat any curve set
// after the main one that shows a history vector as report-owned, and leave other curve sets untouched.
std::vector<RimEnsembleCurveSet*> historyCurveSets;
const auto curveSets = collection->curveSets();
for ( size_t i = 1; i < curveSets.size(); i++ )
{
if ( curveSets[i] == m_historyCurveSet || curveSets[i]->summaryAddressY().isHistoryVector() )
historyCurveSets.push_back( curveSets[i] );
}
collection->deleteCurveSets( historyCurveSets );
m_historyCurveSet = nullptr;
// The project tree is not notified when a curve set is deleted, so refresh it to avoid stale items
auto refreshTree = [this]() { m_summaryPlot->updateAllRequiredEditors(); };
if ( !ensemble || address.isHistoryVector() )
{
refreshTree();
return;
}
auto historyAddress = address;
historyAddress.setVectorName( address.vectorName() + RifEclipseSummaryAddressDefines::historyIdentifier() );
if ( ensemble->ensembleSummaryAddresses().contains( historyAddress ) )
{
m_historyCurveSet = RiaSummaryPlotTools::createCurveSet( ensemble, historyAddress );
collection->addCurveSet( m_historyCurveSet );
}
refreshTree();
}
//--------------------------------------------------------------------------------------------------
/// 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<QString, RiaSummaryCurveDefinition> 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() );
}
updateSummaryPlotHistoryCurve( curveDef.ensemble(), 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 );
}