#8662 QtCharts: Implement tooltip when hovering over curves

This commit is contained in:
Kristian Bendiksen 2022-03-11 16:02:30 +01:00
parent 560917a481
commit c00aea75fb
14 changed files with 365 additions and 56 deletions

View File

@ -45,6 +45,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimSummaryAddressCollection.h
${CMAKE_CURRENT_LIST_DIR}/RimSummaryMultiPlotCollection.h
${CMAKE_CURRENT_LIST_DIR}/RimSummaryPlotControls.h
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveInfoTextProvider.h
)
set(SOURCE_GROUP_SOURCE_FILES
@ -94,6 +95,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimSummaryAddressCollection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimSummaryMultiPlotCollection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimSummaryPlotControls.cpp
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveInfoTextProvider.cpp
)
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})

View File

@ -0,0 +1,39 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2022 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 "RimEnsembleCurveInfoTextProvider.h"
#include "RimSummaryCase.h"
#include "RimSummaryCurve.h"
#include "RiuPlotCurve.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimEnsembleCurveInfoTextProvider::curveInfoText( RiuPlotCurve* riuCurve ) const
{
// RiuPlotCurve* riuCurve = dynamic_cast<RiuPlotCurve*>( curve );
RimSummaryCurve* sumCurve = nullptr;
if ( riuCurve )
{
sumCurve = dynamic_cast<RimSummaryCurve*>( riuCurve->ownerRimCurve() );
}
return sumCurve && sumCurve->summaryCaseY() ? sumCurve->summaryCaseY()->displayCaseName() : "";
}

View File

@ -0,0 +1,32 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2022 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 "RiuPlotCurveInfoTextProvider.h"
#include <QString>
//==================================================================================================
//
//==================================================================================================
class RimEnsembleCurveInfoTextProvider : public RiuPlotCurveInfoTextProvider
{
public:
QString curveInfoText( RiuPlotCurve* ) const override;
};

View File

@ -214,6 +214,7 @@ if(RESINSIGHT_USE_QT_CHARTS)
${CMAKE_CURRENT_LIST_DIR}/RiuQtChartsPlotWidget.h
${CMAKE_CURRENT_LIST_DIR}/RiuQtChartsPlotTools.h
${CMAKE_CURRENT_LIST_DIR}/RiuQtChartsPlotCurveSymbol.h
${CMAKE_CURRENT_LIST_DIR}/RiuQtChartsToolTip.h
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryQtChartsPlot.h
)
@ -225,6 +226,7 @@ if(RESINSIGHT_USE_QT_CHARTS)
${CMAKE_CURRENT_LIST_DIR}/RiuQtChartsPlotWidget.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuQtChartsPlotTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuQtChartsPlotCurveSymbol.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuQtChartsToolTip.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryQtChartsPlot.cpp
)

View File

@ -0,0 +1,33 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015- Equinor ASA
// Copyright (C) 2015- Ceetron Solutions AS
//
// 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>
class RiuPlotCurve;
//--------------------------------------------------------------------------------------------------
/// Interface for retrieving curve info text
//--------------------------------------------------------------------------------------------------
class RiuPlotCurveInfoTextProvider
{
public:
virtual QString curveInfoText( RiuPlotCurve* curve ) const = 0;
};

View File

@ -27,10 +27,12 @@
#include "RiuDraggableOverlayFrame.h"
#include "RiuGuiTheme.h"
#include "RiuPlotCurveInfoTextProvider.h"
#include "RiuPlotMainWindowTools.h"
#include "RiuPlotWidget.h"
#include "RiuQtChartView.h"
#include "RiuQtChartsPlotCurve.h"
#include "RiuQtChartsToolTip.h"
#include "RiuQwtDateScaleWrapper.h"
#include "caf.h"
@ -52,9 +54,13 @@ using namespace QtCharts;
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuQtChartsPlotWidget::RiuQtChartsPlotWidget( RimPlot* plotDefinition, QWidget* parent )
RiuQtChartsPlotWidget::RiuQtChartsPlotWidget( RimPlot* plotDefinition,
QWidget* parent,
RiuPlotCurveInfoTextProvider* plotCurveNameProvider )
: RiuPlotWidget( plotDefinition, parent )
, m_plotCurveNameProvider( plotCurveNameProvider )
, m_dateScaleWrapper( new RiuQwtDateScaleWrapper() )
, m_toolTip( nullptr )
{
CAF_ASSERT( m_plotDefinition );
@ -853,6 +859,8 @@ void RiuQtChartsPlotWidget::attach( RiuPlotCurve* plotCurve,
addToChart( m_lineSeriesMap, plotCurve, lineSeries, xAxis, yAxis, qtChartsPlotCurve );
addToChart( m_areaSeriesMap, plotCurve, areaSeries, xAxis, yAxis, qtChartsPlotCurve );
addToChart( m_scatterSeriesMap, plotCurve, scatterSeries, xAxis, yAxis, qtChartsPlotCurve );
connect( dynamic_cast<QLineSeries*>( lineSeries ), &QLineSeries::hovered, this, &RiuQtChartsPlotWidget::tooltip );
}
//--------------------------------------------------------------------------------------------------
@ -1234,3 +1242,54 @@ void RiuQtChartsPlotWidget::pruneAxes( const std::set<RiuPlotAxis>& usedAxes )
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotWidget::tooltip( const QPointF& point, bool state )
{
QAbstractSeries* series = qobject_cast<QAbstractSeries*>( sender() );
if ( !m_toolTip ) m_toolTip = new RiuQtChartsToolTip( qtChart(), series );
if ( state )
{
QString nameFromSeries = createNameFromSeries( series );
QDateTime date = QDateTime::fromMSecsSinceEpoch( point.x() );
QString dateString = RiaQDateTimeTools::toStringUsingApplicationLocale( date, "hh:mm dd.MMMM.yyyy" );
QString text = QString( "%1 (%2)" ).arg( point.y() ).arg( dateString );
if ( !nameFromSeries.isEmpty() ) text.prepend( nameFromSeries + ": " );
m_toolTip->setText( text );
m_toolTip->setAnchor( point );
m_toolTip->setSeries( series );
m_toolTip->setZValue( 200 );
m_toolTip->updateGeometry();
m_toolTip->show();
}
else
{
m_toolTip->hide();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiuQtChartsPlotWidget::createNameFromSeries( QAbstractSeries* series ) const
{
if ( !m_plotCurveNameProvider ) return "";
for ( auto [plotCurve, plotSeries] : m_lineSeriesMap )
{
if ( plotSeries == series )
{
return m_plotCurveNameProvider->curveInfoText( plotCurve );
}
}
return "";
}

View File

@ -36,6 +36,9 @@ class RiaPlotWindowRedrawScheduler;
class RimPlot;
class RiuPlotCurve;
class RiuQtChartsPlotCurve;
class RiuQtChartsToolTip;
class RiuPlotCurveSymbol;
class RiuPlotCurveInfoTextProvider;
class QEvent;
class QLabel;
@ -64,7 +67,9 @@ class RiuQtChartsPlotWidget : public RiuPlotWidget
Q_OBJECT
public:
RiuQtChartsPlotWidget( RimPlot* plotDefinition, QWidget* parent = nullptr );
RiuQtChartsPlotWidget( RimPlot* plotDefinition,
QWidget* parent = nullptr,
RiuPlotCurveInfoTextProvider* plotCurveNameProvider = nullptr );
~RiuQtChartsPlotWidget() override;
int axisTitleFontSize( RiuPlotAxis axis ) const override;
@ -217,6 +222,7 @@ signals:
private slots:
void axisRangeChanged();
void tooltip( const QPointF& point, bool state );
private:
void addAxis( RiuPlotAxis plotAxis, bool isEnabled, bool isAutoScale );
@ -229,6 +235,8 @@ private:
QtCharts::QCategoryAxis* categoryAxis();
QString createNameFromSeries( QtCharts::QAbstractSeries* series ) const;
private:
QPointer<QtCharts::QChartView> m_viewer;
@ -241,4 +249,6 @@ private:
std::map<RiuPlotCurve*, QtCharts::QAbstractSeries*> m_scatterSeriesMap;
RiuQwtDateScaleWrapper* m_dateScaleWrapper;
RiuQtChartsToolTip* m_toolTip;
RiuPlotCurveInfoTextProvider* m_plotCurveNameProvider;
};

View File

@ -0,0 +1,112 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2022- 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 "RiuQtChartsToolTip.h"
#include <QtCharts/QChart>
#include <QtGui/QFontMetrics>
#include <QtGui/QMouseEvent>
#include <QtGui/QPainter>
#include <QtWidgets/QGraphicsSceneMouseEvent>
using namespace QtCharts;
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuQtChartsToolTip::RiuQtChartsToolTip( QChart* chart, QtCharts::QAbstractSeries* series )
: QGraphicsItem( chart )
, m_chart( chart )
, m_series( series )
, m_radius( 10 )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QRectF RiuQtChartsToolTip::boundingRect() const
{
QPointF anchor = mapFromParent( m_chart->mapToPosition( m_anchor, m_series ) );
QRectF rect = m_rect.united( m_textRect );
rect.setLeft( std::min( rect.left(), anchor.x() ) );
rect.setRight( std::max( rect.right(), anchor.x() ) );
rect.setTop( std::min( rect.top(), anchor.y() ) );
rect.setBottom( std::max( rect.bottom(), anchor.y() ) );
return rect;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsToolTip::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget )
{
Q_UNUSED( option )
Q_UNUSED( widget )
QPainterPath path;
QPointF anchor = mapFromParent( m_chart->mapToPosition( m_anchor, m_series ) );
path.addEllipse( anchor, m_radius, m_radius );
path = path.simplified();
painter->setPen( QPen( Qt::black ) );
painter->drawPath( path );
painter->drawText( m_textRect, m_text );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsToolTip::setSeries( QAbstractSeries* series )
{
m_series = series;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsToolTip::setText( const QString& text )
{
m_text = text;
QFontMetrics metrics( m_font );
m_textRect = metrics.boundingRect( QRect( 0, 0, 150, 150 ), Qt::AlignLeft, m_text );
m_textRect.translate( m_radius, 0 );
prepareGeometryChange();
m_rect.setRect( -m_radius, -m_radius, m_radius, m_radius );
m_rect.moveCenter( QPoint( m_radius, m_radius ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsToolTip::setAnchor( QPointF point )
{
m_anchor = point;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsToolTip::updateGeometry()
{
prepareGeometryChange();
setPos( m_chart->mapToPosition( m_anchor, m_series ) );
}

View File

@ -0,0 +1,48 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2022- 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 <QtCharts/QChart>
#include <QtCharts/QChartGlobal>
#include <QtGui/QFont>
#include <QtWidgets/QGraphicsItem>
class RiuQtChartsToolTip : public QGraphicsItem
{
public:
RiuQtChartsToolTip( QtCharts::QChart* parent, QtCharts::QAbstractSeries* series );
void setSeries( QtCharts::QAbstractSeries* series );
void setText( const QString& text );
void setAnchor( QPointF point );
void updateGeometry();
QRectF boundingRect() const override;
void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget ) override;
private:
QString m_text;
QRectF m_textRect;
QRectF m_rect;
QPointF m_anchor;
QFont m_font;
int m_radius;
QtCharts::QChart* m_chart;
QtCharts::QAbstractSeries* m_series;
};

View File

@ -20,12 +20,13 @@
#include "RiaQDateTimeTools.h"
#include "RiuGuiTheme.h"
#include "qwt_plot_marker.h"
#include "qwt_symbol.h"
#include "RiuPlotCurve.h"
#include "RiuPlotCurveInfoTextProvider.h"
#include "qwt_date_scale_draw.h"
#include "qwt_plot_curve.h"
#include "qwt_plot_marker.h"
#include "qwt_symbol.h"
#include "qwt_text.h"
#include <cfloat> // For DBL_MAX
@ -37,7 +38,7 @@
//--------------------------------------------------------------------------------------------------
RiuQwtCurvePointTracker::RiuQwtCurvePointTracker( QwtPlot* plot,
bool isMainAxisHorizontal,
IPlotCurveInfoTextProvider* curveInfoTextProvider )
RiuPlotCurveInfoTextProvider* curveInfoTextProvider )
: QwtPlotPicker( plot->canvas() )
, m_plot( plot )
, m_isMainAxisHorizontal( isMainAxisHorizontal )
@ -187,7 +188,7 @@ QPointF RiuQwtCurvePointTracker::closestCurvePoint( const QPoint& cursorPositio
if ( curveInfoText && closestCurve && m_curveInfoTextProvider )
{
*curveInfoText = m_curveInfoTextProvider->curveInfoText( closestCurve );
*curveInfoText = m_curveInfoTextProvider->curveInfoText( dynamic_cast<RiuPlotCurve*>( closestCurve ) );
}
if ( dateScaleDraw )

View File

@ -24,7 +24,7 @@
class QwtPlotMarker;
class QwtPlotCurve;
class IPlotCurveInfoTextProvider;
class RiuPlotCurveInfoTextProvider;
//--------------------------------------------------------------------------------------------------
/// Class to add mouse over-tracking of curve points with text marker
@ -34,7 +34,7 @@ class RiuQwtCurvePointTracker : public QwtPlotPicker
public:
explicit RiuQwtCurvePointTracker( QwtPlot* plot,
bool isMainAxisHorizontal,
IPlotCurveInfoTextProvider* curveInfoTextProvider = nullptr );
RiuPlotCurveInfoTextProvider* curveInfoTextProvider = nullptr );
~RiuQwtCurvePointTracker() override;
protected:
@ -55,14 +55,5 @@ protected:
QPointer<QwtPlot> m_plot;
QwtPlotMarker* m_plotMarker;
bool m_isMainAxisHorizontal;
IPlotCurveInfoTextProvider* m_curveInfoTextProvider;
};
//--------------------------------------------------------------------------------------------------
/// Interface for retrieving curve info text
//--------------------------------------------------------------------------------------------------
class IPlotCurveInfoTextProvider
{
public:
virtual QString curveInfoText( QwtPlotCurve* curve ) = 0;
RiuPlotCurveInfoTextProvider* m_curveInfoTextProvider;
};

View File

@ -20,6 +20,7 @@
#include "RiaPreferences.h"
#include "RimEnsembleCurveInfoTextProvider.h"
#include "RimSummaryPlot.h"
#include "RiuPlotCurve.h"
@ -32,7 +33,7 @@
RiuSummaryQtChartsPlot::RiuSummaryQtChartsPlot( RimSummaryPlot* plot )
: RiuSummaryPlot( plot )
{
m_plotWidget = new RiuQtChartsPlotWidget( plot );
m_plotWidget = new RiuQtChartsPlotWidget( plot, nullptr, new RimEnsembleCurveInfoTextProvider );
m_plotWidget->setContextMenuPolicy( Qt::CustomContextMenu );
connect( m_plotWidget, SIGNAL( customContextMenuRequested( QPoint ) ), this, SLOT( showContextMenu( QPoint ) ) );
@ -43,7 +44,6 @@ RiuSummaryQtChartsPlot::RiuSummaryQtChartsPlot( RimSummaryPlot* plot )
m_plotWidget->setInternalLegendVisible( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -23,6 +23,7 @@
#include "Commands/CorrelationPlotCommands/RicNewCorrelationPlotFeature.h"
#include "RimEnsembleCurveInfoTextProvider.h"
#include "RimPlotAxisAnnotation.h"
#include "RimPlotAxisProperties.h"
#include "RimPlotAxisPropertiesInterface.h"
@ -67,28 +68,7 @@
#include <limits>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
class EnsembleCurveInfoTextProvider : public IPlotCurveInfoTextProvider
{
public:
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString curveInfoText( QwtPlotCurve* curve ) override
{
RiuPlotCurve* riuCurve = dynamic_cast<RiuPlotCurve*>( curve );
RimSummaryCurve* sumCurve = nullptr;
if ( riuCurve )
{
sumCurve = dynamic_cast<RimSummaryCurve*>( riuCurve->ownerRimCurve() );
}
return sumCurve && sumCurve->summaryCaseY() ? sumCurve->summaryCaseY()->displayCaseName() : "";
}
};
static EnsembleCurveInfoTextProvider ensembleCurveInfoTextProvider;
static RimEnsembleCurveInfoTextProvider ensembleCurveInfoTextProvider;
//--------------------------------------------------------------------------------------------------
///

View File

@ -27,6 +27,7 @@
#include "RiuGuiTheme.h"
#include "RiuPlotCurve.h"
#include "RiuPlotCurveInfoTextProvider.h"
#include "RiuQwtCurvePointTracker.h"
#include "RiuQwtPlotTools.h"
@ -41,7 +42,7 @@
class RiuWellLogCurvePointTracker : public RiuQwtCurvePointTracker
{
public:
RiuWellLogCurvePointTracker( QwtPlot* plot, IPlotCurveInfoTextProvider* curveInfoTextProvider, RimWellLogTrack* track )
RiuWellLogCurvePointTracker( QwtPlot* plot, RiuPlotCurveInfoTextProvider* curveInfoTextProvider, RimWellLogTrack* track )
: RiuQwtCurvePointTracker( plot, false, curveInfoTextProvider )
, m_wellLogTrack( track )
{
@ -105,15 +106,14 @@ private:
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
class WellLogCurveInfoTextProvider : public IPlotCurveInfoTextProvider
class WellLogCurveInfoTextProvider : public RiuPlotCurveInfoTextProvider
{
public:
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString curveInfoText( QwtPlotCurve* curve ) override
QString curveInfoText( RiuPlotCurve* riuCurve ) const override
{
RiuPlotCurve* riuCurve = dynamic_cast<RiuPlotCurve*>( curve );
RimWellLogCurve* wlCurve = nullptr;
if ( riuCurve )
{