Files
ResInsight/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramPlot.cpp
T

1458 lines
53 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Statoil 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 "RimHistogramPlot.h"
#include "RiaLogging.h"
#include "RiaPlotDefines.h"
#include "Summary/RiaSummaryTools.h"
#include "RicHistogramPlotTools.h"
#include "RifCsvDataTableFormatter.h"
#include "Histogram/RimEnsembleParameterHistogramDataSource.h"
#include "Histogram/RimEnsembleSummaryVectorHistogramDataSource.h"
#include "RimHistogramCurve.h"
#include "RimHistogramCurveCollection.h"
#include "RimHistogramDataSource.h"
#include "RimMultiPlot.h"
#include "RimPlotAxisLogRangeCalculator.h"
#include "Summary/Ensemble/RimSummaryEnsembleParameter.h"
#include "Summary/Ensemble/RimSummaryFileSetEnsemble.h"
#include "Summary/RimSummaryAddress.h"
#include "Tools/RimPlotAxisTools.h"
#include "RiuAbstractOverlayContentFrame.h"
#include "RiuContextMenuLauncher.h"
#include "RiuDraggableOverlayFrame.h"
#include "RiuPlotAxis.h"
#include "RiuPlotMainWindow.h"
#include "RiuPlotMainWindowTools.h"
#include "RiuQwtPlotCurve.h"
#include "RiuQwtPlotItem.h"
#include "RiuQwtPlotWidget.h"
#include "cafAssert.h"
#include "cafPdmFieldScriptingCapability.h"
#include "cafPdmObjectScriptingCapability.h"
#include "cafPdmUiTreeOrdering.h"
#include "cafSelectionManager.h"
#include <QDateTime>
#include <QDebug>
#include <QEvent>
#include <QKeyEvent>
#include <QRectF>
#include <QString>
#include <algorithm>
#include <cmath>
#include <set>
namespace caf
{
template <>
void caf::AppEnum<RimHistogramPlot::FrequencyType>::setUp()
{
addItem( RimHistogramPlot::FrequencyType::ABSOLUTE_FREQUENCY, "ABSOLUTE_FREQUENCY", "Absolute Frequency" );
addItem( RimHistogramPlot::FrequencyType::RELATIVE_FREQUENCY, "RELATIVE_FREQUENCY", "Relative Frequency" );
addItem( RimHistogramPlot::FrequencyType::RELATIVE_FREQUENCY_PERCENT, "RELATIVE_FREQUENCY_PERCENT", "Relative Frequency [%]" );
setDefault( RimHistogramPlot::FrequencyType::RELATIVE_FREQUENCY_PERCENT );
}
template <>
void caf::AppEnum<RimHistogramPlot::GraphType>::setUp()
{
addItem( RimHistogramPlot::GraphType::BAR_GRAPH, "BAR_GRAPH", "Bar Graph" );
addItem( RimHistogramPlot::GraphType::LINE_GRAPH, "LINE_GRAPH", "Line Graph" );
setDefault( RimHistogramPlot::GraphType::BAR_GRAPH );
}
} // namespace caf
CAF_PDM_SOURCE_INIT( RimHistogramPlot, "HistogramPlot" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimHistogramPlot::RimHistogramPlot()
: curvesChanged( this )
, axisChanged( this )
, titleChanged( this )
, m_isValid( true )
, axisChangedReloadRequired( this )
, autoTitleChanged( this )
, m_legendPosition( RiuPlotWidget::Legend::BOTTOM )
{
CAF_PDM_InitScriptableObject( "Histogram Plot", ":/HistogramPlotLight16x16.png", "", "A Histogram Plot" );
CAF_PDM_InitScriptableField( &m_useAutoPlotTitle, "IsUsingAutoName", true, "Auto Title" );
CAF_PDM_InitScriptableField( &m_description, "PlotDescription", QString( "Histogram Plot" ), "Name" );
CAF_PDM_InitScriptableField( &m_normalizeCurveYValues, "normalizeCurveYValues", false, "Normalize all curves" );
CAF_PDM_InitFieldNoDefault( &m_histogramCurveCollection, "HistogramCurveCollection", "" );
m_histogramCurveCollection = new RimHistogramCurveCollection();
m_histogramCurveCollection->curvesChanged.connect( this, &RimHistogramPlot::onCurveCollectionChanged );
CAF_PDM_InitFieldNoDefault( &m_axisPropertiesArray, "AxisProperties", "Axes", ":/Axes16x16.png" );
m_axisPropertiesArray.uiCapability()->setUiTreeHidden( false );
auto leftAxis = addNewAxisProperties( RiuPlotAxis::defaultLeft(), "Left" );
leftAxis->setAlwaysRequired( true );
auto bottomAxis = addNewAxisProperties( RiuPlotAxis::defaultBottom(), "Bottom" );
bottomAxis->setAlwaysRequired( true );
CAF_PDM_InitFieldNoDefault( &m_histogramFrequencyType, "HistogramFrequencyType", "Frequency" );
CAF_PDM_InitFieldNoDefault( &m_graphType, "GraphType", "Graph Type" );
CAF_PDM_InitFieldNoDefault( &m_fallbackPlotName, "AlternateName", "AlternateName" );
m_fallbackPlotName.uiCapability()->setUiReadOnly( true );
m_fallbackPlotName.uiCapability()->setUiHidden( true );
m_fallbackPlotName.xmlCapability()->disableIO();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimHistogramPlot::~RimHistogramPlot()
{
m_isValid = false;
if ( isMainDockedWindow() ) removeWindowFromDock();
deletePlotCurvesAndPlotWidget();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::initAfterRead()
{
for ( auto curve : histogramCurves() )
{
connectCurveSignals( curve );
}
for ( const auto& axisProperties : m_axisPropertiesArray )
{
auto plotAxisProperties = dynamic_cast<RimPlotAxisProperties*>( axisProperties.p() );
if ( plotAxisProperties )
{
connectAxisSignals( plotAxisProperties );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::updateAxes()
{
updateNumericalAxis( RiaDefines::PlotAxis::PLOT_AXIS_LEFT );
updateNumericalAxis( RiaDefines::PlotAxis::PLOT_AXIS_RIGHT );
updateNumericalAxis( RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM );
updateNumericalAxis( RiaDefines::PlotAxis::PLOT_AXIS_TOP );
updatePlotWidgetFromAxisRanges();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimHistogramPlot::isLogarithmicScaleEnabled( RiuPlotAxis plotAxis ) const
{
auto axisProperties = axisPropertiesForPlotAxis( plotAxis );
if ( !axisProperties ) return false;
return axisProperties->isLogarithmicScaleEnabled();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimHistogramPlot::isCurveHighlightSupported() const
{
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QWidget* RimHistogramPlot::viewWidget()
{
return plotWidget();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotWidget* RimHistogramPlot::plotWidget()
{
if ( !m_histogramPlot ) return nullptr;
return m_histogramPlot.get();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimHistogramPlot::asciiDataForPlotExport() const
{
QString text;
for ( RimHistogramCurve* curve : histogramCurves() )
{
if ( !curve->isChecked() || !curve->dataSource() ) continue;
// Compute as line graph to get one value per bin, independent of how the curve is rendered
auto result = curve->dataSource()->compute( GraphType::LINE_GRAPH, frequencyType(), curve->isCumulative() );
if ( result.valuesX.empty() || result.valuesX.size() != result.valuesY.size() ) continue;
QString headerX = "Bin Center";
if ( !curve->unitNameX().empty() ) headerX += QString( " [%1]" ).arg( QString::fromStdString( curve->unitNameX() ) );
QString headerY = caf::AppEnum<FrequencyType>::uiText( frequencyType() );
if ( curve->isCumulative() ) headerY = "Cumulative " + headerY;
if ( !curve->unitNameY().empty() ) headerY += QString( " [%1]" ).arg( QString::fromStdString( curve->unitNameY() ) );
QString tableText;
QTextStream stringStream( &tableText );
// Use tab as field separator to make the text paste into separate columns in Excel
RifCsvDataTableFormatter formatter( stringStream, "\t" );
formatter.setUseQuotes( false );
formatter.header( { RifTextDataTableColumn( headerX ), RifTextDataTableColumn( headerY ) } );
for ( size_t i = 0; i < result.valuesX.size(); i++ )
{
formatter.add( result.valuesX[i] );
formatter.add( result.valuesY[i] );
formatter.rowCompleted();
}
formatter.tableCompleted();
if ( !text.isEmpty() ) text += "\n";
text += curve->curveName() + "\n";
text += tableText;
}
return text;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::onAxisSelected( RiuPlotAxis axis, bool toggle )
{
RiuPlotMainWindowTools::showPlotMainWindow();
caf::PdmObject* itemToSelect = axisPropertiesForPlotAxis( axis );
RiuPlotMainWindowTools::selectOrToggleObject( itemToSelect, toggle );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimHistogramCurve*> RimHistogramPlot::histogramCurves() const
{
return m_histogramCurveCollection->curves();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::updatePlotTitle()
{
if ( m_description().isEmpty() )
{
auto multiPlot = firstAncestorOrThisOfType<RimMultiPlot>();
size_t index = 0;
if ( multiPlot ) index = multiPlot->plotIndex( this );
QString title = QString( "Sub Plot %1" ).arg( index + 1 );
m_fallbackPlotName = title;
}
updateCurveNames();
updateWindowTitle();
if ( plotWidget() )
{
QString plotTitle = description();
plotWidget()->setPlotTitle( plotTitle );
plotWidget()->setPlotTitleEnabled( m_showPlotTitle && !isSubPlot() );
scheduleReplotIfVisible();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::updateAll()
{
if ( plotWidget() )
{
updatePlotTitle();
plotWidget()->updateLegend();
updateAxes();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::updateLegend()
{
if ( plotWidget() )
{
if ( m_showPlotLegends && !isSubPlot() )
{
plotWidget()->insertLegend( m_legendPosition );
}
else
{
plotWidget()->clearLegend();
}
for ( auto c : histogramCurves() )
{
c->updateLegendEntryVisibilityNoPlotUpdate();
}
}
reattachAllCurves();
if ( plotWidget() )
{
plotWidget()->updateLegend();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::setLegendPosition( RiuPlotWidget::Legend position )
{
m_legendPosition = position;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::showPlotInfoLabel( bool show )
{
if ( !m_plotInfoFrame ) return;
if ( show )
{
if ( plotWidget() ) plotWidget()->addOverlayFrame( m_plotInfoFrame );
}
else
{
if ( plotWidget() ) plotWidget()->removeOverlayFrame( m_plotInfoFrame );
delete m_plotInfoFrame;
m_plotInfoFrame = nullptr;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::updatePlotInfoLabel()
{
QStringList descriptions;
if ( m_histogramCurveCollection->isCurvesVisible() )
{
for ( RimHistogramCurve* curve : histogramCurves() )
{
if ( !curve->isChecked() || !curve->dataSource() ) continue;
for ( const QString& description : curve->dataSource()->filterDescriptions() )
{
if ( !descriptions.contains( description ) ) descriptions += description;
}
}
}
if ( !descriptions.isEmpty() && plotWidget() )
{
if ( !m_plotInfoFrame )
{
m_plotInfoFrame = new RiuDraggableOverlayFrame( plotWidget()->getParentForOverlay(), plotWidget()->overlayMargins() );
m_plotInfoFrame->setAnchorCorner( RiuDraggableOverlayFrame::AnchorCorner::TopRight );
m_plotInfoTextFrame = new RiuTextOverlayContentFrame( m_plotInfoFrame );
m_plotInfoFrame->setContentFrame( m_plotInfoTextFrame );
}
m_plotInfoTextFrame->setText( descriptions.join( "\n" ) );
}
showPlotInfoLabel( !descriptions.isEmpty() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::setNormalizationEnabled( bool enable )
{
m_normalizeCurveYValues = enable;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimHistogramPlot::isNormalizationEnabled()
{
return m_normalizeCurveYValues();
}
//--------------------------------------------------------------------------------------------------
///
/// Update regular axis with numerical axis values - i.e. not time axis.
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::updateNumericalAxis( RiaDefines::PlotAxis plotAxis )
{
if ( !plotWidget() ) return;
for ( const RimPlotAxisPropertiesInterface* axisProperties : m_axisPropertiesArray )
{
RiuPlotAxis riuPlotAxis = axisProperties->plotAxis();
if ( riuPlotAxis.axis() == plotAxis )
{
auto* axisProps = dynamic_cast<const RimPlotAxisProperties*>( axisProperties );
if ( !axisProps ) continue;
bool hasVisibleCurveForAxis = !visibleHistogramCurvesForAxis( riuPlotAxis ).empty();
bool shouldEnable = axisProperties->isActive() && hasVisibleCurveForAxis;
plotWidget()->enableAxis( riuPlotAxis, shouldEnable );
plotWidget()->enableAxisNumberLabels( riuPlotAxis, axisProps->showNumbers() );
RimPlotAxisPropertiesInterface::LegendTickmarkCount tickmarkCountEnum = axisProps->majorTickmarkCount();
int maxTickmarkCount = RimPlotAxisPropertiesInterface::tickmarkCountFromEnum( tickmarkCountEnum );
plotWidget()->setAutoTickIntervalCounts( riuPlotAxis, maxTickmarkCount, maxTickmarkCount );
Qt::AlignmentFlag titleAlignment = Qt::AlignCenter;
if ( axisProperties->titlePosition() == RimPlotAxisPropertiesInterface::AXIS_TITLE_END )
{
titleAlignment = Qt::AlignRight;
}
bool titleBold = false;
plotWidget()->setAxisFontsAndAlignment( riuPlotAxis,
axisProperties->titleFontSize(),
axisProperties->valuesFontSize(),
titleBold,
titleAlignment );
plotWidget()->setAxisTitleEnabled( riuPlotAxis, true );
RimPlotAxisTools::applyAxisScaleDraw( plotWidget(), riuPlotAxis, axisProps );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::updateZoomForAxis( RimPlotAxisPropertiesInterface* axisProperties )
{
if ( auto axisProps = dynamic_cast<RimPlotAxisProperties*>( axisProperties ) )
{
updateZoomForNumericalAxis( axisProps );
return;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::updateZoomForNumericalAxis( RimPlotAxisProperties* axisProperties )
{
if ( !axisProperties ) return;
const auto plotAxis = axisProperties->plotAxis();
if ( axisProperties->isAutoZoom() )
{
if ( axisProperties->isLogarithmicScaleEnabled() )
{
plotWidget()->setAxisScaleType( plotAxis, RiuQwtPlotWidget::AxisScaleType::LOGARITHMIC );
std::vector<const RimPlotCurve*> plotCurves;
for ( RimHistogramCurve* c : visibleHistogramCurvesForAxis( plotAxis ) )
{
plotCurves.push_back( c );
}
double min, max;
RimPlotAxisLogRangeCalculator calc( plotAxis.axis(), plotCurves );
calc.computeAxisRange( &min, &max );
if ( axisProperties->isAxisInverted() )
{
std::swap( min, max );
}
plotWidget()->setAxisScale( axisProperties->plotAxis(), min, max );
}
else
{
plotWidget()->setAxisScaleType( plotAxis, RiuQwtPlotWidget::AxisScaleType::LINEAR );
plotWidget()->setAxisAutoScale( axisProperties->plotAxis(), true );
}
}
else
{
if ( axisProperties->isLogarithmicScaleEnabled() )
{
plotWidget()->setAxisScaleType( plotAxis, RiuQwtPlotWidget::AxisScaleType::LOGARITHMIC );
}
else
{
plotWidget()->setAxisScaleType( plotAxis, RiuQwtPlotWidget::AxisScaleType::LINEAR );
}
double min = axisProperties->visibleRangeMin();
double max = axisProperties->visibleRangeMax();
if ( axisProperties->isAxisInverted() ) std::swap( min, max );
plotWidget()->setAxisScale( axisProperties->plotAxis(), min, max );
}
plotWidget()->setAxisInverted( axisProperties->plotAxis(), axisProperties->isAxisInverted() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::scheduleReplotIfVisible()
{
if ( showWindow() && plotWidget() ) plotWidget()->scheduleReplot();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimHistogramCurve*> RimHistogramPlot::visibleHistogramCurvesForAxis( RiuPlotAxis plotAxis ) const
{
std::vector<RimHistogramCurve*> curves;
if ( m_histogramCurveCollection && m_histogramCurveCollection->isCurvesVisible() )
{
for ( RimHistogramCurve* curve : m_histogramCurveCollection->curves() )
{
if ( curve->isChecked() && ( curve->axisY() == plotAxis || curve->axisX() == plotAxis ) )
{
curves.push_back( curve );
}
}
}
return curves;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimPlotAxisPropertiesInterface* RimHistogramPlot::axisPropertiesForPlotAxis( RiuPlotAxis plotAxis ) const
{
for ( RimPlotAxisPropertiesInterface* axisProperties : m_axisPropertiesArray )
{
if ( axisProperties->plotAxis() == plotAxis ) return axisProperties;
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::zoomAll()
{
setAutoScaleXEnabled( true );
setAutoScaleYEnabled( true );
updatePlotWidgetFromAxisRanges();
axisChanged.send( this );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::addCurveNoUpdate( RimHistogramCurve* curve, bool autoAssignPlotAxis )
{
if ( curve )
{
m_histogramCurveCollection->addCurve( curve );
connectCurveToPlot( curve, false, autoAssignPlotAxis );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::connectCurveToPlot( RimHistogramCurve* curve, bool update, bool autoAssignPlotAxis )
{
if ( autoAssignPlotAxis ) assignPlotAxis( curve );
connectCurveSignals( curve );
if ( plotWidget() )
{
plotWidget()->ensureAxisIsCreated( curve->axisY() );
if ( update )
{
curve->setParentPlotAndReplot( plotWidget() );
updateAxes();
}
else
{
curve->setParentPlotNoReplot( plotWidget() );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmFieldHandle* RimHistogramPlot::userDescriptionField()
{
if ( m_description().isEmpty() )
{
return &m_fallbackPlotName;
}
return &m_description;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::zoomAllForMultiPlot()
{
if ( auto multiPlot = firstAncestorOrThisOfType<RimMultiPlot>() )
{
multiPlot->zoomAll();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
RimPlot::fieldChangedByUi( changedField, oldValue, newValue );
if ( changedField == &m_description )
{
m_useAutoPlotTitle = false;
}
if ( changedField == &m_useAutoPlotTitle || changedField == &m_description )
{
autoTitleChanged.send( m_useAutoPlotTitle() );
}
if ( changedField == &m_showPlotTitle || changedField == &m_description || changedField == &m_useAutoPlotTitle )
{
updatePlotTitle();
updateConnectedEditors();
if ( !m_useAutoPlotTitle )
{
// When auto name of plot is turned off, update the auto name for all curves
for ( auto c : histogramCurves() )
{
c->updateCurveNameNoLegendUpdate();
}
}
titleChanged.send();
}
if ( changedField == &m_showPlotLegends ) updateLegend();
if ( changedField == &m_graphType )
{
for ( auto c : histogramCurves() )
{
c->setAppearanceFromGraphType( m_graphType() );
}
}
if ( changedField == &m_normalizeCurveYValues || changedField == &m_histogramFrequencyType || changedField == &m_graphType )
{
loadDataAndUpdate();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::childFieldChangedByUi( const caf::PdmFieldHandle* changedChildField )
{
updateStackedCurveData();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::updateStackedCurveData()
{
auto anyStackedCurvesPresent = true; // updateStackedCurveDataForRelevantAxes();
if ( plotWidget() && anyStackedCurvesPresent )
{
reattachAllCurves();
scheduleReplotIfVisible();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName /*= ""*/ )
{
if ( !m_isValid ) return;
uiTreeOrdering.add( &m_axisPropertiesArray );
for ( auto& curve : m_histogramCurveCollection->curves() )
{
uiTreeOrdering.add( curve );
}
uiTreeOrdering.skipRemainingChildren( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::onLoadDataAndUpdate()
{
updatePlotTitle();
auto plotWindow = firstAncestorOrThisOfType<RimMultiPlot>();
if ( plotWindow == nullptr ) updateDockWindowVisibility();
if ( m_histogramCurveCollection )
{
m_histogramCurveCollection->loadDataAndUpdate( false );
}
if ( plotWidget() )
{
if ( m_showPlotLegends && !isSubPlot() )
{
plotWidget()->insertLegend( m_legendPosition );
}
else
{
plotWidget()->clearLegend();
}
plotWidget()->setLegendFontSize( legendFontSize() );
plotWidget()->updateLegend();
}
updateAxes();
updateStackedCurveData();
updatePlotInfoLabel();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::updatePlotWidgetFromAxisRanges()
{
if ( !plotWidget() ) return;
for ( const auto& axisProperty : m_axisPropertiesArray )
{
updateZoomForAxis( axisProperty );
}
plotWidget()->updateAxes();
updateAxisRangesFromPlotWidget();
plotWidget()->updateZoomDependentCurveProperties();
scheduleReplotIfVisible();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::updateAxisRangesFromPlotWidget()
{
if ( !plotWidget() ) return;
for ( RimPlotAxisPropertiesInterface* axisProperties : m_axisPropertiesArray )
{
if ( !axisProperties ) continue;
auto [axisMin, axisMax] = plotWidget()->axisRange( axisProperties->plotAxis() );
if ( axisProperties->isAxisInverted() ) std::swap( axisMin, axisMax );
if ( auto propertyAxis = dynamic_cast<RimPlotAxisProperties*>( axisProperties ) )
{
propertyAxis->setAutoValueVisibleRangeMax( axisMax );
propertyAxis->setAutoValueVisibleRangeMin( axisMin );
}
axisProperties->setVisibleRangeMax( axisMax );
axisProperties->setVisibleRangeMin( axisMin );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::deletePlotCurvesAndPlotWidget()
{
if ( isDeletable() )
{
detachAllPlotItems();
deleteAllPlotCurves();
if ( m_histogramPlot )
{
// The RiuPlotWidget is owned by Qt, and will be deleted when its parent is destructed.
m_histogramPlot.clear();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::connectCurveSignals( RimStackablePlotCurve* curve )
{
curve->dataChanged.connect( this, &RimHistogramPlot::curveDataChanged );
curve->visibilityChanged.connect( this, &RimHistogramPlot::curveVisibilityChanged );
curve->appearanceChanged.connect( this, &RimHistogramPlot::curveAppearanceChanged );
curve->stackingChanged.connect( this, &RimHistogramPlot::curveStackingChanged );
curve->stackingColorsChanged.connect( this, &RimHistogramPlot::curveStackingColorsChanged );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::disconnectCurveSignals( RimStackablePlotCurve* curve )
{
curve->dataChanged.disconnect( this );
curve->visibilityChanged.disconnect( this );
curve->appearanceChanged.disconnect( this );
curve->stackingChanged.disconnect( this );
curve->stackingColorsChanged.disconnect( this );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::curveDataChanged( const caf::SignalEmitter* emitter )
{
loadDataAndUpdate();
curvesChanged.send();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::curveVisibilityChanged( const caf::SignalEmitter* emitter, bool visible )
{
loadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::curveAppearanceChanged( const caf::SignalEmitter* emitter )
{
scheduleReplotIfVisible();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::curveStackingChanged( const caf::SignalEmitter* emitter, bool stacked )
{
loadDataAndUpdate();
// Change of stacking can result in very large y-axis changes, so zoom all
zoomAll();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::curveStackingColorsChanged( const caf::SignalEmitter* emitter, bool stackWithPhaseColors )
{
loadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::connectAxisSignals( RimPlotAxisProperties* axis )
{
axis->settingsChanged.connect( this, &RimHistogramPlot::axisSettingsChanged );
axis->logarithmicChanged.connect( this, &RimHistogramPlot::axisLogarithmicChanged );
axis->axisPositionChanged.connect( this, &RimHistogramPlot::axisPositionChanged );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::axisSettingsChanged( const caf::SignalEmitter* emitter )
{
axisChanged.send( this );
updateAxes();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::axisLogarithmicChanged( const caf::SignalEmitter* emitter, bool isLogarithmic )
{
axisChanged.send( this );
loadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimPlotAxisProperties* RimHistogramPlot::addNewAxisProperties( RiaDefines::PlotAxis plotAxis, const QString& name )
{
RiuPlotAxis newPlotAxis = plotWidget()->createNextPlotAxis( plotAxis );
return addNewAxisProperties( newPlotAxis, name );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimPlotAxisProperties* RimHistogramPlot::addNewAxisProperties( RiuPlotAxis plotAxis, const QString& name )
{
auto* axisProperties = new RimPlotAxisProperties;
axisProperties->configureForHistogramUse();
axisProperties->enableAutoValueForAllFields( true );
axisProperties->setNameAndAxis( name, name, plotAxis.axis(), plotAxis.index() );
m_axisPropertiesArray.push_back( axisProperties );
connectAxisSignals( axisProperties );
return axisProperties;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimPlotCurve*> RimHistogramPlot::visibleCurvesForLegend()
{
std::vector<RimPlotCurve*> curves;
for ( auto c : histogramCurves() )
{
if ( !c->isChecked() ) continue;
if ( !c->showInLegend() ) continue;
curves.push_back( c );
}
return curves;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::axisPositionChanged( const caf::SignalEmitter* emitter,
RimPlotAxisProperties* axisProperties,
RiuPlotAxis oldPlotAxis,
RiuPlotAxis newPlotAxis )
{
if ( !axisProperties ) return;
if ( plotWidget() && plotWidget()->isMultiAxisSupported() )
{
// Make sure the new axis on the correct side exists.
RiuPlotAxis fixedUpPlotAxis = plotWidget()->createNextPlotAxis( newPlotAxis.axis() );
// The index can change so need to update.
axisProperties->setNameAndAxis( axisProperties->objectName(),
axisProperties->axisTitleText(),
fixedUpPlotAxis.axis(),
fixedUpPlotAxis.index() );
// // Move all attached curves
for ( auto curve : histogramCurves() )
{
if ( curve->axisY() == oldPlotAxis ) curve->setLeftOrRightAxisY( fixedUpPlotAxis );
}
// Remove the now unused axis (but keep the default axis)
if ( oldPlotAxis != RiuPlotAxis::defaultLeft() && oldPlotAxis != RiuPlotAxis::defaultRight() )
{
auto oldAxisProperties = axisPropertiesForPlotAxis( oldPlotAxis );
if ( oldAxisProperties ) m_axisPropertiesArray.removeChild( oldAxisProperties );
plotWidget()->moveAxis( oldPlotAxis, newPlotAxis );
}
updateAxes();
}
// This is probably to much, but difficult to find the required updates
loadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::setDescription( const QString& description )
{
m_description = description;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimHistogramPlot::description() const
{
return m_description();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::enableAutoPlotTitle( bool enable )
{
m_useAutoPlotTitle = enable;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimHistogramPlot::autoPlotTitle() const
{
return m_useAutoPlotTitle;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::onPlotZoomed()
{
// Disable auto scale in plot engine
setAutoScaleXEnabled( false );
setAutoScaleYEnabled( false );
// Disable auto value for min/max fields
for ( auto p : plotAxes( RimPlotAxisProperties::Orientation::ANY ) )
{
p->enableAutoValueMinMax( false );
}
updateAxisRangesFromPlotWidget();
axisChanged.send( this );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
caf::PdmUiGroup* mainOptions = uiOrdering.addNewGroup( "General Plot Options" );
if ( isMainDockedWindow() )
{
mainOptions->add( &m_showPlotTitle );
if ( m_showPlotTitle )
{
mainOptions->add( &m_useAutoPlotTitle );
mainOptions->add( &m_description );
}
}
else
{
mainOptions->add( &m_useAutoPlotTitle );
mainOptions->add( &m_description );
mainOptions->add( &m_colSpan );
}
mainOptions->add( &m_normalizeCurveYValues );
uiOrdering.add( &m_histogramFrequencyType );
uiOrdering.add( &m_graphType );
if ( isMainDockedWindow() )
{
RimPlotWindow::uiOrderingForLegendsAndFonts( uiConfigName, uiOrdering );
}
uiOrdering.skipRemainingFields( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotWidget* RimHistogramPlot::doCreatePlotViewWidget( QWidget* mainWindowParent )
{
CAF_ASSERT( mainWindowParent );
if ( !plotWidget() )
{
m_histogramPlot = new RiuQwtPlotWidget( this, mainWindowParent );
new RiuContextMenuLauncher( m_histogramPlot, { "RicShowPlotDataFeature" } );
QObject::connect( plotWidget(), SIGNAL( curveOrderNeedsUpdate() ), this, SLOT( onUpdateCurveOrder() ) );
for ( const auto& axisProperties : m_axisPropertiesArray )
{
plotWidget()->ensureAxisIsCreated( axisProperties->plotAxis() );
}
if ( m_histogramCurveCollection )
{
m_histogramCurveCollection->setParentPlotNoReplot( plotWidget() );
}
connect( plotWidget(), SIGNAL( plotZoomed() ), SLOT( onPlotZoomed() ) );
updatePlotTitle();
}
plotWidget()->setParent( mainWindowParent );
return plotWidget();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::deleteViewWidget()
{
deletePlotCurvesAndPlotWidget();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::doUpdateLayout()
{
updateFonts();
loadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::detachAllPlotItems()
{
if ( m_histogramCurveCollection )
{
m_histogramCurveCollection->detachPlotCurves();
}
showPlotInfoLabel( false );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::deleteAllPlotCurves()
{
for ( auto* c : histogramCurves() )
{
c->deletePlotCurve();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::updateCurveNames()
{
if ( m_histogramCurveCollection->isCurvesVisible() )
{
for ( auto c : histogramCurves() )
{
if ( c->isChecked() )
{
c->updateCurveNameNoLegendUpdate();
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::detachAllCurves()
{
detachAllPlotItems();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::reattachAllCurves()
{
if ( m_histogramCurveCollection )
{
m_histogramCurveCollection->reattachPlotCurves();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::onCurveCollectionChanged( const SignalEmitter* emitter )
{
curvesChanged.send();
updateStackedCurveData();
updatePlotInfoLabel();
scheduleReplotIfVisible();
updateAllRequiredEditors();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::onPlotItemSelected( std::shared_ptr<RiuPlotItem> plotItem, bool toggle, int sampleIndex )
{
auto wrapper = dynamic_cast<RiuQwtPlotItem*>( plotItem.get() );
if ( !wrapper ) return;
auto qwtPlotItem = wrapper->qwtPlotItem();
if ( !qwtPlotItem ) return;
auto riuPlotCurve = dynamic_cast<RiuQwtPlotCurve*>( qwtPlotItem );
if ( !riuPlotCurve ) return;
auto rimPlotCurve = riuPlotCurve->ownerRimCurve();
RiuPlotMainWindowTools::selectOrToggleObject( rimPlotCurve, toggle );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::setAutoScaleXEnabled( bool enabled )
{
for ( const auto& ap : m_axisPropertiesArray )
{
if ( ap->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_TOP || ap->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM )
{
ap->setAutoZoom( enabled );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::setAutoScaleYEnabled( bool enabled )
{
for ( const auto& ap : m_axisPropertiesArray )
{
if ( ap->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_LEFT || ap->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_RIGHT )
{
ap->setAutoZoom( enabled );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RimHistogramPlot::curveCount() const
{
return m_histogramCurveCollection->curves().size();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimHistogramPlot::isDeletable() const
{
auto plotWindow = firstAncestorOrThisOfType<RimMultiPlot>();
return plotWindow == nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimPlotAxisPropertiesInterface*> RimHistogramPlot::allPlotAxes() const
{
return m_axisPropertiesArray.childrenByType();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimPlotAxisProperties*> RimHistogramPlot::plotAxes( RimPlotAxisProperties::Orientation orientation ) const
{
std::vector<RimPlotAxisProperties*> axisProps;
for ( const auto& ap : m_axisPropertiesArray )
{
auto plotAxisProp = dynamic_cast<RimPlotAxisProperties*>( ap.p() );
if ( !plotAxisProp ) continue;
if ( ( orientation == RimPlotAxisProperties::Orientation::ANY ) ||
( orientation == RimPlotAxisProperties::Orientation::VERTICAL && plotAxisProp->plotAxis().isVertical() ) )
{
axisProps.push_back( plotAxisProp );
}
else if ( ( orientation == RimPlotAxisProperties::Orientation::ANY ) ||
( orientation == RimPlotAxisProperties::Orientation::HORIZONTAL && plotAxisProp->plotAxis().isHorizontal() ) )
{
axisProps.push_back( plotAxisProp );
}
}
return axisProps;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::assignPlotAxis( RimHistogramCurve* destinationCurve )
{
destinationCurve->setLeftOrRightAxisY( RiuPlotAxis::defaultLeft() );
destinationCurve->setTopOrBottomAxisX( RiuPlotAxis::defaultBottom() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::onChildDeleted( caf::PdmChildArrayFieldHandle* childArray, std::vector<caf::PdmObjectHandle*>& referringObjects )
{
if ( childArray == &m_axisPropertiesArray )
{
for ( caf::PdmObjectHandle* reffingObj : referringObjects )
{
auto* curve = dynamic_cast<RimHistogramCurve*>( reffingObj );
if ( curve )
{
curve->setLeftOrRightAxisY( RiuPlotAxis::defaultLeft() );
}
}
if ( plotWidget() )
{
std::set<RiuPlotAxis> usedPlotAxis;
for ( const auto& axisProperties : m_axisPropertiesArray )
{
usedPlotAxis.insert( axisProperties->plotAxis() );
}
plotWidget()->pruneAxes( usedPlotAxis );
updateAxes();
scheduleReplotIfVisible();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::onUpdateCurveOrder()
{
m_histogramCurveCollection->updateCurveOrder();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimHistogramPlot::FrequencyType RimHistogramPlot::frequencyType() const
{
return m_histogramFrequencyType();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimHistogramPlot::GraphType RimHistogramPlot::graphType() const
{
return m_graphType();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimHistogramPlot::isDroppableObject( caf::PdmObjectHandle* object )
{
if ( dynamic_cast<RimSummaryFileSetEnsemble*>( object ) ) return true;
if ( auto parameter = dynamic_cast<RimSummaryEnsembleParameter*>( object ) )
{
return RiaSummaryTools::ensembleById( parameter->ensembleId() ) != nullptr;
}
if ( auto address = dynamic_cast<RimSummaryAddress*>( object ) )
{
return address->isEnsemble() && RiaSummaryTools::ensembleById( address->ensembleId() ) != nullptr;
}
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::handleDroppedObjects( const std::vector<caf::PdmObjectHandle*>& objects )
{
for ( auto obj : objects )
{
if ( !isDroppableObject( obj ) ) continue;
if ( auto fileSet = dynamic_cast<RimSummaryFileSetEnsemble*>( obj ) )
{
RicHistogramPlotTools::appendEnsembleToHistogram( this, fileSet );
}
else if ( auto parameter = dynamic_cast<RimSummaryEnsembleParameter*>( obj ) )
{
auto ensemble = RiaSummaryTools::ensembleById( parameter->ensembleId() );
if ( ensemble == nullptr ) continue;
auto newDataSource = new RimEnsembleParameterHistogramDataSource();
newDataSource->setEnsemble( ensemble );
newDataSource->setEnsembleParameter( parameter->name() );
RicHistogramPlotTools::appendEnsembleParameterHistogramCurve( this, newDataSource );
}
else if ( auto address = dynamic_cast<RimSummaryAddress*>( obj ) )
{
if ( !address->isEnsemble() ) continue;
auto ensemble = RiaSummaryTools::ensembleById( address->ensembleId() );
if ( ensemble == nullptr ) continue;
auto newDataSource = new RimEnsembleSummaryVectorHistogramDataSource();
newDataSource->setDefaults(); // get default timestep
auto fileAddress = address->address();
newDataSource->setSummaryAddress( fileAddress );
newDataSource->setEnsemble( ensemble );
RicHistogramPlotTools::createHistogramCurve( this, newDataSource );
}
}
scheduleReplotIfVisible();
}