mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-27 13:47:12 -05:00
Selecting a summary vector in a correlation report now propagates to the contained tornado, cross and summary plots by selecting the first matrix cell, which sends the existing selection signal handled by the report. A virtual onDataSourceChanged() hook on the abstract correlation plot is called after the summary vector selection dialog, and the matrix and tornado plots override it to re-select their first item so dependent plots update. When a plot is used as a sub-plot of a correlation report, the data source and correlation settings are controlled from the report, so its property editor exposes only the plot settings. Used individually the plots show the complete set of options. Whether a plot is a report sub-plot is tracked with a flag set by the owning report on construction and after read. Use updateAllRequiredEditors() for sub-plot interactions and for time step changes driven from the summary plot, so the project tree text and property panel reflect the updated auto-generated titles. Re-apply the configured dock title bar visibility after toggling a sub-plot dock, so the title bar stays hidden when a hidden sub-plot is shown again.
861 lines
37 KiB
C++
861 lines
37 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 "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 <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 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<RimTimeAxisAnnotation*>( anno ) );
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
caf::PdmPointer<RimSummaryPlot> 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<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();
|
|
|
|
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<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() );
|
|
}
|
|
}
|
|
|
|
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<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() );
|
|
}
|
|
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 );
|
|
}
|