mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
Add filtering/exclude of rows in SummaryTable (#10193)
- Add selector for excluding rows in table - Row selection is based on category, vector, threshold - Move data containers to utils class - TreeSelectionEditor: Add context menu to invert selection - Improved naming of menu items - Guard plotDefinition before connect signal/slots --------- Co-authored-by: Magne Sjaastad <magne.sjaastad@ceetronsolutions.com>
This commit is contained in:
parent
c9839fcf93
commit
342f191288
@ -152,11 +152,9 @@ RimWellConnectivityTable::RimWellConnectivityTable()
|
||||
|
||||
// Producer/Injector tracer configuration
|
||||
CAF_PDM_InitFieldNoDefault( &m_selectedProducerTracersUiField, "SelectedProducerTracers", "Producer Tracers" );
|
||||
m_selectedProducerTracersUiField.xmlCapability()->disableIO();
|
||||
m_selectedProducerTracersUiField.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
|
||||
m_selectedProducerTracersUiField.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
|
||||
CAF_PDM_InitFieldNoDefault( &m_selectedInjectorTracersUiField, "SelectedInjectorTracers", "Injector Tracers" );
|
||||
m_selectedInjectorTracersUiField.xmlCapability()->disableIO();
|
||||
m_selectedInjectorTracersUiField.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
|
||||
m_selectedInjectorTracersUiField.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
|
||||
CAF_PDM_InitField( &m_syncSelectedInjectorsFromProducerSelection, "SyncSelectedProdInj", false, "Synch Communicators ->" );
|
||||
|
@ -49,6 +49,7 @@ set(SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimCsvSummaryCase.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryTable.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryTableCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryTableTools.h
|
||||
)
|
||||
|
||||
set(SOURCE_GROUP_SOURCE_FILES
|
||||
@ -102,6 +103,7 @@ set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimCsvSummaryCase.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryTable.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryTableCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryTableTools.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
|
||||
|
@ -56,11 +56,11 @@ RimSummaryTable::RimSummaryTable()
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_case, "SummaryCase", "Case" );
|
||||
m_case.uiCapability()->setUiTreeChildrenHidden( true );
|
||||
CAF_PDM_InitFieldNoDefault( &m_vector, "Vectors", "Vector" );
|
||||
CAF_PDM_InitFieldNoDefault( &m_vector, "Vector", "Vector" );
|
||||
m_vector.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
|
||||
CAF_PDM_InitFieldNoDefault( &m_categories, "Categories", "Category" );
|
||||
m_categories.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
|
||||
m_categories = RifEclipseSummaryAddress::SummaryVarCategory::SUMMARY_WELL;
|
||||
CAF_PDM_InitFieldNoDefault( &m_category, "Categories", "Category" );
|
||||
m_category.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
|
||||
m_category = RifEclipseSummaryAddress::SummaryVarCategory::SUMMARY_WELL;
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_resamplingSelection, "ResamplingSelection", "Date Resampling" );
|
||||
m_resamplingSelection.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
|
||||
@ -68,6 +68,9 @@ RimSummaryTable::RimSummaryTable()
|
||||
|
||||
CAF_PDM_InitField( &m_thresholdValue, "ThresholdValue", 0.0, "Threshold" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_excludedRowsUiField, "ExcludedTableRows", "Exclude Rows" );
|
||||
m_excludedRowsUiField.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
|
||||
|
||||
// Table settings
|
||||
CAF_PDM_InitField( &m_showValueLabels, "ShowValueLabels", false, "Show Value Labels" );
|
||||
|
||||
@ -107,9 +110,10 @@ void RimSummaryTable::setDefaultCaseAndCategoryAndVectorName()
|
||||
{
|
||||
const auto summaryCases = getToplevelSummaryCases();
|
||||
m_case = nullptr;
|
||||
m_categories = RifEclipseSummaryAddress::SUMMARY_WELL;
|
||||
m_category = RifEclipseSummaryAddress::SUMMARY_WELL;
|
||||
m_vector = "";
|
||||
m_tableName = createTableName();
|
||||
|
||||
m_tableName = createTableName();
|
||||
if ( summaryCases.empty() ) return;
|
||||
|
||||
m_case = summaryCases.front();
|
||||
@ -117,7 +121,7 @@ void RimSummaryTable::setDefaultCaseAndCategoryAndVectorName()
|
||||
const auto summaryReader = m_case->summaryReader();
|
||||
if ( !summaryReader ) return;
|
||||
|
||||
const auto categoryVectors = getCategoryVectorsFromSummaryReader( summaryReader, m_categories() );
|
||||
const auto categoryVectors = getCategoryVectorFromSummaryReader( summaryReader, m_category() );
|
||||
if ( !categoryVectors.empty() )
|
||||
{
|
||||
m_vector = *categoryVectors.begin();
|
||||
@ -132,10 +136,10 @@ void RimSummaryTable::setFromCaseAndCategoryAndVectorName( RimSummaryCase*
|
||||
RifEclipseSummaryAddress::SummaryVarCategory category,
|
||||
const QString& vectorName )
|
||||
{
|
||||
m_case = summaryCase;
|
||||
m_categories = category;
|
||||
m_vector = vectorName;
|
||||
m_tableName = createTableName();
|
||||
m_case = summaryCase;
|
||||
m_category = category;
|
||||
m_vector = vectorName;
|
||||
m_tableName = createTableName();
|
||||
onLoadDataAndUpdate();
|
||||
}
|
||||
|
||||
@ -173,7 +177,7 @@ void RimSummaryTable::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
|
||||
if ( m_case )
|
||||
{
|
||||
auto* summaryReader = m_case->summaryReader();
|
||||
const auto categoryVectors = getCategoryVectorsFromSummaryReader( summaryReader, m_categories() );
|
||||
const auto categoryVectors = getCategoryVectorFromSummaryReader( summaryReader, m_category() );
|
||||
if ( summaryReader && !categoryVectors.empty() )
|
||||
{
|
||||
m_vector = *categoryVectors.begin();
|
||||
@ -189,12 +193,12 @@ void RimSummaryTable::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
|
||||
}
|
||||
onLoadDataAndUpdate();
|
||||
}
|
||||
else if ( changedField == &m_categories )
|
||||
else if ( changedField == &m_category )
|
||||
{
|
||||
if ( m_case )
|
||||
{
|
||||
auto* summaryReader = m_case->summaryReader();
|
||||
const auto categoryVectors = getCategoryVectorsFromSummaryReader( summaryReader, m_categories() );
|
||||
const auto categoryVectors = getCategoryVectorFromSummaryReader( summaryReader, m_category() );
|
||||
if ( summaryReader && !categoryVectors.empty() )
|
||||
{
|
||||
m_vector = *categoryVectors.begin();
|
||||
@ -222,6 +226,10 @@ void RimSummaryTable::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
|
||||
}
|
||||
onLoadDataAndUpdate();
|
||||
}
|
||||
else if ( changedField == &m_excludedRowsUiField )
|
||||
{
|
||||
onLoadDataAndUpdate();
|
||||
}
|
||||
else if ( changedField == &m_isAutomaticName && m_isAutomaticName )
|
||||
{
|
||||
m_tableName = createTableName();
|
||||
@ -263,10 +271,11 @@ void RimSummaryTable::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering
|
||||
dataGroup.add( &m_tableName );
|
||||
dataGroup.add( &m_isAutomaticName );
|
||||
dataGroup.add( &m_case );
|
||||
dataGroup.add( &m_categories );
|
||||
dataGroup.add( &m_category );
|
||||
dataGroup.add( &m_vector );
|
||||
dataGroup.add( &m_resamplingSelection );
|
||||
dataGroup.add( &m_thresholdValue );
|
||||
dataGroup.add( &m_excludedRowsUiField );
|
||||
|
||||
caf::PdmUiGroup* tableSettingsGroup = uiOrdering.addNewGroup( "Table Settings" );
|
||||
tableSettingsGroup->add( &m_showValueLabels );
|
||||
@ -300,7 +309,7 @@ QList<caf::PdmOptionItemInfo> RimSummaryTable::calculateValueOptions( const caf:
|
||||
options.push_back( caf::PdmOptionItemInfo( summaryCase->displayCaseName(), summaryCase, false, summaryCase->uiIconProvider() ) );
|
||||
}
|
||||
}
|
||||
else if ( fieldNeedingOptions == &m_categories )
|
||||
else if ( fieldNeedingOptions == &m_category )
|
||||
{
|
||||
options.push_back( caf::PdmOptionItemInfo( caf::AppEnum<RifEclipseSummaryAddress::SummaryVarCategory>::uiText(
|
||||
RifEclipseSummaryAddress::SummaryVarCategory::SUMMARY_WELL ),
|
||||
@ -317,13 +326,21 @@ QList<caf::PdmOptionItemInfo> RimSummaryTable::calculateValueOptions( const caf:
|
||||
auto* summaryReader = m_case->summaryReader();
|
||||
if ( summaryReader )
|
||||
{
|
||||
const auto categoryVectorsUnion = getCategoryVectorsFromSummaryReader( summaryReader, m_categories() );
|
||||
const auto categoryVectorsUnion = getCategoryVectorFromSummaryReader( summaryReader, m_category() );
|
||||
for ( const auto& vectorName : categoryVectorsUnion )
|
||||
{
|
||||
options.push_back( caf::PdmOptionItemInfo( vectorName, vectorName ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( fieldNeedingOptions == &m_excludedRowsUiField )
|
||||
{
|
||||
const auto vectorNames = RimSummaryTableTools::categoryNames( m_tableData.vectorDataCollection );
|
||||
for ( const auto& vectorName : vectorNames )
|
||||
{
|
||||
options.push_back( caf::PdmOptionItemInfo( vectorName, vectorName ) );
|
||||
}
|
||||
}
|
||||
else if ( fieldNeedingOptions == &m_axisTitleFontSize || fieldNeedingOptions == &m_axisLabelFontSize ||
|
||||
fieldNeedingOptions == &m_valueLabelFontSize )
|
||||
{
|
||||
@ -338,6 +355,8 @@ QList<caf::PdmOptionItemInfo> RimSummaryTable::calculateValueOptions( const caf:
|
||||
void RimSummaryTable::onLoadDataAndUpdate()
|
||||
{
|
||||
updateMdiWindowVisibility();
|
||||
createTableData();
|
||||
setExcludedRowsUiSelectionsFromTableData();
|
||||
|
||||
if ( m_matrixPlotWidget == nullptr || m_case == nullptr )
|
||||
{
|
||||
@ -350,82 +369,12 @@ void RimSummaryTable::onLoadDataAndUpdate()
|
||||
return;
|
||||
}
|
||||
|
||||
// Struct for storing vector data
|
||||
struct VectorData
|
||||
{
|
||||
QString category;
|
||||
QString name;
|
||||
std::vector<double> values;
|
||||
time_t firstTimeStep;
|
||||
time_t lastTimeStep;
|
||||
bool hasValueAboveThreshold;
|
||||
};
|
||||
|
||||
// Create time step value for vectors with no values above threshold
|
||||
const time_t invalidTimeStep = 0;
|
||||
|
||||
// Get all summary addresses for selected category (group, region, well)
|
||||
std::vector<VectorData> vectorDataCollection;
|
||||
std::set<time_t> timeStepsUnion;
|
||||
const auto summaryAddresses = getSummaryAddressesFromReader( summaryReader, m_categories(), m_vector() );
|
||||
QString unitName;
|
||||
for ( const auto& adr : summaryAddresses )
|
||||
{
|
||||
std::vector<double> values;
|
||||
summaryReader->values( adr, &values );
|
||||
const std::vector<time_t> timeSteps = summaryReader->timeSteps( adr );
|
||||
const QString vectorName = QString::fromStdString( adr.vectorName() );
|
||||
const QString categoryName = getCategoryNameFromAddress( adr );
|
||||
unitName = QString::fromStdString( summaryReader->unitName( adr ) );
|
||||
|
||||
// Get re-sampled time steps and values
|
||||
const auto [resampledTimeSteps, resampledValues] =
|
||||
RiaSummaryTools::resampledValuesForPeriod( adr, timeSteps, values, m_resamplingSelection() );
|
||||
|
||||
// Detect if values contain at least one value above threshold
|
||||
const auto valueAboveThresholdItr =
|
||||
std::find_if( resampledValues.begin(), resampledValues.end(), [&]( double value ) { return value > m_thresholdValue; } );
|
||||
const bool hasValueAboveThreshold = valueAboveThresholdItr != resampledValues.end();
|
||||
|
||||
// Find first and last time step with value above 0.0 when hasValueAboveThreshold flag is true (first and last should be
|
||||
// valid/invalid simultaneously)
|
||||
const auto firstTimeStepItr =
|
||||
std::find_if( resampledValues.begin(), resampledValues.end(), [&]( double value ) { return value > 0.0; } );
|
||||
const auto lastTimeStepItr =
|
||||
std::find_if( resampledValues.rbegin(), resampledValues.rend(), [&]( double value ) { return value > 0.0; } );
|
||||
const auto firstIdx = static_cast<size_t>( std::distance( resampledValues.begin(), firstTimeStepItr ) );
|
||||
const auto lastIdx = resampledValues.size() - static_cast<size_t>( std::distance( resampledValues.rbegin(), lastTimeStepItr ) ) - 1;
|
||||
const auto firstTimeStep = hasValueAboveThreshold ? resampledTimeSteps[firstIdx] : invalidTimeStep;
|
||||
const auto lastTimeStep = hasValueAboveThreshold ? resampledTimeSteps[lastIdx] : invalidTimeStep;
|
||||
|
||||
// Add to vector of VectorData
|
||||
VectorData vectorData{ .category = categoryName,
|
||||
.name = vectorName,
|
||||
.values = resampledValues,
|
||||
.firstTimeStep = firstTimeStep,
|
||||
.lastTimeStep = lastTimeStep,
|
||||
.hasValueAboveThreshold = hasValueAboveThreshold };
|
||||
vectorDataCollection.push_back( vectorData );
|
||||
|
||||
// Build union of resampled time steps
|
||||
timeStepsUnion.insert( resampledTimeSteps.begin(), resampledTimeSteps.end() );
|
||||
}
|
||||
|
||||
// Sort vector data on date (vectors with no values above threshold placed last):
|
||||
std::sort( vectorDataCollection.begin(),
|
||||
vectorDataCollection.end(),
|
||||
[]( const VectorData& v1, const VectorData& v2 )
|
||||
{
|
||||
if ( !v1.hasValueAboveThreshold ) return false;
|
||||
if ( v1.hasValueAboveThreshold && !v2.hasValueAboveThreshold ) return true;
|
||||
if ( v1.firstTimeStep < v2.firstTimeStep ) return true;
|
||||
if ( v1.firstTimeStep == v2.firstTimeStep && v1.lastTimeStep < v2.lastTimeStep ) return true;
|
||||
return false;
|
||||
} );
|
||||
// Exclude rows that are selected
|
||||
const auto excludedRows = std::set<QString>( m_excludedRowsUiField().begin(), m_excludedRowsUiField().end() );
|
||||
|
||||
// Convert to strings
|
||||
std::vector<QString> timeStepStrings;
|
||||
for ( const auto& timeStep : timeStepsUnion )
|
||||
for ( const auto& timeStep : m_tableData.timeStepsUnion )
|
||||
{
|
||||
timeStepStrings.push_back( RiaQDateTimeTools::fromTime_t( timeStep ).toString( dateFormatString() ) );
|
||||
}
|
||||
@ -433,28 +382,25 @@ void RimSummaryTable::onLoadDataAndUpdate()
|
||||
// Clear matrix plot
|
||||
m_matrixPlotWidget->clearPlotData();
|
||||
m_matrixPlotWidget->setColumnHeaders( timeStepStrings );
|
||||
double maxValue = 0.0;
|
||||
double minValue = 0.0;
|
||||
for ( const auto& vectorData : vectorDataCollection )
|
||||
for ( const auto& vectorData : m_tableData.vectorDataCollection )
|
||||
{
|
||||
const auto maxRowValue = *std::max_element( vectorData.values.begin(), vectorData.values.end() );
|
||||
const auto minRowValue = *std::min_element( vectorData.values.begin(), vectorData.values.end() );
|
||||
if ( maxRowValue < m_thresholdValue() ) continue;
|
||||
maxValue = std::max( maxValue, maxRowValue );
|
||||
minValue = std::min( minValue, minRowValue );
|
||||
if ( excludedRows.contains( vectorData.category ) ) continue;
|
||||
|
||||
m_matrixPlotWidget->setRowValues( vectorData.category, vectorData.values );
|
||||
}
|
||||
|
||||
if ( m_legendConfig )
|
||||
{
|
||||
m_legendConfig->setAutomaticRanges( minValue, maxValue, 0.0, 0.0 );
|
||||
m_legendConfig->setAutomaticRanges( m_tableData.minValue, m_tableData.maxValue, 0.0, 0.0 );
|
||||
}
|
||||
|
||||
// Set titles and font sizes
|
||||
const QString title =
|
||||
QString( "Summary Table - %1 [%2]<br>Date Resampling: %3</br>" ).arg( m_vector() ).arg( unitName ).arg( m_resamplingSelection().uiText() );
|
||||
const QString title = QString( "Summary Table - %1 [%2]<br>Date Resampling: %3</br>" )
|
||||
.arg( m_vector() )
|
||||
.arg( m_tableData.unitName )
|
||||
.arg( m_resamplingSelection().uiText() );
|
||||
m_matrixPlotWidget->setPlotTitle( title );
|
||||
m_matrixPlotWidget->setRowTitle( QString( "%1s" ).arg( m_categories().uiText() ) );
|
||||
m_matrixPlotWidget->setRowTitle( QString( "%1s" ).arg( m_category().uiText() ) );
|
||||
m_matrixPlotWidget->setColumnTitle( "Time steps" );
|
||||
m_matrixPlotWidget->setPlotTitleFontSize( titleFontSize() );
|
||||
m_matrixPlotWidget->setLegendFontSize( legendFontSize() );
|
||||
@ -591,7 +537,7 @@ std::set<RifEclipseSummaryAddress> RimSummaryTable::getSummaryAddressesFromReade
|
||||
RifEclipseSummaryAddress::SummaryVarCategory category,
|
||||
const QString& vector ) const
|
||||
{
|
||||
if ( !summaryReader ) return std::set<RifEclipseSummaryAddress>();
|
||||
if ( !summaryReader ) return {};
|
||||
|
||||
std::set<RifEclipseSummaryAddress> categoryAddresses;
|
||||
const std::set<RifEclipseSummaryAddress> allResultAddresses = summaryReader->allResultAddresses();
|
||||
@ -607,15 +553,15 @@ std::set<RifEclipseSummaryAddress> RimSummaryTable::getSummaryAddressesFromReade
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::set<QString> RimSummaryTable::getCategoryVectorsFromSummaryReader( const RifSummaryReaderInterface* summaryReader,
|
||||
RifEclipseSummaryAddress::SummaryVarCategory category ) const
|
||||
std::set<QString> RimSummaryTable::getCategoryVectorFromSummaryReader( const RifSummaryReaderInterface* summaryReader,
|
||||
RifEclipseSummaryAddress::SummaryVarCategory category ) const
|
||||
{
|
||||
if ( !summaryReader ) return std::set<QString>();
|
||||
if ( !summaryReader ) return {};
|
||||
if ( category != RifEclipseSummaryAddress::SummaryVarCategory::SUMMARY_WELL &&
|
||||
category != RifEclipseSummaryAddress::SummaryVarCategory::SUMMARY_GROUP &&
|
||||
category != RifEclipseSummaryAddress::SummaryVarCategory::SUMMARY_REGION )
|
||||
{
|
||||
return std::set<QString>();
|
||||
return {};
|
||||
}
|
||||
|
||||
std::set<QString> categoryVectors;
|
||||
@ -658,3 +604,101 @@ std::vector<RimSummaryCase*> RimSummaryTable::getToplevelSummaryCases() const
|
||||
if ( !summaryCaseMainCollection ) return {};
|
||||
return summaryCaseMainCollection->topLevelSummaryCases();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryTable::createTableData()
|
||||
{
|
||||
m_tableData = TableData();
|
||||
m_tableData.thresholdValue = m_thresholdValue();
|
||||
|
||||
const auto summaryReader = m_case->summaryReader();
|
||||
if ( !summaryReader )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create time step value for vectors with no values above threshold
|
||||
const time_t invalidTimeStep = 0;
|
||||
|
||||
// Get all summary addresses for selected category (group, region, well)
|
||||
const auto summaryAddresses = getSummaryAddressesFromReader( summaryReader, m_category(), m_vector() );
|
||||
QString unitName;
|
||||
for ( const auto& adr : summaryAddresses )
|
||||
{
|
||||
std::vector<double> values;
|
||||
summaryReader->values( adr, &values );
|
||||
const std::vector<time_t> timeSteps = summaryReader->timeSteps( adr );
|
||||
const QString vectorName = QString::fromStdString( adr.vectorName() );
|
||||
const QString categoryName = getCategoryNameFromAddress( adr );
|
||||
|
||||
// Get re-sampled time steps and values
|
||||
const auto& [resampledTimeSteps, resampledValues] =
|
||||
RiaSummaryTools::resampledValuesForPeriod( adr, timeSteps, values, m_resamplingSelection() );
|
||||
|
||||
if ( resampledValues.empty() ) continue;
|
||||
|
||||
// Exclude vectors with values BELOW threshold - to include visualization of values equal to threshold!
|
||||
const auto maxRowValue = *std::max_element( resampledValues.begin(), resampledValues.end() );
|
||||
const auto minRowValue = *std::min_element( resampledValues.begin(), resampledValues.end() );
|
||||
if ( maxRowValue < m_tableData.thresholdValue ) continue;
|
||||
|
||||
// Detect if values contain at least one value ABOVE threshold
|
||||
const bool hasValueAboveThreshold = RimSummaryTableTools::hasValueAboveThreshold( resampledValues, m_tableData.thresholdValue );
|
||||
|
||||
// Find first and last time step with value above 0.0 when hasValueAboveThreshold flag is true (first and last should be
|
||||
// valid/invalid simultaneously)
|
||||
const auto firstTimeStepItr =
|
||||
std::find_if( resampledValues.begin(), resampledValues.end(), [&]( double value ) { return value > 0.0; } );
|
||||
const auto lastTimeStepItr =
|
||||
std::find_if( resampledValues.rbegin(), resampledValues.rend(), [&]( double value ) { return value > 0.0; } );
|
||||
const auto firstIdx = static_cast<size_t>( std::distance( resampledValues.begin(), firstTimeStepItr ) );
|
||||
const auto lastIdx = resampledValues.size() - static_cast<size_t>( std::distance( resampledValues.rbegin(), lastTimeStepItr ) ) - 1;
|
||||
const auto firstTimeStep = hasValueAboveThreshold ? resampledTimeSteps[firstIdx] : invalidTimeStep;
|
||||
const auto lastTimeStep = hasValueAboveThreshold ? resampledTimeSteps[lastIdx] : invalidTimeStep;
|
||||
|
||||
// Add to collection of VectorData for table data
|
||||
VectorData vectorData{ .category = categoryName,
|
||||
.name = vectorName,
|
||||
.values = resampledValues,
|
||||
.firstTimeStep = firstTimeStep,
|
||||
.lastTimeStep = lastTimeStep };
|
||||
m_tableData.vectorDataCollection.push_back( vectorData );
|
||||
|
||||
// Update min/max values
|
||||
m_tableData.maxValue = std::max( m_tableData.maxValue, maxRowValue );
|
||||
m_tableData.minValue = std::min( m_tableData.minValue, minRowValue );
|
||||
|
||||
// Build union of resampled time steps
|
||||
m_tableData.timeStepsUnion.insert( resampledTimeSteps.begin(), resampledTimeSteps.end() );
|
||||
|
||||
// Set unit name
|
||||
if ( m_tableData.unitName.isEmpty() )
|
||||
{
|
||||
m_tableData.unitName = QString::fromStdString( summaryReader->unitName( adr ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Sort vector data on date
|
||||
RimSummaryTableTools::sortVectorDataOnDate( m_tableData );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryTable::setExcludedRowsUiSelectionsFromTableData()
|
||||
{
|
||||
const auto initialSelections = std::set<QString>( m_excludedRowsUiField().begin(), m_excludedRowsUiField().end() );
|
||||
|
||||
std::vector<QString> newSelections;
|
||||
const auto categoryNames = RimSummaryTableTools::categoryNames( m_tableData.vectorDataCollection );
|
||||
for ( const auto& categoryName : categoryNames )
|
||||
{
|
||||
if ( initialSelections.contains( categoryName ) )
|
||||
{
|
||||
newSelections.push_back( categoryName );
|
||||
}
|
||||
}
|
||||
m_excludedRowsUiField.setValueWithFieldChanged( newSelections );
|
||||
}
|
||||
|
@ -24,6 +24,8 @@
|
||||
|
||||
#include "RifEclipseSummaryAddress.h"
|
||||
|
||||
#include "RimSummaryTableTools.h"
|
||||
|
||||
#include "cafPdmField.h"
|
||||
#include "cafPdmPtrField.h"
|
||||
|
||||
@ -90,8 +92,8 @@ private:
|
||||
std::set<RifEclipseSummaryAddress> getSummaryAddressesFromReader( const RifSummaryReaderInterface* summaryReader,
|
||||
RifEclipseSummaryAddress::SummaryVarCategory category,
|
||||
const QString& vector ) const;
|
||||
std::set<QString> getCategoryVectorsFromSummaryReader( const RifSummaryReaderInterface* summaryReader,
|
||||
RifEclipseSummaryAddress::SummaryVarCategory category ) const;
|
||||
std::set<QString> getCategoryVectorFromSummaryReader( const RifSummaryReaderInterface* summaryReader,
|
||||
RifEclipseSummaryAddress::SummaryVarCategory category ) const;
|
||||
QString getCategoryNameFromAddress( const RifEclipseSummaryAddress& address ) const;
|
||||
|
||||
std::vector<RimSummaryCase*> getToplevelSummaryCases() const;
|
||||
@ -104,15 +106,26 @@ private:
|
||||
caf::PdmField<bool> m_isAutomaticName;
|
||||
caf::PdmPtrField<RimSummaryCase*> m_case;
|
||||
|
||||
caf::PdmField<caf::AppEnum<RifEclipseSummaryAddress::SummaryVarCategory>> m_categories;
|
||||
caf::PdmField<caf::AppEnum<RifEclipseSummaryAddress::SummaryVarCategory>> m_category;
|
||||
caf::PdmField<QString> m_vector;
|
||||
caf::PdmField<caf::AppEnum<RiaDefines::DateTimePeriod>> m_resamplingSelection;
|
||||
caf::PdmField<double> m_thresholdValue;
|
||||
|
||||
caf::PdmField<std::vector<QString>> m_excludedRowsUiField;
|
||||
|
||||
caf::PdmChildField<RimRegularLegendConfig*> m_legendConfig;
|
||||
|
||||
caf::PdmField<caf::FontTools::RelativeSizeEnum> m_axisTitleFontSize;
|
||||
caf::PdmField<caf::FontTools::RelativeSizeEnum> m_axisLabelFontSize;
|
||||
caf::PdmField<caf::FontTools::RelativeSizeEnum> m_valueLabelFontSize;
|
||||
caf::PdmField<bool> m_showValueLabels;
|
||||
|
||||
private:
|
||||
using VectorData = RimSummaryTableTools::VectorData;
|
||||
using TableData = RimSummaryTableTools::TableData;
|
||||
|
||||
TableData m_tableData;
|
||||
|
||||
void createTableData();
|
||||
void setExcludedRowsUiSelectionsFromTableData();
|
||||
};
|
||||
|
@ -0,0 +1,83 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2023- 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 "RimSummaryTableTools.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimSummaryTableTools::hasValueAboveThreshold( const std::vector<double>& values, double thresholdValue )
|
||||
{
|
||||
// Detect if values contain at least one value above threshold
|
||||
const auto valueAboveThresholdItr = std::find_if( values.begin(), values.end(), [&]( double value ) { return value > thresholdValue; } );
|
||||
return valueAboveThresholdItr != values.end();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimSummaryTableTools::VectorData>
|
||||
RimSummaryTableTools::vectorsAboveThreshold( const std::vector<VectorData>& vectorDataCollection, double thresholdValue )
|
||||
{
|
||||
std::vector<VectorData> vectorsAboveThreshold;
|
||||
for ( const auto& vectorData : vectorDataCollection )
|
||||
{
|
||||
if ( hasValueAboveThreshold( vectorData.values, thresholdValue ) )
|
||||
{
|
||||
vectorsAboveThreshold.push_back( vectorData );
|
||||
}
|
||||
}
|
||||
return vectorsAboveThreshold;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryTableTools::sortVectorDataOnDate( TableData& rTableData )
|
||||
{
|
||||
// Sort vector data on date (vectors with no values above threshold placed last):
|
||||
std::sort( rTableData.vectorDataCollection.begin(),
|
||||
rTableData.vectorDataCollection.end(),
|
||||
[&rTableData]( const VectorData& v1, const VectorData& v2 )
|
||||
{
|
||||
const bool firstHasValueAboveThreshold = hasValueAboveThreshold( v1.values, rTableData.thresholdValue );
|
||||
const bool secondHasValueAboveThreshold = hasValueAboveThreshold( v2.values, rTableData.thresholdValue );
|
||||
if ( !firstHasValueAboveThreshold ) return false;
|
||||
if ( firstHasValueAboveThreshold && !secondHasValueAboveThreshold ) return true;
|
||||
if ( v1.firstTimeStep < v2.firstTimeStep ) return true;
|
||||
if ( v1.firstTimeStep == v2.firstTimeStep && v1.lastTimeStep < v2.lastTimeStep ) return true;
|
||||
return false;
|
||||
} );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<QString> RimSummaryTableTools::categoryNames( const std::vector<VectorData>& vectorDataCollection )
|
||||
{
|
||||
if ( vectorDataCollection.empty() ) return {};
|
||||
|
||||
std::vector<QString> categoryNames;
|
||||
for ( const auto& vectorData : vectorDataCollection )
|
||||
{
|
||||
categoryNames.push_back( vectorData.category );
|
||||
}
|
||||
return categoryNames;
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2023- 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 <QString>
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace RimSummaryTableTools
|
||||
{
|
||||
// Struct for storing vector data
|
||||
struct VectorData
|
||||
{
|
||||
QString category;
|
||||
QString name;
|
||||
std::vector<double> values;
|
||||
time_t firstTimeStep;
|
||||
time_t lastTimeStep;
|
||||
};
|
||||
|
||||
// Struct for storing table data
|
||||
struct TableData
|
||||
{
|
||||
std::vector<VectorData> vectorDataCollection;
|
||||
std::set<time_t> timeStepsUnion;
|
||||
double maxValue = 0.0;
|
||||
double minValue = 0.0;
|
||||
double thresholdValue = 0.0;
|
||||
QString unitName;
|
||||
};
|
||||
|
||||
bool hasValueAboveThreshold( const std::vector<double>& values, double thresholdValue );
|
||||
std::vector<VectorData> vectorsAboveThreshold( const std::vector<VectorData>& vectorDataCollection, double thresholdValue );
|
||||
void sortVectorDataOnDate( TableData& rTableData );
|
||||
std::vector<QString> categoryNames( const std::vector<VectorData>& vectorDataCollection );
|
||||
} // namespace RimSummaryTableTools
|
@ -94,15 +94,18 @@ RiuQwtPlotWidget::RiuQwtPlotWidget( RimPlot* plotDefinition, QWidget* parent )
|
||||
|
||||
setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
|
||||
|
||||
connect( this, SIGNAL( plotSelected( bool ) ), plotDefinition, SLOT( onPlotSelected( bool ) ) );
|
||||
connect( this, SIGNAL( axisSelected( RiuPlotAxis, bool ) ), plotDefinition, SLOT( onAxisSelected( RiuPlotAxis, bool ) ) );
|
||||
connect( this,
|
||||
SIGNAL( plotItemSelected( std::shared_ptr<RiuPlotItem>, bool, int ) ),
|
||||
plotDefinition,
|
||||
SLOT( onPlotItemSelected( std::shared_ptr<RiuPlotItem>, bool, int ) ) );
|
||||
connect( this, SIGNAL( onKeyPressEvent( QKeyEvent* ) ), plotDefinition, SLOT( onKeyPressEvent( QKeyEvent* ) ) );
|
||||
connect( this, SIGNAL( onWheelEvent( QWheelEvent* ) ), plotDefinition, SLOT( onWheelEvent( QWheelEvent* ) ) );
|
||||
connect( this, SIGNAL( destroyed() ), plotDefinition, SLOT( onViewerDestroyed() ) );
|
||||
if ( plotDefinition )
|
||||
{
|
||||
connect( this, SIGNAL( plotSelected( bool ) ), plotDefinition, SLOT( onPlotSelected( bool ) ) );
|
||||
connect( this, SIGNAL( axisSelected( RiuPlotAxis, bool ) ), plotDefinition, SLOT( onAxisSelected( RiuPlotAxis, bool ) ) );
|
||||
connect( this,
|
||||
SIGNAL( plotItemSelected( std::shared_ptr<RiuPlotItem>, bool, int ) ),
|
||||
plotDefinition,
|
||||
SLOT( onPlotItemSelected( std::shared_ptr<RiuPlotItem>, bool, int ) ) );
|
||||
connect( this, SIGNAL( onKeyPressEvent( QKeyEvent* ) ), plotDefinition, SLOT( onKeyPressEvent( QKeyEvent* ) ) );
|
||||
connect( this, SIGNAL( onWheelEvent( QWheelEvent* ) ), plotDefinition, SLOT( onWheelEvent( QWheelEvent* ) ) );
|
||||
connect( this, SIGNAL( destroyed() ), plotDefinition, SLOT( onViewerDestroyed() ) );
|
||||
}
|
||||
|
||||
ensureAxisIsCreated( RiuPlotAxis::defaultLeft() );
|
||||
ensureAxisIsCreated( RiuPlotAxis::defaultBottom() );
|
||||
|
@ -232,14 +232,14 @@ void PdmUiTreeSelectionEditor::configureAndUpdateUi( const QString& uiConfigName
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( options.size() == 0 )
|
||||
if ( options.empty() == 0 )
|
||||
{
|
||||
m_toggleAllCheckBox->setChecked( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
QModelIndexList indices = allVisibleSourceModelIndices();
|
||||
if ( indices.size() > 0 )
|
||||
if ( !indices.empty() )
|
||||
{
|
||||
size_t editableItems = 0u;
|
||||
size_t checkedEditableItems = 0u;
|
||||
@ -388,7 +388,7 @@ void PdmUiTreeSelectionEditor::customMenuRequested( const QPoint& pos )
|
||||
}
|
||||
}
|
||||
|
||||
if ( onlyHeadersInSelection && selectedIndexes.size() > 0 )
|
||||
if ( onlyHeadersInSelection && !selectedIndexes.empty() )
|
||||
{
|
||||
{
|
||||
QAction* act = new QAction( "Sub Items On", this );
|
||||
@ -404,24 +404,41 @@ void PdmUiTreeSelectionEditor::customMenuRequested( const QPoint& pos )
|
||||
menu.addAction( act );
|
||||
}
|
||||
}
|
||||
else if ( selectedIndexes.size() > 0 )
|
||||
else if ( !selectedIndexes.empty() )
|
||||
{
|
||||
{
|
||||
QAction* act = new QAction( "Set Selected On", this );
|
||||
QAction* act = new QAction( "Set Selected Checked", this );
|
||||
connect( act, SIGNAL( triggered() ), SLOT( slotSetSelectedOn() ) );
|
||||
|
||||
menu.addAction( act );
|
||||
}
|
||||
|
||||
{
|
||||
QAction* act = new QAction( "Set Selected Off", this );
|
||||
QAction* act = new QAction( "Set Selected Unchecked", this );
|
||||
connect( act, SIGNAL( triggered() ), SLOT( slotSetSelectedOff() ) );
|
||||
|
||||
menu.addAction( act );
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
if ( selectedIndexes.size() == 1 )
|
||||
{
|
||||
QAction* act = new QAction( "Invert Checked State Of All", this );
|
||||
connect( act, SIGNAL( triggered() ), SLOT( slotInvertCheckedStateOfAll() ) );
|
||||
|
||||
menu.addAction( act );
|
||||
}
|
||||
else
|
||||
{
|
||||
QAction* act = new QAction( "Invert Checked States of Selected", this );
|
||||
connect( act, SIGNAL( triggered() ), SLOT( slotInvertCheckedStateForSelection() ) );
|
||||
|
||||
menu.addAction( act );
|
||||
}
|
||||
}
|
||||
|
||||
if ( menu.actions().size() > 0 )
|
||||
if ( !menu.actions().empty() )
|
||||
{
|
||||
// Qt doc: QAbstractScrollArea and its subclasses that map the context menu event to coordinates of the viewport().
|
||||
QPoint globalPos = m_treeView->viewport()->mapToGlobal( pos );
|
||||
@ -509,6 +526,27 @@ void PdmUiTreeSelectionEditor::slotToggleAll()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void PdmUiTreeSelectionEditor::slotInvertCheckedStateForSelection()
|
||||
{
|
||||
QItemSelection selectionInProxyModel = m_treeView->selectionModel()->selection();
|
||||
QItemSelection selectionInSourceModel = m_proxyModel->mapSelectionToSource( selectionInProxyModel );
|
||||
|
||||
m_model->invertCheckedStateForItems( selectionInSourceModel.indexes() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void PdmUiTreeSelectionEditor::slotInvertCheckedStateOfAll()
|
||||
{
|
||||
QModelIndexList indices = allVisibleSourceModelIndices();
|
||||
|
||||
m_model->invertCheckedStateForItems( indices );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
@ -109,6 +109,8 @@ private slots:
|
||||
void slotSetSubItemsOff();
|
||||
|
||||
void slotToggleAll();
|
||||
void slotInvertCheckedStateForSelection();
|
||||
void slotInvertCheckedStateOfAll();
|
||||
|
||||
void slotTextFilterChanged();
|
||||
|
||||
|
@ -137,6 +137,42 @@ void caf::PdmUiTreeSelectionQModel::setCheckedStateForItems( const QModelIndexLi
|
||||
PdmUiCommandSystemProxy::instance()->setUiValueToField( m_uiFieldHandle->uiField(), fieldValueSelection );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void caf::PdmUiTreeSelectionQModel::invertCheckedStateForItems( const QModelIndexList& indices )
|
||||
{
|
||||
if ( !m_uiFieldHandle || !m_uiFieldHandle->uiField() ) return;
|
||||
|
||||
std::set<unsigned int> currentSelectedIndices;
|
||||
{
|
||||
QVariant fieldValue = m_uiFieldHandle->uiField()->uiValue();
|
||||
QList<QVariant> fieldValueSelection = fieldValue.toList();
|
||||
|
||||
for ( const auto& v : fieldValueSelection )
|
||||
{
|
||||
currentSelectedIndices.insert( v.toUInt() );
|
||||
}
|
||||
}
|
||||
|
||||
QList<QVariant> fieldValueSelection;
|
||||
|
||||
for ( const auto& mi : indices )
|
||||
{
|
||||
const caf::PdmOptionItemInfo* optionItemInfo = optionItem( mi );
|
||||
if ( !optionItemInfo->isReadOnly() )
|
||||
{
|
||||
auto index = static_cast<unsigned int>( optionIndex( mi ) );
|
||||
if ( currentSelectedIndices.count( index ) == 0 )
|
||||
{
|
||||
fieldValueSelection.push_back( QVariant( index ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PdmUiCommandSystemProxy::instance()->setUiValueToField( m_uiFieldHandle->uiField(), fieldValueSelection );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
@ -62,6 +62,8 @@ public:
|
||||
static int optionItemValueRole();
|
||||
|
||||
void setCheckedStateForItems( const QModelIndexList& indices, bool checked );
|
||||
void invertCheckedStateForItems( const QModelIndexList& indices );
|
||||
|
||||
void enableSingleSelectionMode( bool enable );
|
||||
|
||||
int optionItemCount() const;
|
||||
|
Loading…
Reference in New Issue
Block a user