ResInsight/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotManager.cpp
Magne Sjaastad 0c90f67dcc
Change API for PdmObjectHandle and PdmFieldHandle
* Refactor interface to PdmObjectHandle and PdmFieldHandle
Return objects instead of passing in structures as parameters

* Add nodiscard to several functions
* Remove redundant this->
* Rename to ptrReferencedObjectsByType
2023-05-12 21:41:34 +02:00

624 lines
24 KiB
C++

////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2021- 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 "RimSummaryPlotManager.h"
#include "RiaStdStringTools.h"
#include "RiaStringListSerializer.h"
#include "RiaSummaryStringTools.h"
#include "RiaSummaryTools.h"
#include "RiaTextStringTools.h"
#include "RifReaderEclipseSummary.h"
#include "RifSummaryReaderInterface.h"
#include "RimEnsembleCurveSet.h"
#include "RimEnsembleCurveSetCollection.h"
#include "RimMainPlotCollection.h"
#include "RimMultiPlotCollection.h"
#include "RimProject.h"
#include "RimSummaryCase.h"
#include "RimSummaryCaseCollection.h"
#include "RimSummaryCaseMainCollection.h"
#include "RimSummaryCurve.h"
#include "RimSummaryMultiPlotCollection.h"
#include "RimSummaryPlot.h"
#include "PlotBuilderCommands/RicSummaryPlotBuilder.h"
#include "SummaryPlotCommands/RicSummaryPlotFeatureImpl.h"
#include "RiuPlotMainWindowTools.h"
#include "cafPdmObjectHandle.h"
#include "cafPdmUiCheckBoxEditor.h"
#include "cafPdmUiComboBoxEditor.h"
#include "cafPdmUiLabelEditor.h"
#include "cafPdmUiLineEditor.h"
#include "cafPdmUiPushButtonEditor.h"
#include "cafPdmUiTextEditor.h"
#include "cafPdmUiTreeSelectionEditor.h"
#include "cafSelectionManager.h"
// Multi plot
#include "RimMainPlotCollection.h"
#include "RimMultiPlotCollection.h"
#include "RimProject.h"
#include "RimSummaryMultiPlot.h"
#include <QKeyEvent>
CAF_PDM_SOURCE_INIT( RimSummaryPlotManager, "RimSummaryPlotManager" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSummaryPlotManager::RimSummaryPlotManager()
{
CAF_PDM_InitObject( "Summary Plot Manager" );
CAF_PDM_InitFieldNoDefault( &m_summaryPlot, "SummaryPlot", "Summary Plot" );
CAF_PDM_InitFieldNoDefault( &m_filterText, "FilterText", "Filter Text" );
m_filterText.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
CAF_PDM_InitFieldNoDefault( &m_addressCandidates, "AddressCandidates", "Vectors" );
m_addressCandidates.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::TOP );
m_addressCandidates.uiCapability()->setUiReadOnly( true );
CAF_PDM_InitFieldNoDefault( &m_selectedDataSources, "SelectedDataSources", "Data Sources" );
m_selectedDataSources.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::TOP );
m_selectedDataSources.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
CAF_PDM_InitField( &m_includeDiffCurves,
"IncludeDiffCurves",
false,
"Include Difference Vectors",
"",
"Difference between simulated and observed(history) curve",
"" );
m_includeDiffCurves.uiCapability()->setUiEditorTypeName( caf::PdmUiNativeCheckBoxEditor::uiEditorTypeName() );
CAF_PDM_InitFieldNoDefault( &m_pushButtonReplace, "PushButtonReplace", "Replace (CTRL + Enter)" );
m_pushButtonReplace.uiCapability()->setUiEditorTypeName( caf::PdmUiPushButtonEditor::uiEditorTypeName() );
m_pushButtonReplace.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
CAF_PDM_InitFieldNoDefault( &m_pushButtonNewPlot, "PushButtonNewPlot", "New (Alt + Enter)" );
m_pushButtonNewPlot.uiCapability()->setUiEditorTypeName( caf::PdmUiPushButtonEditor::uiEditorTypeName() );
m_pushButtonNewPlot.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
CAF_PDM_InitFieldNoDefault( &m_pushButtonAppend, "PushButtonAppend", "Append (Shift + Enter)" );
m_pushButtonAppend.uiCapability()->setUiEditorTypeName( caf::PdmUiPushButtonEditor::uiEditorTypeName() );
m_pushButtonAppend.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
CAF_PDM_InitFieldNoDefault( &m_labelA, "LabelA", "" );
m_labelA.uiCapability()->setUiEditorTypeName( caf::PdmUiLabelEditor::uiEditorTypeName() );
m_labelA.xmlCapability()->disableIO();
m_labelA.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
CAF_PDM_InitFieldNoDefault( &m_labelB, "LabelB", "" );
m_labelB.uiCapability()->setUiEditorTypeName( caf::PdmUiLabelEditor::uiEditorTypeName() );
m_labelB.xmlCapability()->disableIO();
CAF_PDM_InitField( &m_individualPlotPerObject, "IndividualPlotPerObject", true, "One plot per Object" );
caf::PdmUiNativeCheckBoxEditor::configureFieldForEditor( &m_individualPlotPerObject );
CAF_PDM_InitField( &m_individualPlotPerVector, "IndividualPlotPerVector", false, "One plot per Vector" );
caf::PdmUiNativeCheckBoxEditor::configureFieldForEditor( &m_individualPlotPerVector );
CAF_PDM_InitField( &m_individualPlotPerDataSource, "IndividualPlotPerDataSource", false, "One plot per Data Source" );
caf::PdmUiNativeCheckBoxEditor::configureFieldForEditor( &m_individualPlotPerDataSource );
CAF_PDM_InitField( &m_createMultiPlot, "CreateMultiPlot", true, "Create Multiple Plots in One Window" );
caf::PdmUiNativeCheckBoxEditor::configureFieldForEditor( &m_createMultiPlot );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::setFocusToFilterText()
{
setFocusToEditorWidget( m_filterText.uiCapability() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::resetDataSourceSelection()
{
m_previousDataSourceText.clear();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::onSelectionManagerSelectionChanged( const std::set<int>& changedSelectionLevels )
{
updateUiFromSelection();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
if ( changedField == &m_summaryPlot || changedField == &m_filterText || changedField == &m_includeDiffCurves ||
changedField == &m_selectedDataSources )
{
updateSelectionFromUiChange();
updateCurveCandidates();
}
else if ( changedField == &m_pushButtonReplace )
{
replaceCurves();
m_pushButtonReplace = false;
updateFilterTextHistory();
}
else if ( changedField == &m_pushButtonNewPlot )
{
createNewPlot();
m_pushButtonNewPlot = false;
updateFilterTextHistory();
}
else if ( changedField == &m_pushButtonAppend )
{
appendCurves();
m_pushButtonAppend = false;
updateFilterTextHistory();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<caf::PdmOptionItemInfo> RimSummaryPlotManager::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
{
QList<caf::PdmOptionItemInfo> options;
if ( fieldNeedingOptions == &m_summaryPlot )
{
auto coll = RiaSummaryTools::summaryMultiPlotCollection();
coll->summaryPlotItemInfos( &options );
}
else if ( fieldNeedingOptions == &m_filterText )
{
RiaStringListSerializer stringListSerializer( curveFilterRecentlyUsedRegistryKey() );
for ( const auto& s : stringListSerializer.textStrings() )
{
options.push_back( caf::PdmOptionItemInfo( s, s ) );
}
}
else if ( fieldNeedingOptions == &m_selectedDataSources )
{
for ( const auto& displayName : dataSourceDisplayNames() )
{
options.push_back( caf::PdmOptionItemInfo( displayName, displayName ) );
}
updateSelectionFromUiChange();
}
return options;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::updateCurveCandidates()
{
std::vector<QString> curveCandidates;
auto addresses = filteredAddresses();
for ( const auto& adr : addresses )
{
curveCandidates.push_back( QString::fromStdString( adr.uiText() ) );
}
m_addressCandidates = curveCandidates;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::pair<QString, caf::PdmObject*>> RimSummaryPlotManager::findDataSourceCandidates() const
{
std::vector<std::pair<QString, PdmObject*>> candidates;
QStringList dataSourceFilters = extractDataSourceFilters();
auto [summaryCases, ensembles] = RiaSummaryStringTools::dataSourcesMatchingFilters( dataSourceFilters );
for ( auto ensemble : ensembles )
{
auto ensembleName = ensemble->name();
candidates.push_back( std::make_pair( ensembleName, ensemble ) );
}
for ( const auto& summaryCase : summaryCases )
{
auto summaryCaseName = summaryCase->displayCaseName();
QString displayName;
auto ensemble = summaryCase->ensemble();
if ( ensemble )
{
displayName = ensemble->name() + ":" + summaryCaseName;
}
else
{
displayName = summaryCaseName;
}
candidates.push_back( std::make_pair( displayName, summaryCase ) );
}
return candidates;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QString> RimSummaryPlotManager::dataSourceDisplayNames() const
{
std::vector<QString> displayNames;
std::vector<std::pair<QString, PdmObject*>> dataSources = findDataSourceCandidates();
for ( const auto& dataSource : dataSources )
{
auto displayName = dataSource.first;
displayNames.push_back( displayName );
}
return displayNames;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
{
{
auto attr = dynamic_cast<caf::PdmUiPushButtonEditorAttribute*>( attribute );
if ( attr )
{
if ( field == &m_pushButtonReplace )
{
attr->m_buttonText = "Replace Curves \n(Ctrl + Enter)";
}
if ( field == &m_pushButtonNewPlot )
{
attr->m_buttonText = "Create New Plot \n(Enter)";
}
if ( field == &m_pushButtonAppend )
{
attr->m_buttonText = "Append Curves \n(Shift + Enter)";
}
}
}
if ( field == &m_filterText )
{
auto attr = dynamic_cast<caf::PdmUiComboBoxEditorAttribute*>( attribute );
if ( attr )
{
attr->enableEditableContent = true;
attr->enableAutoComplete = false;
attr->adjustWidthToContents = true;
attr->notifyWhenTextIsEdited = true;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
uiOrdering.add( &m_summaryPlot );
uiOrdering.add( &m_includeDiffCurves );
uiOrdering.add( &m_filterText );
uiOrdering.add( &m_addressCandidates );
uiOrdering.add( &m_selectedDataSources, false );
uiOrdering.add( &m_individualPlotPerVector );
uiOrdering.add( &m_individualPlotPerDataSource, false );
uiOrdering.add( &m_individualPlotPerObject );
uiOrdering.add( &m_createMultiPlot, false );
uiOrdering.add( &m_pushButtonAppend );
uiOrdering.add( &m_pushButtonReplace, { false } );
uiOrdering.add( &m_labelB, { false } );
uiOrdering.add( &m_pushButtonNewPlot, { false } );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::appendCurves()
{
if ( !m_summaryPlot ) return;
appendCurvesToPlot( m_summaryPlot );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::replaceCurves()
{
if ( !m_summaryPlot ) return;
RimSummaryPlot* destinationPlot = m_summaryPlot;
destinationPlot->deleteAllSummaryCurves();
destinationPlot->ensembleCurveSetCollection()->deleteAllCurveSets();
RiuPlotMainWindowTools::selectAsCurrentItem( destinationPlot );
appendCurvesToPlot( destinationPlot );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::createNewPlot()
{
if ( m_filterText().trimmed().isEmpty() ) return;
std::vector<RimSummaryCase*> summaryCases;
std::vector<RimSummaryCaseCollection*> ensembles;
findFilteredSummaryCasesAndEnsembles( summaryCases, ensembles );
std::set<RifEclipseSummaryAddress> filteredAddressesFromSource = filteredAddresses();
RicSummaryPlotBuilder plotBuilder;
plotBuilder.setAddresses( filteredAddressesFromSource );
plotBuilder.setDataSources( summaryCases, ensembles );
RicSummaryPlotBuilder::RicGraphCurveGrouping groping = RicSummaryPlotBuilder::RicGraphCurveGrouping::NONE;
if ( m_individualPlotPerVector ) groping = RicSummaryPlotBuilder::RicGraphCurveGrouping::SINGLE_CURVES;
if ( m_individualPlotPerObject ) groping = RicSummaryPlotBuilder::RicGraphCurveGrouping::CURVES_FOR_OBJECT;
plotBuilder.setGrouping( groping );
plotBuilder.setIndividualPlotPerDataSource( m_individualPlotPerDataSource );
auto summaryPlots = plotBuilder.createPlots();
if ( m_createMultiPlot )
{
RicSummaryPlotBuilder::createAndAppendSummaryMultiPlot( summaryPlots );
}
else
{
for ( auto plot : summaryPlots )
{
RicSummaryPlotBuilder::createAndAppendSingleSummaryMultiPlot( plot );
plot->loadDataAndUpdate();
}
}
updateProjectTreeAndRefresUi();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimSummaryPlotManager::eventFilter( QObject* obj, QEvent* event )
{
if ( event->type() == QEvent::KeyPress )
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>( event );
if ( keyEvent && ( keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return ) )
{
auto mods = keyEvent->modifiers();
if ( mods & Qt::ShiftModifier )
appendCurves();
else if ( mods & Qt::ControlModifier )
replaceCurves();
else if ( mods == Qt::NoModifier )
createNewPlot();
}
}
return QObject::eventFilter( obj, event );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::updateUiFromSelection()
{
auto destinationObject = dynamic_cast<caf::PdmObjectHandle*>( caf::SelectionManager::instance()->selectedItem() );
RimSummaryPlot* summaryPlot = nullptr;
if ( destinationObject ) summaryPlot = destinationObject->firstAncestorOrThisOfType<RimSummaryPlot>();
if ( summaryPlot && ( m_summaryPlot() != summaryPlot ) )
{
m_summaryPlot = summaryPlot;
updateSelectionFromUiChange();
updateCurveCandidates();
}
updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<RifEclipseSummaryAddress> RimSummaryPlotManager::filteredAddresses()
{
std::vector<RimSummaryCase*> summaryCases;
std::vector<RimSummaryCaseCollection*> ensembles;
findFilteredSummaryCasesAndEnsembles( summaryCases, ensembles );
std::set<RifEclipseSummaryAddress> nativeAddresses;
if ( !summaryCases.empty() )
{
nativeAddresses = RicSummaryPlotBuilder::addressesForSource( summaryCases.front() );
}
else if ( !ensembles.empty() )
{
nativeAddresses = RicSummaryPlotBuilder::addressesForSource( ensembles.front() );
}
if ( nativeAddresses.empty() ) return {};
QStringList allCurveAddressFilters = RiaTextStringTools::splitSkipEmptyParts( m_filterText(), QRegExp( "\\s+" ) );
return RiaSummaryStringTools::computeFilteredAddresses( allCurveAddressFilters, nativeAddresses, m_includeDiffCurves );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::appendCurvesToPlot( RimSummaryPlot* destinationPlot )
{
CAF_ASSERT( destinationPlot );
std::vector<RimSummaryCase*> summaryCases;
std::vector<RimSummaryCaseCollection*> ensembles;
findFilteredSummaryCasesAndEnsembles( summaryCases, ensembles );
std::set<RifEclipseSummaryAddress> filteredAddressesFromSource = filteredAddresses();
RicSummaryPlotBuilder::appendCurvesToPlot( destinationPlot, filteredAddressesFromSource, summaryCases, ensembles );
destinationPlot->applyDefaultCurveAppearances();
destinationPlot->loadDataAndUpdate();
updateProjectTreeAndRefresUi();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::updateFilterTextHistory()
{
RiaStringListSerializer stringListSerializer( curveFilterRecentlyUsedRegistryKey() );
int maxItemCount = 10;
stringListSerializer.addString( m_filterText, maxItemCount );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::updateProjectTreeAndRefresUi()
{
RiaSummaryTools::summaryMultiPlotCollection()->updateConnectedEditors();
updateFilterTextHistory();
m_filterText.uiCapability()->updateConnectedEditors();
setFocusToFilterText();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::updateSelectionFromUiChange()
{
QStringList dataSourceFilters = extractDataSourceFilters();
if ( m_previousDataSourceText != dataSourceFilters )
{
m_selectedDataSources = dataSourceDisplayNames();
m_previousDataSourceText = dataSourceFilters;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::setFocusToEditorWidget( caf::PdmUiFieldHandle* uiFieldHandle )
{
CAF_ASSERT( uiFieldHandle );
auto editors = uiFieldHandle->connectedEditors();
if ( !editors.empty() )
{
auto fieldEditorHandle = dynamic_cast<caf::PdmUiFieldEditorHandle*>( editors.front() );
if ( fieldEditorHandle && fieldEditorHandle->editorWidget() )
{
auto widget = fieldEditorHandle->editorWidget();
// If the dock widget is floating, activateWindow() must be called to make sure the top level widget has
// focus before the editor widget is given focus
widget->activateWindow();
widget->setFocus();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QStringList RimSummaryPlotManager::extractDataSourceFilters() const
{
auto [addressFilters, dataSourceFilters] = RiaSummaryStringTools::splitIntoAddressAndDataSourceFilters( m_filterText() );
return dataSourceFilters;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::findFilteredSummaryCasesAndEnsembles( std::vector<RimSummaryCase*>& summaryCases,
std::vector<RimSummaryCaseCollection*>& ensembles ) const
{
auto filteredDataSources = findDataSourceCandidates();
for ( const auto& [dataSourceName, dataSource] : filteredDataSources )
{
auto selectedDataSources = m_selectedDataSources();
if ( std::find( selectedDataSources.begin(), selectedDataSources.end(), dataSourceName ) == std::end( selectedDataSources ) )
continue;
auto summaryCase = dynamic_cast<RimSummaryCase*>( dataSource );
if ( summaryCase )
{
summaryCases.push_back( summaryCase );
}
auto ensemble = dynamic_cast<RimSummaryCaseCollection*>( dataSource );
if ( ensemble )
{
ensembles.push_back( ensemble );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimSummaryPlotManager::curveFilterRecentlyUsedRegistryKey()
{
return "SummaryPlotManagerCurveFilterStrings";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlotManager::onSummaryDataSourceHasChanged( const caf::SignalEmitter* emitter )
{
resetDataSourceSelection();
updateSelectionFromUiChange();
}