Files
ResInsight/ApplicationLibCode/UserInterface/RiuQwtPlotWidget.cpp
Magne Sjaastad bbd79cca6a #12168 Allow highlight of curves based on selection of realization object
Use the first available highlighted curve as basis for display of horizontal readout value. Fallback to single realization curves.
When selecting a realization in project tree, highlight all related curves
Make sure zoom rect works when readout annotations are active
2025-02-18 11:04:40 +01:00

1625 lines
63 KiB
C++

////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015- Statoil 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.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RiuQwtPlotWidget.h"
#include "RiaColorTools.h"
#include "RiaDefines.h"
#include "RiaGuiApplication.h"
#include "RiaLogging.h"
#include "RiaPlotDefines.h"
#include "RiaPlotWindowRedrawScheduler.h"
#include "RimPlot.h"
#include "RimPlotCurve.h"
#include "RimSummaryEnsembleTools.h"
#include "RiuDraggableOverlayFrame.h"
#include "RiuGuiTheme.h"
#include "RiuPlotAxis.h"
#include "RiuPlotMainWindowTools.h"
#include "RiuPlotWidget.h"
#include "RiuQwtCurvePointTracker.h"
#include "RiuQwtLinearScaleEngine.h"
#include "RiuQwtPlotCurve.h"
#include "RiuQwtPlotItem.h"
#include "RiuQwtPlotTools.h"
#include "RiuQwtScalePicker.h"
#include "cafAssert.h"
#include "qwt_axis.h"
#include "qwt_date_scale_engine.h"
#include "qwt_legend.h"
#include "qwt_legend_label.h"
#include "qwt_plot_barchart.h"
#include "qwt_plot_canvas.h"
#include "qwt_plot_curve.h"
#include "qwt_plot_grid.h"
#include "qwt_plot_layout.h"
#include "qwt_plot_marker.h"
#include "qwt_plot_renderer.h"
#include "qwt_plot_shapeitem.h"
#include "qwt_scale_draw.h"
#include "qwt_scale_engine.h"
#include "qwt_scale_widget.h"
#include "qwt_symbol.h"
#include "qwt_text.h"
#include "qwt_text_label.h"
#include <QFont>
#include <QFontMetrics>
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QVBoxLayout>
#include <QWheelEvent>
#include <algorithm>
#include <limits>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuQwtPlotWidget::RiuQwtPlotWidget( RimPlot* plotDefinition, QWidget* parent )
: RiuPlotWidget( plotDefinition, parent )
, m_titleRenderingFlags( Qt::AlignHCenter | Qt::TextWordWrap )
, m_titleFontSize( -1 )
{
auto* layout = new QVBoxLayout;
layout->setContentsMargins( 0, 0, 0, 0 );
setLayout( layout );
m_plot = new QwtPlot( this );
m_plot->setAcceptDrops( true );
layout->addWidget( m_plot );
RiuQwtPlotTools::setCommonPlotBehaviour( m_plot );
m_plot->installEventFilter( this );
m_plot->canvas()->installEventFilter( this );
setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
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() );
ensureAxisIsCreated( RiuPlotAxis::defaultRight() );
ensureAxisIsCreated( RiuPlotAxis::defaultTop() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuQwtPlotWidget::~RiuQwtPlotWidget()
{
if ( m_plotDefinition )
{
m_plotDefinition->detachAllCurves();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiuQwtPlotWidget::axisTitleFontSize( RiuPlotAxis axis ) const
{
if ( axisEnabled( axis ) )
{
return m_plot->axisFont( toQwtPlotAxis( axis ) ).pointSize();
}
return -1;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiuQwtPlotWidget::axisValueFontSize( RiuPlotAxis axis ) const
{
if ( axisEnabled( axis ) )
{
return m_plot->axisTitle( toQwtPlotAxis( axis ) ).font().pointSize();
}
return -1;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisFontsAndAlignment( RiuPlotAxis axis, int titleFontSize, int valueFontSize, bool titleBold, int alignment )
{
int titleFontPixelSize = caf::FontTools::pointSizeToPixelSize( titleFontSize );
int valueFontPixelSize = caf::FontTools::pointSizeToPixelSize( valueFontSize );
// Axis number font
auto qwtAxis = toQwtPlotAxis( axis );
QFont axisFont = m_plot->axisFont( qwtAxis );
axisFont.setPixelSize( valueFontPixelSize );
axisFont.setBold( false );
m_plot->setAxisFont( qwtAxis, axisFont );
// Axis title font
QwtText axisTitle = m_plot->axisTitle( qwtAxis );
QFont axisTitleFont = axisTitle.font();
axisTitleFont.setPixelSize( titleFontPixelSize );
axisTitleFont.setBold( titleBold );
axisTitle.setFont( axisTitleFont );
axisTitle.setRenderFlags( alignment | Qt::TextWordWrap );
m_plot->setAxisTitle( qwtAxis, axisTitle );
applyAxisTitleToQwt( axis );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxesFontsAndAlignment( int titleFontSize, int valueFontSize, bool titleBold, int alignment )
{
for ( const auto& axisTitlePair : m_axisTitles )
{
setAxisFontsAndAlignment( axisTitlePair.first, titleFontSize, valueFontSize, titleBold, alignment );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisTitleText( RiuPlotAxis axis, const QString& title )
{
m_axisTitles[axis] = title;
applyAxisTitleToQwt( axis );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisTitleEnabled( RiuPlotAxis axis, bool enable )
{
m_axisTitlesEnabled[axis] = enable;
applyAxisTitleToQwt( axis );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setPlotTitle( const QString& plotTitle )
{
m_plotTitleText = plotTitle;
applyPlotTitleToQwt();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const QString& RiuQwtPlotWidget::plotTitle() const
{
return m_plotTitleText;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setPlotTitleEnabled( bool enabled )
{
m_plotTitleEnabled = enabled;
applyPlotTitleToQwt();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiuQwtPlotWidget::plotTitleEnabled() const
{
return m_plotTitleEnabled;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setPlotTitleFontSize( int titleFontSize )
{
m_titleFontSize = titleFontSize;
applyPlotTitleToQwt();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setLegendFontSize( int fontSize )
{
if ( m_plot->legend() )
{
QFont font = m_plot->legend()->font();
font.setPixelSize( caf::FontTools::pointSizeToPixelSize( fontSize ) );
m_plot->legend()->setFont( font );
// Set font size for all existing labels
QList<QwtLegendLabel*> labels = m_plot->legend()->findChildren<QwtLegendLabel*>();
for ( QwtLegendLabel* label : labels )
{
label->setFont( font );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::insertLegend( RiuPlotWidget::Legend legendPosition )
{
CAF_ASSERT( legendPosition == RiuPlotWidget::Legend::BOTTOM || legendPosition == RiuPlotWidget::Legend::TOP ||
legendPosition == RiuPlotWidget::Legend::LEFT || legendPosition == RiuPlotWidget::Legend::RIGHT );
QwtPlot::LegendPosition pos = QwtPlot::LegendPosition::BottomLegend;
if ( legendPosition == RiuPlotWidget::Legend::TOP )
{
pos = QwtPlot::LegendPosition::TopLegend;
}
else if ( legendPosition == RiuPlotWidget::Legend::LEFT )
{
pos = QwtPlot::LegendPosition::LeftLegend;
}
else if ( legendPosition == RiuPlotWidget::Legend::RIGHT )
{
pos = QwtPlot::LegendPosition::RightLegend;
}
auto* legend = new QwtLegend( this );
m_plot->insertLegend( legend, pos );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::clearLegend()
{
m_plot->insertLegend( nullptr );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<double, double> RiuQwtPlotWidget::axisRange( RiuPlotAxis axis ) const
{
QwtInterval interval = m_plot->axisScaleDiv( toQwtPlotAxis( axis ) ).interval();
return std::make_pair( interval.minValue(), interval.maxValue() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisRange( RiuPlotAxis axis, double min, double max )
{
// Note: Especially the Y-axis may be inverted
if ( m_plot->axisScaleEngine( toQwtPlotAxis( axis ) )->testAttribute( QwtScaleEngine::Inverted ) )
{
setAxisScale( axis, max, min );
}
else
{
setAxisScale( axis, min, max );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisInverted( RiuPlotAxis axis, bool isInverted )
{
auto scaleEngine = m_plot->axisScaleEngine( toQwtPlotAxis( axis ) );
if ( scaleEngine ) scaleEngine->setAttribute( QwtScaleEngine::Inverted, isInverted );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisLabelsAndTicksEnabled( RiuPlotAxis axis, bool enableLabels, bool enableTicks )
{
m_plot->axisScaleDraw( toQwtPlotAxis( axis ) )->enableComponent( QwtAbstractScaleDraw::Ticks, enableTicks );
m_plot->axisScaleDraw( toQwtPlotAxis( axis ) )->enableComponent( QwtAbstractScaleDraw::Labels, enableLabels );
recalculateAxisExtents( axis );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::enableGridLines( RiuPlotAxis axis, bool majorGridLines, bool minorGridLines )
{
QwtPlotItemList plotItems = m_plot->itemList( QwtPlotItem::Rtti_PlotGrid );
auto qwtAxis = toQwtPlotAxis( axis );
for ( QwtPlotItem* plotItem : plotItems )
{
auto* grid = static_cast<QwtPlotGrid*>( plotItem );
if ( qwtAxis == QwtAxis::XTop || qwtAxis == QwtAxis::XBottom )
{
grid->setXAxis( qwtAxis );
grid->enableX( majorGridLines );
grid->enableXMin( minorGridLines );
}
else
{
grid->setYAxis( qwtAxis );
grid->enableY( majorGridLines );
grid->enableYMin( minorGridLines );
}
grid->setMajorPen( Qt::lightGray, 1.0, Qt::SolidLine );
grid->setMinorPen( Qt::lightGray, 1.0, Qt::DashLine );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setMajorTicksList( RiuPlotAxis axis, const QList<double>& majorTicks, double minValue, double maxValue )
{
auto qwtAxis = toQwtPlotAxis( axis );
QwtScaleDiv scaleDiv( minValue, maxValue );
scaleDiv.setTicks( QwtScaleDiv::TickType::MajorTick, majorTicks );
m_plot->setAxisScaleDiv( qwtAxis, scaleDiv );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setMajorAndMinorTickIntervals( RiuPlotAxis axis,
double majorTickInterval,
double minorTickInterval,
double minValue,
double maxValue )
{
auto qwtAxis = toQwtPlotAxis( axis );
auto* linearScaleEngine = dynamic_cast<RiuQwtLinearScaleEngine*>( m_plot->axisScaleEngine( qwtAxis ) );
if ( linearScaleEngine )
{
QwtScaleDiv scaleDiv = linearScaleEngine->divideScaleWithExplicitIntervals( minValue, maxValue, majorTickInterval, minorTickInterval );
m_plot->setAxisScaleDiv( qwtAxis, scaleDiv );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setMajorAndMinorTickIntervalsAndRange( RiuPlotAxis axis,
double majorTickInterval,
double minorTickInterval,
double minTickValue,
double maxTickValue,
double rangeMin,
double rangeMax )
{
auto qwtAxis = toQwtPlotAxis( axis );
auto* scaleEngine = dynamic_cast<RiuQwtLinearScaleEngine*>( m_plot->axisScaleEngine( qwtAxis ) );
if ( scaleEngine )
{
QwtScaleDiv scaleDiv = scaleEngine->divideScaleWithExplicitIntervalsAndRange( minTickValue,
maxTickValue,
majorTickInterval,
minorTickInterval,
rangeMin,
rangeMax );
m_plot->setAxisScaleDiv( qwtAxis, scaleDiv );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAutoTickIntervalCounts( RiuPlotAxis axis, int maxMajorTickIntervalCount, int maxMinorTickIntervalCount )
{
m_plot->setAxisMaxMajor( toQwtPlotAxis( axis ), maxMajorTickIntervalCount );
m_plot->setAxisMaxMinor( toQwtPlotAxis( axis ), maxMinorTickIntervalCount );
// Reapply axis limits to force Qwt to use the tick settings.
QwtInterval currentRange = m_plot->axisInterval( toQwtPlotAxis( axis ) );
setAxisScale( axis, currentRange.minValue(), currentRange.maxValue() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RiuQwtPlotWidget::majorTickInterval( RiuPlotAxis axis ) const
{
QwtScaleDiv scaleDiv = m_plot->axisScaleDiv( toQwtPlotAxis( axis ) );
QList<double> majorTicks = scaleDiv.ticks( QwtScaleDiv::MajorTick );
if ( majorTicks.size() < 2 ) return 0.0;
return majorTicks.at( 1 ) - majorTicks.at( 0 );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RiuQwtPlotWidget::minorTickInterval( RiuPlotAxis axis ) const
{
QwtScaleDiv scaleDiv = m_plot->axisScaleDiv( QwtAxis::XTop );
QList<double> minorTicks = scaleDiv.ticks( QwtScaleDiv::MinorTick );
if ( minorTicks.size() < 2 ) return 0.0;
return minorTicks.at( 1 ) - minorTicks.at( 0 );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiuQwtPlotWidget::axisExtent( RiuPlotAxis axis ) const
{
auto [rangeMin, rangeMax] = axisRange( axis );
if ( std::abs( rangeMax - rangeMin ) < 1.0e-14 ) return 0;
int lineExtent = 0;
auto qwtAxis = toQwtPlotAxis( axis );
if ( m_plot->axisScaleDraw( qwtAxis )->hasComponent( QwtAbstractScaleDraw::Ticks ) )
{
lineExtent += m_plot->axisScaleDraw( qwtAxis )->maxTickLength();
}
if ( m_plot->axisScaleDraw( qwtAxis )->hasComponent( QwtAbstractScaleDraw::Labels ) )
{
QFont tickLabelFont = m_plot->axisFont( toQwtPlotAxis( axis ) );
// Make space for a fairly long value label
QSize labelSize = QFontMetrics( tickLabelFont ).boundingRect( QString( "9.9e-9" ) ).size();
if ( axis.axis() == RiaDefines::PlotAxis::PLOT_AXIS_LEFT || axis.axis() == RiaDefines::PlotAxis::PLOT_AXIS_RIGHT )
{
lineExtent = labelSize.width();
}
else
{
lineExtent = labelSize.height();
}
}
if ( !m_plot->axisTitle( qwtAxis ).text().isEmpty() )
{
auto it = m_axisTitlesEnabled.find( axis );
if ( it != m_axisTitlesEnabled.end() && it->second )
{
QFont titleFont = m_plot->axisTitle( qwtAxis ).font();
// Label is aligned vertically on vertical axes
// So height is sufficient in both cases.
lineExtent += QFontMetrics( titleFont ).height();
}
}
return lineExtent;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QPoint RiuQwtPlotWidget::dragStartPosition() const
{
return m_clickPosition;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::scheduleReplot()
{
RiaPlotWindowRedrawScheduler::instance()->schedulePlotWidgetReplot( this );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::updateLayout()
{
m_plot->updateLayout();
updateOverlayFrameLayout();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::updateLegend()
{
m_plot->updateLegend();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiuQwtPlotWidget::eventFilter( QObject* watched, QEvent* event )
{
if ( RiuPlotWidget::handleDragDropEvent( event ) ) return true;
auto* wheelEvent = dynamic_cast<QWheelEvent*>( event );
if ( wheelEvent && watched == m_plot->canvas() )
{
event->accept();
emit onWheelEvent( wheelEvent );
return true;
}
auto* mouseEvent = dynamic_cast<QMouseEvent*>( event );
if ( mouseEvent )
{
if ( isZoomerActive() ) return false;
if ( mouseEvent->type() == QMouseEvent::MouseButtonDblClick )
{
if ( m_plotDefinition ) m_plotDefinition->zoomAll();
return true;
}
bool toggleItemInSelection = ( mouseEvent->modifiers() & Qt::ControlModifier ) != 0;
if ( mouseEvent->type() == QMouseEvent::MouseButtonPress && mouseEvent->button() == Qt::LeftButton )
{
m_clickPosition = mouseEvent->pos();
}
if ( watched == m_plot && !m_plot->canvas()->geometry().contains( mouseEvent->pos() ) )
{
if ( mouseEvent->type() == QMouseEvent::MouseButtonPress && ( mouseEvent->button() == Qt::LeftButton ) && !m_clickPosition.isNull() )
{
QWidget* childClicked = m_plot->childAt( m_clickPosition );
if ( childClicked )
{
auto* scaleWidget = qobject_cast<QwtScaleWidget*>( childClicked );
if ( scaleWidget )
{
onAxisSelected( scaleWidget, toggleItemInSelection );
m_clickPosition = QPoint();
return true;
}
}
else
{
endZoomOperations();
emit plotSelected( toggleItemInSelection );
m_clickPosition = QPoint();
return true;
}
}
}
else if ( watched == m_plot->canvas() )
{
if ( mouseEvent->type() == QMouseEvent::MouseButtonRelease && mouseEvent->button() == Qt::LeftButton && !m_clickPosition.isNull() )
{
endZoomOperations();
selectClosestPlotItem( mouseEvent->pos(), toggleItemInSelection );
m_clickPosition = QPoint();
return true;
}
}
onMouseMoveEvent( mouseEvent );
}
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::hideEvent( QHideEvent* event )
{
resetPlotItemHighlighting();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::resizeEvent( QResizeEvent* event )
{
updateOverlayFrameLayout();
event->accept();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::keyPressEvent( QKeyEvent* event )
{
emit onKeyPressEvent( event );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::applyPlotTitleToQwt()
{
QString plotTitleToApply = m_plotTitleEnabled ? m_plotTitleText : QString( "" );
QwtText qwtText = m_plot->title();
qwtText.setRenderFlags( m_titleRenderingFlags );
if ( m_titleFontSize > 0 )
{
QFont font = qwtText.font();
font.setPixelSize( caf::FontTools::pointSizeToPixelSize( m_titleFontSize ) );
qwtText.setFont( font );
}
qwtText.setText( plotTitleToApply );
// Always set the title, as Qwt does not do anything if the text is the same
m_plot->setTitle( qwtText );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::applyAxisTitleToQwt( RiuPlotAxis axis )
{
QString titleToApply = m_axisTitlesEnabled[axis] ? m_axisTitles[axis] : QString( "" );
auto qwtAxis = toQwtPlotAxis( axis );
QwtText axisTitle = m_plot->axisTitle( qwtAxis );
if ( titleToApply != axisTitle.text() )
{
axisTitle.setText( titleToApply );
m_plot->setAxisTitle( qwtAxis, axisTitle );
}
recalculateAxisExtents( axis );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QSize RiuQwtPlotWidget::sizeHint() const
{
return { 0, 0 };
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QSize RiuQwtPlotWidget::minimumSizeHint() const
{
return { 0, 0 };
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiuQwtPlotWidget::isZoomerActive() const
{
return false;
}
//--------------------------------------------------------------------------------------------------
/// Empty default implementation
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::endZoomOperations()
{
}
//--------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::renderTo( QPainter* painter, const QRect& targetRect, double scaling )
{
static_cast<QwtPlotCanvas*>( m_plot->canvas() )->setPaintAttribute( QwtPlotCanvas::BackingStore, false );
QPoint plotTopLeftInWindowCoords = targetRect.topLeft();
QRectF canvasRect = m_plot->plotLayout()->canvasRect();
QPoint canvasTopLeftInPlotCoords( canvasRect.topLeft().x() * scaling, canvasRect.topLeft().y() * scaling );
QPoint canvasBottomRightInPlotCoords( canvasRect.bottomRight().x(), canvasRect.bottomRight().y() );
QPoint canvasTopLeftInWindowCoords = canvasTopLeftInPlotCoords + plotTopLeftInWindowCoords;
QPoint canvasBottomRightInWindowCoords = canvasBottomRightInPlotCoords + plotTopLeftInWindowCoords;
QwtPlotRenderer renderer( this );
renderer.render( m_plot, painter, targetRect );
static_cast<QwtPlotCanvas*>( m_plot->canvas() )->setPaintAttribute( QwtPlotCanvas::BackingStore, true );
for ( RiuDraggableOverlayFrame* overlayFrame : m_overlayFrames )
{
if ( overlayFrame->isVisible() )
{
QPoint overlayTopLeftInCanvasCoords = overlayFrame->frameGeometry().topLeft();
QPoint overlayTopLeftInWindowCoords = overlayTopLeftInCanvasCoords + canvasTopLeftInWindowCoords;
{
QRect overlayRect = overlayFrame->frameGeometry();
QSize desiredSize = overlayRect.size();
QSize minimumSize = overlayFrame->minimumSizeHint();
QSize actualSize = desiredSize.expandedTo( minimumSize );
overlayRect.moveTo( overlayTopLeftInWindowCoords );
overlayRect.setSize( actualSize );
QPoint overlayBottomRightInWindowCoords = overlayRect.bottomRight();
overlayBottomRightInWindowCoords.setX( std::min( overlayBottomRightInWindowCoords.x(),
canvasBottomRightInWindowCoords.x() - (int)scaling * m_overlayMargins ) );
overlayBottomRightInWindowCoords.setY( std::min( overlayBottomRightInWindowCoords.y(),
canvasBottomRightInWindowCoords.y() - (int)scaling * m_overlayMargins ) );
overlayRect.moveBottomRight( overlayBottomRightInWindowCoords );
overlayFrame->renderTo( painter, overlayRect );
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::renderTo( QPaintDevice* paintDevice, const QRect& targetRect )
{
auto scaling = RiaDefines::scalingFactor( paintDevice );
QPainter painter( paintDevice );
renderTo( &painter, targetRect, scaling );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiuQwtPlotWidget::overlayMargins() const
{
return m_overlayMargins;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimViewWindow* RiuQwtPlotWidget::ownerViewWindow() const
{
return m_plotDefinition;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::onAxisSelected( QwtScaleWidget* scale, bool toggleItemInSelection )
{
for ( int i = 0; i < QwtAxis::AxisPositions; ++i )
{
auto pos = static_cast<QwtAxis::Position>( i );
int count = m_plot->axesCount( pos );
for ( int id = 0; id < count; id++ )
{
QwtAxisId axisId( pos, id );
if ( scale == m_plot->axisWidget( axisId ) )
{
resetPlotItemHighlighting();
highlightPlotItemsForQwtAxis( axisId );
scheduleReplot();
RiuPlotAxis plotAxis = findPlotAxisForQwtAxis( axisId );
emit axisSelected( plotAxis, toggleItemInSelection );
return;
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::recalculateAxisExtents( RiuPlotAxis axis )
{
auto qwtAxis = toQwtPlotAxis( axis );
if ( qwtAxis.pos == QwtAxis::YLeft || qwtAxis.pos == QwtAxis::YRight )
{
int extent = axisExtent( axis );
m_plot->axisScaleDraw( qwtAxis )->setMinimumExtent( extent );
setMinimumWidth( defaultMinimumWidth() + extent );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiuQwtPlotWidget::highlightItemWidthAdjustment()
{
return 2;
}
//--------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QWidget* RiuQwtPlotWidget::getParentForOverlay() const
{
return m_plot->canvas();
}
//--------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::findClosestPlotItem( const QPoint& pos, QwtPlotItem** closestItem, int* closestCurvePoint, double* distanceFromClick ) const
{
CAF_ASSERT( closestItem && closestCurvePoint && distanceFromClick );
// Force empty defaults
*closestItem = nullptr;
*closestCurvePoint = -1;
*distanceFromClick = std::numeric_limits<double>::infinity();
const QwtPlotItemList& itmList = m_plot->itemList();
for ( auto it : itmList )
{
if ( it->rtti() == QwtPlotItem::Rtti_PlotCurve )
{
auto* candidateCurve = static_cast<QwtPlotCurve*>( it );
double dist = std::numeric_limits<double>::infinity();
int curvePoint = candidateCurve->closestPoint( pos, &dist );
if ( dist < *distanceFromClick )
{
*closestItem = candidateCurve;
*distanceFromClick = dist;
*closestCurvePoint = curvePoint;
}
}
else if ( it->rtti() == QwtPlotItem::Rtti_PlotShape )
{
auto* shapeItem = static_cast<QwtPlotShapeItem*>( it );
QPointF scalePos( m_plot->invTransform( QwtAxis::XBottom, pos.x() ), m_plot->invTransform( QwtAxis::YLeft, pos.y() ) );
if ( shapeItem->shape().boundingRect().contains( scalePos ) )
{
*closestItem = it;
*distanceFromClick = 0.0;
}
}
else if ( it->rtti() == QwtPlotItem::Rtti_PlotBarChart )
{
auto* barChart = static_cast<QwtPlotBarChart*>( it );
QPointF scalePos( m_plot->invTransform( QwtAxis::XBottom, pos.x() ), m_plot->invTransform( QwtAxis::YLeft, pos.y() ) );
bool horizontal = barChart->orientation() == Qt::Horizontal;
for ( size_t i = 0; i < barChart->dataSize(); ++i )
{
QPointF samplePoint = barChart->sample( (int)i );
double dist = horizontal ? std::abs( samplePoint.x() - scalePos.y() ) : std::abs( samplePoint.x() - scalePos.x() );
if ( dist < *distanceFromClick )
{
*closestItem = it;
*closestCurvePoint = (int)i;
*distanceFromClick = dist;
}
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<RiuPlotCurve*, int> RiuQwtPlotWidget::findClosestCurve( const QPoint& pos, double& distanceFromClick ) const
{
QwtPlotItem* closestItem = nullptr;
int closestCurvePoint = -1;
QPoint globalPos = mapToGlobal( pos );
QPoint localPos = qwtPlot()->canvas()->mapFromGlobal( globalPos );
findClosestPlotItem( localPos, &closestItem, &closestCurvePoint, &distanceFromClick );
auto plotCurve = dynamic_cast<RiuQwtPlotCurve*>( closestItem );
return std::make_pair( plotCurve, closestCurvePoint );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::selectClosestPlotItem( const QPoint& pos, bool toggleItemInSelection /*= false*/ )
{
QwtPlotItem* closestItem = nullptr;
double distanceFromClick = std::numeric_limits<double>::infinity();
int closestCurvePoint = -1;
findClosestPlotItem( pos, &closestItem, &closestCurvePoint, &distanceFromClick );
RiuPlotMainWindowTools::showPlotMainWindow();
if ( closestItem && distanceFromClick < 20 )
{
bool updateCurveOrder = false;
resetPlotItemHighlighting( updateCurveOrder );
auto curve = dynamic_cast<RiuPlotCurve*>( closestItem );
if ( curve && curve->ownerRimCurve() )
{
const auto rimCurve = curve->ownerRimCurve();
if ( RimSummaryEnsembleTools::isEnsembleCurve( rimCurve ) )
{
RimSummaryEnsembleTools::highlightCurvesForSameRealizationFromCurve( rimCurve );
}
else
{
highlightCurvesUpdateOrder( { rimCurve } );
}
}
else
{
// Currently used from the matrix plot to highlight the selected cell in the matrix plot, see
// RimCorrelationMatrixPlot::createMatrix()
highlightPlotShapeItems( { closestItem } );
}
auto plotItem = std::make_shared<RiuQwtPlotItem>( closestItem );
emit plotItemSelected( plotItem, toggleItemInSelection, distanceFromClick < 10 ? closestCurvePoint : -1 );
}
else
{
RimSummaryEnsembleTools::resetHighlightAllPlots();
emit plotSelected( toggleItemInSelection );
}
// Always do a replot, as the reset operation also requires replot
replot();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiuQwtPlotWidget::defaultMinimumWidth()
{
return 80;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::replot()
{
m_plot->replot();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::highlightCurvesUpdateOrder( const std::set<RimPlotCurve*>& curves )
{
highlightPlotCurves( curves );
updateCurveOrder();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::highlightPlotCurves( const std::set<RimPlotCurve*>& curves )
{
if ( !m_plotDefinition || !m_plotDefinition->isCurveHighlightSupported() )
{
return;
}
auto plotItemList = m_plot->itemList();
for ( QwtPlotItem* plotItem : plotItemList )
{
auto* riuPlotCurve = dynamic_cast<RiuPlotCurve*>( plotItem );
if ( !riuPlotCurve ) continue;
auto currentRimPlotCurve = riuPlotCurve->ownerRimCurve();
// Do not modify curve objects with no associated Rim object, as the Rim object is used to restore color after highlight manipulation
if ( !currentRimPlotCurve ) continue;
auto* plotCurve = dynamic_cast<QwtPlotCurve*>( plotItem );
if ( plotCurve )
{
QPen existingPen = plotCurve->pen();
QColor bgColor = m_plot->canvasBackground().color();
QColor curveColor = existingPen.color();
QColor symbolColor;
QColor symbolLineColor;
auto penWidth = existingPen.width();
auto* symbol = const_cast<QwtSymbol*>( plotCurve->symbol() );
if ( symbol )
{
symbolColor = symbol->brush().color();
symbolLineColor = symbol->pen().color();
}
double zValue = plotCurve->z();
if ( curves.count( currentRimPlotCurve ) > 0 )
{
auto highlightColor = curveColor;
const double threshold = 0.05;
if ( curveColor.hsvHueF() > threshold || curveColor.saturationF() > threshold )
{
auto saturation = 1.0;
auto value = 1.0;
auto hue = curveColor.hueF();
highlightColor = QColor::fromHsvF( hue, saturation, value );
}
existingPen.setColor( highlightColor );
existingPen.setWidth( penWidth + highlightItemWidthAdjustment() );
plotCurve->setPen( existingPen );
plotCurve->setZ( zValue + 100.0 );
highlightPlotAxes( plotCurve->xAxis(), plotCurve->yAxis() );
m_hightlightedCurves.push_back( currentRimPlotCurve );
}
else
{
int backgroundWeight = 2;
QColor blendedColor = RiaColorTools::blendQColors( bgColor, curveColor, backgroundWeight, 1 );
QColor blendedSymbolColor = RiaColorTools::blendQColors( bgColor, symbolColor, backgroundWeight, 1 );
QColor blendedSymbolLineColor = RiaColorTools::blendQColors( bgColor, symbolLineColor, backgroundWeight, 1 );
plotCurve->setPen( blendedColor, existingPen.width(), existingPen.style() );
if ( symbol )
{
symbol->setColor( blendedSymbolColor );
symbol->setPen( blendedSymbolLineColor, symbol->pen().width(), symbol->pen().style() );
}
}
m_originalZValues.insert( std::make_pair( plotCurve, zValue ) );
continue;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::highlightPlotShapeItems( const std::set<const QwtPlotItem*>& closestItems )
{
auto plotItemList = m_plot->itemList();
for ( QwtPlotItem* plotItem : plotItemList )
{
auto* plotShapeItem = dynamic_cast<QwtPlotShapeItem*>( plotItem );
if ( plotShapeItem && closestItems.count( plotItem ) > 0 )
{
QPen pen = plotShapeItem->pen();
pen.setColor( QColor( Qt::green ) );
pen.setWidth( 3 );
plotShapeItem->setPen( pen );
plotShapeItem->setZ( plotShapeItem->z() + 100.0 );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::resetPlotItemHighlighting( bool doUpdateCurveOrder )
{
resetPlotCurveHighlighting();
resetPlotShapeItemHighlighting();
resetPlotAxisHighlighting();
if ( doUpdateCurveOrder ) updateCurveOrder();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimPlotCurve*> RiuQwtPlotWidget::highlightedCurves() const
{
return m_hightlightedCurves;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::resetPlotCurveHighlighting()
{
m_hightlightedCurves.clear();
if ( !m_plotDefinition || m_originalZValues.empty() )
{
return;
}
const auto& plotItemList = m_plot->itemList();
for ( QwtPlotItem* plotItem : plotItemList )
{
if ( auto* riuPlotCurve = dynamic_cast<RiuQwtPlotCurve*>( plotItem ) )
{
if ( auto rimPlotCurve = riuPlotCurve->ownerRimCurve() )
{
rimPlotCurve->updateCurveAppearance();
double zValue = m_originalZValues[riuPlotCurve];
riuPlotCurve->setZ( zValue );
continue;
}
}
}
m_originalZValues.clear();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::resetPlotShapeItemHighlighting()
{
const auto& plotItemList = m_plot->itemList();
for ( QwtPlotItem* plotItem : plotItemList )
{
auto* plotShapeItem = dynamic_cast<QwtPlotShapeItem*>( plotItem );
if ( plotShapeItem )
{
QPen pen = plotShapeItem->pen();
auto color = RiuGuiTheme::getColorByVariableName( "markerColor" );
pen.setColor( color );
pen.setWidth( 1 );
plotShapeItem->setPen( pen );
plotShapeItem->setZ( plotShapeItem->z() - 100.0 );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::resetPlotAxisHighlighting()
{
// Reset axis widgets highlighting
std::vector<QwtAxis::Position> axisPositions = { QwtAxis::Position::YLeft,
QwtAxis::Position::YRight,
QwtAxis::Position::XTop,
QwtAxis::Position::XBottom };
// Use text color from theme
QColor textColor = RiuGuiTheme::getColorByVariableName( "textColor" );
QString style = QString( "color: %1;" ).arg( textColor.name() );
for ( auto pos : axisPositions )
{
int count = m_plot->axesCount( pos );
for ( int i = 0; i < count; i++ )
{
QwtAxisId axisId( pos, i );
auto axisWidget = m_plot->axisWidget( axisId );
axisWidget->setStyleSheet( style );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::highlightPlotItemsForQwtAxis( QwtAxisId axisId )
{
std::set<RimPlotCurve*> curves;
auto plotItemList = m_plot->itemList();
for ( QwtPlotItem* plotItem : plotItemList )
{
auto* qwtPlotCurve = dynamic_cast<RiuQwtPlotCurve*>( plotItem );
if ( qwtPlotCurve )
{
QwtAxisId xAxis = qwtPlotCurve->xAxis();
QwtAxisId yAxis = qwtPlotCurve->yAxis();
if ( xAxis == axisId || yAxis == axisId )
{
if ( auto curve = qwtPlotCurve->ownerRimCurve() )
{
curves.insert( curve );
}
}
}
}
highlightCurvesUpdateOrder( curves );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::highlightPlotAxes( QwtAxisId axisIdX, QwtAxisId axisIdY )
{
std::vector<QwtAxis::Position> axisPositions = { QwtAxis::Position::YLeft,
QwtAxis::Position::YRight,
QwtAxis::Position::XTop,
QwtAxis::Position::XBottom };
// Highlight selected axis by toning down the others in same dimension
for ( auto pos : axisPositions )
{
int count = m_plot->axesCount( pos );
for ( int i = 0; i < count; i++ )
{
QwtAxisId axisId( pos, i );
if ( axisId != axisIdX && axisId != axisIdY )
{
auto axisWidget = m_plot->axisWidget( axisId );
auto color = RiuGuiTheme::getColorByVariableName( "backgroundColor2" );
auto colorText = color.name();
axisWidget->setStyleSheet( QString( "color: %1;" ).arg( colorText ) );
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QwtPlot* RiuQwtPlotWidget::qwtPlot() const
{
return m_plot;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::ensureAxisIsCreated( RiuPlotAxis axis )
{
// Check if the axis already exists
auto it = m_axisMapping.find( axis );
if ( it != m_axisMapping.end() ) return;
// Special handling for default axis (index == 0):
// These are already created by qwt.
if ( axis.index() == 0 )
{
QwtAxisId newQwtAxis( RiuQwtPlotTools::toQwtPlotAxisEnum( axis.axis() ), 0 );
m_axisMapping.insert( std::make_pair( axis, newQwtAxis ) );
}
else
{
auto qwtAxisId = RiuQwtPlotTools::toQwtPlotAxisEnum( axis.axis() );
int count = m_plot->axesCount( qwtAxisId );
int requiredCount = count + 1;
m_plot->setAxesCount( qwtAxisId, requiredCount );
QwtAxisId newQwtAxis( qwtAxisId, count );
m_axisMapping.insert( std::make_pair( axis, newQwtAxis ) );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::enableAxis( RiuPlotAxis axis, bool isEnabled )
{
ensureAxisIsCreated( axis );
m_plot->setAxisVisible( toQwtPlotAxis( axis ), isEnabled );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::enableAxisNumberLabels( RiuPlotAxis axis, bool isEnabled )
{
m_plot->axisScaleDraw( toQwtPlotAxis( axis ) )->enableComponent( QwtAbstractScaleDraw::Labels, isEnabled );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiuQwtPlotWidget::axisEnabled( RiuPlotAxis axis ) const
{
auto qwtPlotAxis = toQwtPlotAxis( axis );
if ( qwtPlotAxis.pos < 0 ) return false;
return m_plot->isAxisVisible( qwtPlotAxis );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisMaxMinor( RiuPlotAxis axis, int maxMinor )
{
m_plot->setAxisMaxMinor( toQwtPlotAxis( axis ), maxMinor );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisMaxMajor( RiuPlotAxis axis, int maxMajor )
{
m_plot->setAxisMaxMajor( toQwtPlotAxis( axis ), maxMajor );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::removeEventFilter()
{
m_plot->removeEventFilter( m_plot );
m_plot->canvas()->removeEventFilter( m_plot );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisAutoScale( RiuPlotAxis axis, bool autoScale )
{
m_plot->setAxisAutoScale( toQwtPlotAxis( axis ), autoScale );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisScale( RiuPlotAxis axis, double min, double max )
{
setAxisScale( toQwtPlotAxis( axis ), min, max );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisScale( QwtAxisId axis, double min, double max )
{
m_plot->setAxisScale( axis, min, max );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuQwtPlotWidget::AxisScaleType RiuQwtPlotWidget::axisScaleType( RiuPlotAxis axis ) const
{
auto qwtAxis = toQwtPlotAxis( axis );
auto* logScaleEngine = dynamic_cast<QwtLogScaleEngine*>( m_plot->axisScaleEngine( qwtAxis ) );
auto* dateScaleEngine = dynamic_cast<QwtDateScaleEngine*>( m_plot->axisScaleEngine( qwtAxis ) );
if ( logScaleEngine != nullptr ) return AxisScaleType::LOGARITHMIC;
if ( dateScaleEngine != nullptr ) return AxisScaleType::DATE;
return AxisScaleType::LINEAR;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisScaleType( RiuPlotAxis axis, RiuQwtPlotWidget::AxisScaleType axisScaleType )
{
auto qwtAxis = toQwtPlotAxis( axis );
setAxisScaleType( qwtAxis, axisScaleType );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::setAxisScaleType( QwtAxisId axis, RiuQwtPlotWidget::AxisScaleType axisScaleType )
{
if ( axisScaleType == AxisScaleType::LOGARITHMIC ) m_plot->setAxisScaleEngine( axis, new QwtLogScaleEngine );
if ( axisScaleType == AxisScaleType::LINEAR ) m_plot->setAxisScaleEngine( axis, new QwtLinearScaleEngine );
if ( axisScaleType == AxisScaleType::DATE ) m_plot->setAxisScaleEngine( axis, new QwtDateScaleEngine );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::updateAxes()
{
m_plot->updateAxes();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotAxis RiuQwtPlotWidget::createNextPlotAxis( RiaDefines::PlotAxis axis )
{
auto qwtAxis = RiuQwtPlotTools::toQwtPlotAxisEnum( axis );
auto count = m_plot->axesCount( qwtAxis );
return RiuPlotAxis( axis, count );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotCurve* RiuQwtPlotWidget::createPlotCurve( RimPlotCurve* ownerRimCurve, const QString& title )
{
return new RiuQwtPlotCurve( ownerRimCurve, title );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::detachItems( RiuPlotWidget::PlotItemType plotItemType )
{
CAF_ASSERT( plotItemType == RiuPlotWidget::PlotItemType::CURVE );
if ( plotItemType == RiuPlotWidget::PlotItemType::CURVE ) qwtPlot()->detachItems( QwtPlotItem::Rtti_PlotCurve );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const QColor& RiuQwtPlotWidget::backgroundColor() const
{
return m_plot->canvasBackground().color();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiuQwtPlotWidget::isMultiAxisSupported() const
{
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::pruneAxes( const std::set<RiuPlotAxis>& usedAxes )
{
// Make a list of axes to remove since moving the axis invalidates the m_axisMapping iterator
std::vector<RiuPlotAxis> axesToRemove;
for ( auto [plotAxis, qwtMapping] : m_axisMapping )
{
if ( usedAxes.count( plotAxis ) == 0 )
{
axesToRemove.push_back( plotAxis );
}
}
for ( const auto& plotAxis : axesToRemove )
moveAxis( plotAxis, RiuPlotAxis::defaultLeft() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotAxis RiuQwtPlotWidget::findPlotAxisForQwtAxis( const QwtAxisId& qwtAxisId ) const
{
for ( auto [plotAxis, qwtMapping] : m_axisMapping )
if ( qwtMapping == qwtAxisId ) return plotAxis;
CAF_ASSERT( false );
return RiuPlotAxis::defaultLeft();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::onMouseMoveEvent( QMouseEvent* event )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::moveAxis( RiuPlotAxis oldAxis, RiuPlotAxis newAxis )
{
auto countAxis = [this]( RiaDefines::PlotAxis axis )
{
int count = 0;
for ( auto [plotAxis, qwtMapping] : m_axisMapping )
{
if ( plotAxis.axis() == axis ) count++;
}
return count;
};
auto isLastItem = [this]( RiuPlotAxis plotAxis, int count )
{
auto qwtAxis = toQwtPlotAxis( plotAxis );
return qwtAxis.id == ( count - 1 );
};
auto removeAxis = [this, countAxis, isLastItem]( RiuPlotAxis plotAxis )
{
auto qwtAxisPos = RiuQwtPlotTools::toQwtPlotAxisEnum( plotAxis.axis() );
int count = countAxis( plotAxis.axis() );
bool isLast = isLastItem( plotAxis, count );
if ( isLast )
{
// If axis to remove is the last axis item on the given side it
// is safe to let qwt delete it in setAxesCount.
m_axisMapping.erase( plotAxis );
m_plot->setAxesCount( qwtAxisPos, count - 1 );
}
else
{
// When the axis to delete is not the last axis item on the given side
// we have to move the last axis into the position of the axis to remove.
// Move the last item into the spot which has been freed up
auto targetQwtAxis = m_axisMapping.find( plotAxis )->second;
// Last item on the same side as we are deleting from
auto sourceQwtAxis = QwtAxisId( qwtAxisPos, count - 1 );
auto sourcePlotAxis = findPlotAxisForQwtAxis( sourceQwtAxis );
// Copy properties of the last axis item
setAxisScaleType( targetQwtAxis, axisScaleType( sourcePlotAxis ) );
auto range = axisRange( sourcePlotAxis );
setAxisScale( targetQwtAxis, range.first, range.second );
bool autoScale = m_plot->axisAutoScale( sourceQwtAxis );
m_plot->setAxisAutoScale( targetQwtAxis, autoScale );
// Finally remove the last item (which has been overwritten the item to remove).
m_axisMapping.erase( plotAxis );
m_axisMapping.erase( sourcePlotAxis );
m_axisMapping.insert( std::make_pair( sourcePlotAxis, targetQwtAxis ) );
m_plot->setAxesCount( qwtAxisPos, count - 1 );
}
};
removeAxis( oldAxis );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QwtAxisId RiuQwtPlotWidget::toQwtPlotAxis( RiuPlotAxis plotAxis ) const
{
auto it = m_axisMapping.find( plotAxis );
if ( it != m_axisMapping.end() )
{
return it->second;
}
return { -1, -1 };
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::highlightPlotItem( const QwtPlotItem* plotItem )
{
bool refreshCurveOrder = false;
resetPlotItemHighlighting( refreshCurveOrder );
if ( auto curve = dynamic_cast<RiuQwtPlotCurve*>( const_cast<QwtPlotItem*>( plotItem ) ) )
{
highlightCurvesUpdateOrder( { curve->ownerRimCurve() } );
}
else
{
highlightPlotShapeItems( { plotItem } );
}
replot();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::onLegendClicked( const QVariant& itemInfo, int index )
{
if ( !itemInfo.canConvert<QwtPlotItem*>() ) return;
QwtPlotItem* plotItem = qvariant_cast<QwtPlotItem*>( itemInfo );
if ( plotItem )
{
highlightPlotItem( plotItem );
auto wrappedPlotItem = std::make_shared<RiuQwtPlotItem>( plotItem );
emit plotItemSelected( wrappedPlotItem, false, -1 );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotWidget::updateCurveOrder()
{
emit curveOrderNeedsUpdate();
}