mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 15:10:22 -05:00
843 lines
36 KiB
C++
843 lines
36 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 );
|
|
|
|
setAsPlotMdiWindow();
|
|
|
|
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_correlationPlot = new RimCorrelationPlot;
|
|
m_correlationPlot->setLegendsVisible( false );
|
|
|
|
m_parameterResultCrossPlot = new RimParameterResultCrossPlot;
|
|
m_parameterResultCrossPlot->setLegendsVisible( 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()
|
|
{
|
|
removeMdiWindowFromMdiArea();
|
|
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()
|
|
{
|
|
updateMdiWindowVisibility();
|
|
|
|
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()
|
|
{
|
|
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() );
|
|
|
|
// 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();
|
|
|
|
updateConnectedEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
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();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
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 );
|
|
}
|