Histogram drag/drop and paste improvements (#12851)

* Add drag'n'drop support for ensemble summary vectors and ensembles into histogram plots

* Make sure histogram plot is shown and all update signals are connected when loading a project.

* Support ensemble parameter drag'n'drop from data sources into histogram plots. 

* Show ensemble parameters in data sources tree.

* Add copy/paste of histogram curves

* Re-use all existing parameters in a plot when dragging a new ensemble into it
This commit is contained in:
JJ authored and GitHub committed 2025-08-29 15:12:32 +02:00
1 parent 35d1279b1d
commit 393917e6f3
23 files changed
+746 -15

No files matched your search

@@ -200,6 +200,14 @@ void RimEnsembleParameterHistogramDataSource::setEnsembleParameter( const QStrin
m_parameter = ensembleParameter;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimEnsembleParameterHistogramDataSource::ensembleParameter() const
{
return m_parameter;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -208,6 +216,14 @@ void RimEnsembleParameterHistogramDataSource::setEnsemble( RimSummaryEnsemble* e
m_ensemble = ensemble;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSummaryEnsemble* RimEnsembleParameterHistogramDataSource::ensemble() const
{
return m_ensemble;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -37,8 +37,11 @@ public:
RimEnsembleParameterHistogramDataSource();
~RimEnsembleParameterHistogramDataSource() override;
void setEnsembleParameter( const QString& ensembleParameter );
void setEnsemble( RimSummaryEnsemble* ensemble );
void setEnsembleParameter( const QString& ensembleParameter );
QString ensembleParameter() const;
void setEnsemble( RimSummaryEnsemble* ensemble );
RimSummaryEnsemble* ensemble() const;
std::string unitNameX() const override;
std::string unitNameY() const override;
@@ -77,6 +77,14 @@ RimHistogramCurve::~RimHistogramCurve()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramCurve::initAfterRead()
{
setDataSource( m_dataSource );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -102,6 +110,14 @@ void RimHistogramCurve::setDataSource( RimHistogramDataSource* dataSource )
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimHistogramDataSource* RimHistogramCurve::dataSource() const
{
return m_dataSource;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -60,6 +60,8 @@ public:
void setDataSource( RimHistogramDataSource* dataSource );
void setAppearanceFromGraphType( RimHistogramPlot::GraphType graphType );
RimHistogramDataSource* dataSource() const;
protected:
// RimPlotCurve overrides
QString createCurveAutoName() override;
@@ -82,6 +84,8 @@ protected:
void onDataSourceChanged( const caf::SignalEmitter* emitter );
void initAfterRead() override;
private:
caf::PdmPtrField<RimPlotAxisPropertiesInterface*> m_yPlotAxisProperties;
caf::PdmPtrField<RimPlotAxisPropertiesInterface*> m_xPlotAxisProperties;
@@ -341,6 +341,8 @@ void RimHistogramMultiPlot::initAfterRead()
plot->titleChanged.connect( this, &RimHistogramMultiPlot::onSubPlotChanged );
plot->autoTitleChanged.connect( this, &RimHistogramMultiPlot::onSubPlotAutoTitleChanged );
}
loadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
@@ -20,11 +20,19 @@
#include "RiaLogging.h"
#include "RiaPlotDefines.h"
#include "Summary/RiaSummaryTools.h"
#include "RicHistogramPlotTools.h"
#include "Histogram/RimEnsembleParameterHistogramDataSource.h"
#include "Histogram/RimEnsembleSummaryVectorHistogramDataSource.h"
#include "RimHistogramCurve.h"
#include "RimHistogramCurveCollection.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 "RiuPlotAxis.h"
@@ -128,6 +136,26 @@ RimHistogramPlot::~RimHistogramPlot()
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 );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -1277,3 +1305,45 @@ RimHistogramPlot::GraphType RimHistogramPlot::graphType() const
{
return m_graphType();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimHistogramPlot::handleDroppedObjects( const std::vector<caf::PdmObjectHandle*>& objects )
{
for ( auto obj : objects )
{
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();
}
@@ -146,6 +146,8 @@ public:
void scheduleReplotIfVisible();
std::vector<RimHistogramCurve*> histogramCurves() const;
public:
// RimViewWindow overrides
void deleteViewWidget() override;
@@ -177,8 +179,12 @@ protected:
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
void onLoadDataAndUpdate() override;
void handleDroppedObjects( const std::vector<caf::PdmObjectHandle*>& objects ) override;
QImage snapshotWindowContent() override;
void initAfterRead() override;
private slots:
void onPlotZoomed();
void onUpdateCurveOrder();
@@ -209,7 +215,6 @@ private:
std::vector<RimPlotAxisPropertiesInterface*> allPlotAxes() const;
std::vector<RimHistogramCurve*> histogramCurves() const;
std::vector<RimHistogramCurve*> visibleHistogramCurvesForAxis( RiuPlotAxis plotAxis ) const;
void assignPlotAxis( RimHistogramCurve* curve );
@@ -25,6 +25,7 @@
#include "ContourMap/RimEclipseContourMapViewCollection.h"
#include "Formations/RimFormationNames.h"
#include "Formations/RimFormationNamesCollection.h"
#include "Histogram/RimHistogramCurve.h"
#include "Histogram/RimHistogramMultiPlot.h"
#include "Histogram/RimHistogramPlot.h"
#include "PlotTemplates/RimPlotTemplateFileItem.h"
@@ -992,6 +993,7 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection()
}
else if ( dynamic_cast<RimHistogramPlot*>( firstUiItem ) )
{
menuBuilder << "RicPasteHistogramCurveFeature";
menuBuilder.subMenuStart( "New Histogram Curve" );
for ( RicHistogramPlotTools::DataSourceType dataSourceType : RicHistogramPlotTools::allDataSourceTypes() )
@@ -1002,6 +1004,10 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection()
menuBuilder.subMenuEnd();
}
else if ( dynamic_cast<RimHistogramCurve*>( firstUiItem ) )
{
menuBuilder << "RicPasteHistogramCurveFeature";
}
else if ( dynamic_cast<RimMultiPlot*>( firstUiItem ) )
{
menuBuilder << "RicSnapshotViewToPdfFeature";
@@ -1,9 +1,13 @@
set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimSummaryFileSetEnsemble.h
${CMAKE_CURRENT_LIST_DIR}/RimSummaryEnsembleParameter.h
${CMAKE_CURRENT_LIST_DIR}/RimSummaryEnsembleParameterCollection.h
)
set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimSummaryFileSetEnsemble.cpp
${CMAKE_CURRENT_LIST_DIR}/RimSummaryEnsembleParameter.cpp
${CMAKE_CURRENT_LIST_DIR}/RimSummaryEnsembleParameterCollection.cpp
)
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
@@ -0,0 +1,76 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 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 "RimSummaryEnsembleParameter.h"
CAF_PDM_SOURCE_INIT( RimSummaryEnsembleParameter, "RimSummaryEnsembleParameter" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSummaryEnsembleParameter::RimSummaryEnsembleParameter()
{
CAF_PDM_InitObject( "Summary Ensemble Parameter", ":/Parameter.svg" );
CAF_PDM_InitFieldNoDefault( &m_name, "Name", "Name" );
m_name.uiCapability()->setUiReadOnly( true );
CAF_PDM_InitField( &m_ensembleId, "EnsembleId", -1, "EnsembleId" );
m_ensembleId.uiCapability()->setUiHidden( true );
setDeletable( false );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSummaryEnsembleParameter::~RimSummaryEnsembleParameter()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimSummaryEnsembleParameter::name() const
{
return m_name;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryEnsembleParameter::setName( const QString& name )
{
setUiName( name );
m_name = name;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryEnsembleParameter::setEnsembleId( int ensembleId )
{
m_ensembleId = ensembleId;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimSummaryEnsembleParameter::ensembleId() const
{
return m_ensembleId;
}
@@ -0,0 +1,47 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cafPdmField.h"
#include "cafPdmObject.h"
#include <QString>
//==================================================================================================
//
//
//==================================================================================================
class RimSummaryEnsembleParameter : public caf::PdmObject
{
CAF_PDM_HEADER_INIT;
public:
RimSummaryEnsembleParameter();
~RimSummaryEnsembleParameter();
QString name() const;
void setName( const QString& name );
void setEnsembleId( int ensembleId );
int ensembleId() const;
private:
caf::PdmField<QString> m_name;
caf::PdmField<int> m_ensembleId;
};
@@ -0,0 +1,123 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 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 "RimSummaryEnsembleParameterCollection.h"
#include "RimSummaryEnsembleParameter.h"
#include "cafPdmUiTreeOrdering.h"
CAF_PDM_SOURCE_INIT( RimSummaryEnsembleParameterCollection, "RimSummaryEnsembleParameterCollection" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSummaryEnsembleParameterCollection::RimSummaryEnsembleParameterCollection()
{
CAF_PDM_InitObject( "Ensemble Parameters", ":/Parameter.svg", "", "" );
CAF_PDM_InitFieldNoDefault( &m_parameters, "Parameters", "Parameters" );
CAF_PDM_InitField( &m_ensembleId, "EnsembleId", -1, "EnsembleId" );
m_ensembleId.uiCapability()->setUiHidden( true );
nameField()->uiCapability()->setUiHidden( true );
nameField()->uiCapability()->setUiReadOnly( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSummaryEnsembleParameterCollection::~RimSummaryEnsembleParameterCollection()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimSummaryEnsembleParameterCollection::hasParameter( const QString name ) const
{
for ( const auto& p : m_parameters )
{
if ( p->name() == name ) return true;
}
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryEnsembleParameterCollection::addParameter( QString name, bool checkDuplicates )
{
bool okToAdd = checkDuplicates ? !hasParameter( name ) : true;
if ( okToAdd )
{
auto p = new RimSummaryEnsembleParameter();
p->setName( name );
p->setEnsembleId( ensembleId() );
m_parameters.push_back( p );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryEnsembleParameterCollection::deleteChildren()
{
m_parameters.deleteChildren();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimSummaryEnsembleParameterCollection::isEmpty() const
{
return m_parameters.empty();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryEnsembleParameterCollection::updateUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering ) const
{
if ( isEmpty() ) return;
auto subnode = uiTreeOrdering.add( "Parameters", ":/Parameter.svg" );
for ( auto& p : m_parameters() )
{
subnode->add( p );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryEnsembleParameterCollection::setEnsembleId( int ensembleId )
{
m_ensembleId = ensembleId;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimSummaryEnsembleParameterCollection::ensembleId() const
{
return m_ensembleId;
}
@@ -0,0 +1,54 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RimNamedObject.h"
#include "cafPdmChildArrayField.h"
#include <QString>
#include <string>
#include <vector>
class RimSummaryEnsembleParameter;
class RimSummaryEnsembleParameterCollection : public RimNamedObject
{
CAF_PDM_HEADER_INIT;
public:
RimSummaryEnsembleParameterCollection();
~RimSummaryEnsembleParameterCollection() override;
void addParameter( QString name, bool checkDuplicates = true );
bool hasParameter( const QString name ) const;
void setEnsembleId( int ensembleId );
int ensembleId() const;
void deleteChildren();
bool isEmpty() const;
void updateUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering ) const;
private:
caf::PdmChildArrayField<RimSummaryEnsembleParameter*> m_parameters;
caf::PdmField<int> m_ensembleId;
};
@@ -25,6 +25,7 @@
#include "RiaStatisticsTools.h"
#include "RiaStdStringTools.h"
#include "RiaTextStringTools.h"
#include "Summary/Ensemble/RimSummaryEnsembleParameterCollection.h"
#include "Summary/RiaSummaryAddressAnalyzer.h"
#include "Summary/RiaSummaryTools.h"
@@ -91,6 +92,12 @@ RimSummaryEnsemble::RimSummaryEnsemble()
m_dataVectorFolders->uiCapability()->setUiTreeHidden( true );
m_dataVectorFolders.xmlCapability()->disableIO();
CAF_PDM_InitFieldNoDefault( &m_ensembleParameters, "EnsembleParameters", "Ensemble Parameters" );
m_ensembleParameters = new RimSummaryEnsembleParameterCollection();
m_ensembleParameters.uiCapability()->setUiHidden( true );
m_ensembleParameters->uiCapability()->setUiTreeHidden( true );
m_ensembleParameters.xmlCapability()->disableIO();
CAF_PDM_InitFieldNoDefault( &m_ensembleDescription, "Description", "Description" );
m_ensembleDescription.registerGetMethod( this, &RimSummaryEnsemble::ensembleDescription );
m_ensembleDescription.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::TOP );
@@ -1000,6 +1007,8 @@ void RimSummaryEnsemble::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrd
}
}
m_ensembleParameters->updateUiTreeOrdering( uiTreeOrdering );
uiTreeOrdering.skipRemainingChildren( true );
}
}
@@ -1183,6 +1192,14 @@ void RimSummaryEnsemble::buildChildNodes()
{
m_dataVectorFolders->updateFolderStructure( ensembleSummaryAddresses(), -1, m_ensembleId );
}
if ( m_ensembleParameters->isEmpty() )
{
m_ensembleParameters->setEnsembleId( ensembleId() );
for ( auto& ensPar : variationSortedEnsembleParameters() )
{
m_ensembleParameters->addParameter( ensPar.name, false /*check duplicate*/ );
}
}
}
//--------------------------------------------------------------------------------------------------
@@ -40,6 +40,7 @@
class RimSummaryCase;
class RimSummaryAddressCollection;
class RiaSummaryAddressAnalyzer;
class RimSummaryEnsembleParameterCollection;
//==================================================================================================
///
@@ -167,6 +168,8 @@ private:
caf::PdmField<bool> m_isEnsemble;
caf::PdmChildField<RimSummaryAddressCollection*> m_dataVectorFolders;
caf::PdmChildField<RimSummaryEnsembleParameterCollection*> m_ensembleParameters;
caf::PdmField<int> m_ensembleId;
mutable std::vector<RigEnsembleParameter> m_cachedSortedEnsembleParameters;