mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-24 22:27:55 -05:00
#14891: Add option to use a common legend across summary sub plots
This commit is contained in:
1 parent
6a15c84463
commit
7ed51e1fd5
11 files changed
+207
-5
No files matched your search
@@ -71,6 +71,14 @@ RimMultiPlot::RimMultiPlot()
|
||||
CAF_PDM_InitField( &m_showIndividualPlotTitles, "ShowPlotTitles", true, "Show Sub Plot Titles" );
|
||||
CAF_PDM_InitFieldNoDefault( &m_majorTickmarkCount, "MajorTickmarkCount", "Major Tickmark Count" );
|
||||
|
||||
CAF_PDM_InitField( &m_useCommonLegend,
|
||||
"UseCommonLegend",
|
||||
false,
|
||||
"Use Common Legend",
|
||||
"",
|
||||
"Show one legend above the sub plots, merging identical entries. Useful when all sub plots share the same legend "
|
||||
"entries." );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_subTitleFontSize, "SubTitleFontSize", "Sub Plot Title Font Size" );
|
||||
m_subTitleFontSize = caf::FontTools::RelativeSize::Large;
|
||||
|
||||
@@ -119,6 +127,7 @@ RimMultiPlot& RimMultiPlot::operator=( RimMultiPlot&& rhs )
|
||||
m_rowsPerPage = rhs.m_rowsPerPage;
|
||||
m_showIndividualPlotTitles = rhs.m_showIndividualPlotTitles;
|
||||
m_subTitleFontSize = rhs.m_subTitleFontSize;
|
||||
m_useCommonLegend = rhs.m_useCommonLegend;
|
||||
m_pagePreviewMode = rhs.m_pagePreviewMode;
|
||||
|
||||
return *this;
|
||||
@@ -527,6 +536,14 @@ bool RimMultiPlot::showPlotTitles() const
|
||||
return m_showIndividualPlotTitles;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimMultiPlot::useCommonLegend() const
|
||||
{
|
||||
return m_useCommonLegend();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -706,7 +723,7 @@ void RimMultiPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField, co
|
||||
{
|
||||
RimPlotWindow::fieldChangedByUi( changedField, oldValue, newValue );
|
||||
|
||||
if ( changedField == &m_showIndividualPlotTitles )
|
||||
if ( changedField == &m_showIndividualPlotTitles || changedField == &m_useCommonLegend )
|
||||
{
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ public:
|
||||
|
||||
void setShowPlotTitles( bool enable );
|
||||
bool showPlotTitles() const;
|
||||
bool useCommonLegend() const;
|
||||
|
||||
void zoomAll() override;
|
||||
void zoomAllYAxes();
|
||||
@@ -166,6 +167,7 @@ protected:
|
||||
caf::PdmField<ColumnCountEnum> m_columnCount;
|
||||
caf::PdmField<RowCountEnum> m_rowsPerPage;
|
||||
caf::PdmField<bool> m_showIndividualPlotTitles;
|
||||
caf::PdmField<bool> m_useCommonLegend;
|
||||
RimFontSizeField m_subTitleFontSize;
|
||||
RimFontSizeField m_axisTitleFontSize;
|
||||
RimFontSizeField m_axisValueFontSize;
|
||||
|
||||
@@ -447,6 +447,7 @@ void RimSummaryMultiPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrde
|
||||
legendsGroup->add( &m_showPlotLegends );
|
||||
legendsGroup->add( &m_plotLegendsHorizontal );
|
||||
legendsGroup->add( &m_legendPosition );
|
||||
legendsGroup->add( &m_useCommonLegend );
|
||||
legendsGroup->add( &m_legendFontSize );
|
||||
|
||||
uiOrdering.skipRemainingFields( true );
|
||||
|
||||
@@ -98,3 +98,11 @@ TEST( RicWellPathExportCompletionDataFeatureImpl, CompletionNumberIgnoresContrib
|
||||
EXPECT_EQ( 4, numberForAllOrders( { makeCompletion( 100.0, 110.0, std::nullopt ), makeCompletion( 110.0, 111.0, 4 ) } ) );
|
||||
EXPECT_EQ( std::nullopt, numberForAllOrders( { makeCompletion( 100.0, 110.0, std::nullopt ) } ) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicWellPathExportCompletionDataFeatureImpl, CompletionNumberEmptyCompletionsGivesNoNumber )
|
||||
{
|
||||
EXPECT_EQ( std::nullopt, RicWellPathExportCompletionDataFeatureImpl::completionNumberForCell( {} ) );
|
||||
}
|
||||
@@ -641,6 +641,7 @@ const QList<QPointer<RiuMultiPlotPage>>& RiuMultiPlotBook::pages() const
|
||||
RiuMultiPlotPage* RiuMultiPlotBook::createPage()
|
||||
{
|
||||
RiuMultiPlotPage* page = new RiuMultiPlotPage( m_plotDefinition, this );
|
||||
page->setHostsCommonLegend( m_pages.isEmpty() );
|
||||
|
||||
applyPageSettings( page );
|
||||
|
||||
|
||||
@@ -68,6 +68,15 @@
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
|
||||
namespace
|
||||
{
|
||||
bool useCommonLegend( const RimPlotWindow* plotWindow )
|
||||
{
|
||||
auto multiPlot = dynamic_cast<const RimMultiPlot*>( plotWindow );
|
||||
return multiPlot && multiPlot->useCommonLegend();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -357,6 +366,22 @@ void RiuMultiPlotPage::scheduleReplotOfAllPlots()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RiuMultiPlotPage::setHostsCommonLegend( bool hostsCommonLegend )
|
||||
{
|
||||
m_hostsCommonLegend = hostsCommonLegend;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// With a common legend, the legends of the individual sub plots are not displayed
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RiuMultiPlotPage::showSubPlotLegends() const
|
||||
{
|
||||
return !useCommonLegend( m_plotDefinition );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -404,13 +429,20 @@ void RiuMultiPlotPage::renderTo( QPainter* painter, double scalingFactor )
|
||||
|
||||
for ( auto legend : legendsForVisiblePlots() )
|
||||
{
|
||||
if ( legend )
|
||||
if ( legend && !legend->isHidden() )
|
||||
{
|
||||
QPoint renderOffset = m_plotWidgetFrame->mapToParent( legend->frameGeometry().topLeft() ) - marginOffset;
|
||||
legend->render( painter, renderOffset );
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_commonLegend && !m_commonLegend->isHidden() )
|
||||
{
|
||||
// The common legend is a direct child of the page, not of the plot widget frame
|
||||
QPoint renderOffset = m_commonLegend->frameGeometry().topLeft() - marginOffset;
|
||||
m_commonLegend->render( painter, renderOffset );
|
||||
}
|
||||
|
||||
for ( auto plotWidget : visiblePlotWidgets() )
|
||||
{
|
||||
QRect plotWidgetGeometry = plotWidget->frameGeometry();
|
||||
@@ -611,9 +643,19 @@ void RiuMultiPlotPage::onLegendUpdated()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RiuMultiPlotPage::refreshLegends()
|
||||
{
|
||||
// The common legend is rebuilt from the sub plot legend data. A full layout rebuild here could trigger new legend
|
||||
// data changes in the plots, and thereby an endless update loop
|
||||
if ( useCommonLegend( m_plotDefinition ) ) updateCommonLegend();
|
||||
|
||||
QList<QPointer<RiuQwtPlotLegend>> legends = legendsForVisiblePlots();
|
||||
for ( const auto& l : legends )
|
||||
{
|
||||
if ( !l ) continue;
|
||||
if ( !showSubPlotLegends() )
|
||||
{
|
||||
l->hide();
|
||||
continue;
|
||||
}
|
||||
l->setVisible( !l->isEmpty() );
|
||||
updateLegendFont( l );
|
||||
}
|
||||
@@ -668,6 +710,67 @@ void RiuMultiPlotPage::reinsertPlotWidgets()
|
||||
reinsertPlotWidget( plotWidget, legend, legendFrame, subTitle, row, column, rowSpan, colSpan );
|
||||
}
|
||||
}
|
||||
|
||||
updateCommonLegend();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Build one legend from the legend data of all sub plots. Entries with identical title and icon are merged.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RiuMultiPlotPage::updateCommonLegend()
|
||||
{
|
||||
if ( m_commonLegend ) m_layout->removeWidget( m_commonLegend );
|
||||
|
||||
bool isActive = useCommonLegend( m_plotDefinition ) && m_hostsCommonLegend && m_plotDefinition->legendsVisible();
|
||||
if ( !isActive )
|
||||
{
|
||||
if ( m_commonLegend ) m_commonLegend->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !m_commonLegend )
|
||||
{
|
||||
m_commonLegend = new RiuQwtPlotLegend( this );
|
||||
m_commonLegend->horizontalScrollBar()->setVisible( false );
|
||||
m_commonLegend->verticalScrollBar()->setVisible( false );
|
||||
m_commonLegend->setDefaultItemMode( QwtLegendData::ReadOnly );
|
||||
m_commonLegend->contentsWidget()->layout()->setAlignment( Qt::AlignBottom | Qt::AlignHCenter );
|
||||
m_commonLegend->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < m_commonLegendItemCount; i++ )
|
||||
{
|
||||
m_commonLegend->updateLegend( QVariant( i ), {} );
|
||||
}
|
||||
m_commonLegendItemCount = 0;
|
||||
|
||||
std::vector<QwtPlot*> qwtPlots;
|
||||
for ( auto plotWidget : visiblePlotWidgets() )
|
||||
{
|
||||
if ( plotWidget->plotDefinition() && !plotWidget->plotDefinition()->legendsVisible() ) continue;
|
||||
|
||||
if ( auto qwtPlotWidget = dynamic_cast<RiuQwtPlotWidget*>( plotWidget.data() ) )
|
||||
{
|
||||
qwtPlots.push_back( qwtPlotWidget->qwtPlot() );
|
||||
}
|
||||
}
|
||||
|
||||
for ( const auto& legendData : RiuQwtPlotTools::uniqueLegendData( qwtPlots ) )
|
||||
{
|
||||
m_commonLegend->updateLegend( QVariant( m_commonLegendItemCount++ ), legendData );
|
||||
}
|
||||
|
||||
if ( m_commonLegendItemCount == 0 )
|
||||
{
|
||||
m_commonLegend->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// The common legend is placed below the title, centered above all sub plots
|
||||
m_layout->insertWidget( 1, m_commonLegend, 0, Qt::AlignHCenter );
|
||||
updateLegendColumns( m_commonLegend );
|
||||
updateLegendFont( m_commonLegend );
|
||||
m_commonLegend->show();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -682,6 +785,17 @@ void RiuMultiPlotPage::reinsertPlotWidget( RiuPlotWidget* plotWidget,
|
||||
int rowSpan,
|
||||
int colSpan )
|
||||
{
|
||||
if ( !showSubPlotLegends() )
|
||||
{
|
||||
if ( legend ) legend->hide();
|
||||
if ( legendFrame )
|
||||
{
|
||||
legendFrame->hide();
|
||||
plotWidget->removeOverlayFrame( legendFrame );
|
||||
}
|
||||
legend = nullptr;
|
||||
}
|
||||
|
||||
m_gridLayout->addWidget( subTitle, 3 * row, column, 1, colSpan );
|
||||
|
||||
addLegendWidget( plotWidget, legend, legendFrame, 3 * row + 1, column, colSpan );
|
||||
|
||||
@@ -82,6 +82,7 @@ public:
|
||||
void setPagePreviewModeEnabled( bool previewMode );
|
||||
|
||||
void setAutoAlignAxes( bool autoAlignAxes );
|
||||
void setHostsCommonLegend( bool hostsCommonLegend );
|
||||
|
||||
void scheduleUpdate( RiaDefines::MultiPlotPageUpdateType whatToUpdate = RiaDefines::MultiPlotPageUpdateType::ALL );
|
||||
void scheduleReplotOfAllPlots();
|
||||
@@ -181,6 +182,14 @@ protected:
|
||||
bool m_previewMode;
|
||||
bool m_showSubTitles;
|
||||
bool m_autoAlignAxes;
|
||||
bool m_hostsCommonLegend = true;
|
||||
|
||||
bool showSubPlotLegends() const;
|
||||
void updateCommonLegend();
|
||||
|
||||
// Owned by Qt as a child widget of this page; QPointer only observes it
|
||||
QPointer<RiuQwtPlotLegend> m_commonLegend;
|
||||
int m_commonLegendItemCount = 0;
|
||||
|
||||
std::map<RiuQwtPlotLegend*, int> m_childCountForAdjustSizeOperation;
|
||||
|
||||
|
||||
@@ -36,9 +36,15 @@
|
||||
#include "qwt_painter.h"
|
||||
#include "qwt_plot.h"
|
||||
#include "qwt_plot_grid.h"
|
||||
#include "qwt_plot_item.h"
|
||||
#include "qwt_plot_layout.h"
|
||||
#include "qwt_plot_shapeitem.h"
|
||||
#include "qwt_scale_widget.h"
|
||||
#include "qwt_text.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QImage>
|
||||
#include <QSet>
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -264,6 +270,46 @@ void RiuQwtPlotTools::updateLegendData( RiuQwtPlotLegend* legend, const std::vec
|
||||
legend->updateLegend( QVariant(), legendDataList );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Collect legend data, one entry per legend item, from the plots. Items with identical title and icon are merged.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QList<QList<QwtLegendData>> RiuQwtPlotTools::uniqueLegendData( const std::vector<QwtPlot*>& plots )
|
||||
{
|
||||
QList<QList<QwtLegendData>> uniqueData;
|
||||
QSet<QString> keys;
|
||||
|
||||
for ( QwtPlot* plot : plots )
|
||||
{
|
||||
if ( !plot ) continue;
|
||||
|
||||
for ( QwtPlotItem* item : plot->itemList() )
|
||||
{
|
||||
if ( !item->testItemAttribute( QwtPlotItem::Legend ) ) continue;
|
||||
|
||||
QList<QwtLegendData> legendData = item->legendData();
|
||||
if ( legendData.isEmpty() ) continue;
|
||||
|
||||
QString title = legendData.first().value( QwtLegendData::TitleRole ).value<QwtText>().text();
|
||||
|
||||
// Include the icon in the key, so entries with the same title but different color or style are all kept
|
||||
QString key = title;
|
||||
auto icon = legendData.first().value( QwtLegendData::IconRole ).value<QwtGraphic>();
|
||||
if ( !icon.isNull() )
|
||||
{
|
||||
QImage image = icon.toImage( QSize( 32, 16 ).expandedTo( icon.defaultSize().toSize() ) );
|
||||
QByteArray bytes( reinterpret_cast<const char*>( image.constBits() ), image.sizeInBytes() );
|
||||
key += "|" + QString::fromLatin1( QCryptographicHash::hash( bytes, QCryptographicHash::Md5 ).toHex() );
|
||||
}
|
||||
if ( keys.contains( key ) ) continue;
|
||||
|
||||
keys.insert( key );
|
||||
uniqueData.append( legendData );
|
||||
}
|
||||
}
|
||||
|
||||
return uniqueData;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -65,9 +65,10 @@ public:
|
||||
QColor color,
|
||||
Qt::BrushStyle brushStyle = Qt::SolidPattern );
|
||||
|
||||
static void updateLegendData( RiuQwtPlotLegend* legend, const std::vector<RimPlotCurve*>& curves );
|
||||
static QList<QwtLegendData> createLegendData( const std::vector<RimPlotCurve*>& curves );
|
||||
static QwtAxis::Position toQwtPlotAxisEnum( RiaDefines::PlotAxis riaPlotAxis );
|
||||
static void updateLegendData( RiuQwtPlotLegend* legend, const std::vector<RimPlotCurve*>& curves );
|
||||
static QList<QwtLegendData> createLegendData( const std::vector<RimPlotCurve*>& curves );
|
||||
static QList<QList<QwtLegendData>> uniqueLegendData( const std::vector<QwtPlot*>& plots );
|
||||
static QwtAxis::Position toQwtPlotAxisEnum( RiaDefines::PlotAxis riaPlotAxis );
|
||||
|
||||
static RiaDefines::PlotAxis fromQwtPlotAxis( QwtAxis::Position );
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ RiuSummaryMultiPlotPage* RiuSummaryMultiPlotBook::createSummaryPage()
|
||||
{
|
||||
RimSummaryMultiPlot* sumMultPlot = dynamic_cast<RimSummaryMultiPlot*>( m_plotDefinition.p() );
|
||||
RiuSummaryMultiPlotPage* page = new RiuSummaryMultiPlotPage( sumMultPlot, this );
|
||||
page->setHostsCommonLegend( m_pages.isEmpty() );
|
||||
|
||||
applyPageSettings( page );
|
||||
|
||||
|
||||
@@ -114,6 +114,8 @@ void RiuSummaryMultiPlotPage::reinsertPlotWidgets()
|
||||
column += colSpan - 1;
|
||||
}
|
||||
}
|
||||
|
||||
updateCommonLegend();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user