mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
* Guard null pointer access * Delay open of RFT files until data is requested When opening ensemble summary cases, the opening of RFT files can take some time. Avoid this by delaying the file open operation. * Fix infinite update loop for RFT plots * Avoid moving the tracker line when clicking in the plot
1513 lines
59 KiB
C++
1513 lines
59 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 "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 )
|
|
{
|
|
CAF_ASSERT( m_plotDefinition );
|
|
|
|
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 );
|
|
|
|
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_plotTitle = plotTitle;
|
|
applyPlotTitleToQwt();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const QString& RiuQwtPlotWidget::plotTitle() const
|
|
{
|
|
return m_plotTitle;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RiuQwtPlotWidget::setPlotTitleEnabled( bool enabled )
|
|
{
|
|
m_plotTitleEnabled = enabled;
|
|
applyPlotTitleToQwt();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RiuQwtPlotWidget::plotTitleEnabled() const
|
|
{
|
|
return m_plotTitleEnabled;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RiuQwtPlotWidget::setPlotTitleFontSize( int titleFontSize )
|
|
{
|
|
auto title = m_plot->title();
|
|
QFont font = title.font();
|
|
font.setPixelSize( caf::FontTools::pointSizeToPixelSize( titleFontSize ) );
|
|
title.setFont( font );
|
|
m_plot->setTitle( title );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
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::setInternalLegendVisible( bool visible )
|
|
{
|
|
if ( visible )
|
|
{
|
|
auto* legend = new QwtLegend( this );
|
|
m_plot->insertLegend( legend, QwtPlot::BottomLegend );
|
|
}
|
|
else
|
|
{
|
|
m_plot->insertLegend( nullptr );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RiuQwtPlotWidget::insertLegend( RiuPlotWidget::Legend legendPosition )
|
|
{
|
|
CAF_ASSERT( legendPosition == RiuPlotWidget::Legend::BOTTOM );
|
|
|
|
auto* legend = new QwtLegend( this );
|
|
m_plot->insertLegend( legend, QwtPlot::BottomLegend );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
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 )
|
|
{
|
|
m_plot->axisScaleEngine( toQwtPlotAxis( axis ) )->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::setMajorAndMinorTickIntervals( RiuPlotAxis axis,
|
|
double majorTickInterval,
|
|
double minorTickInterval,
|
|
double minValue,
|
|
double maxValue )
|
|
{
|
|
auto qwtAxis = toQwtPlotAxis( axis );
|
|
auto* scaleEngine = dynamic_cast<RiuQwtLinearScaleEngine*>( m_plot->axisScaleEngine( qwtAxis ) );
|
|
if ( scaleEngine )
|
|
{
|
|
QwtScaleDiv scaleDiv =
|
|
scaleEngine->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();
|
|
|
|
if ( m_plotDefinition->isCurveHighlightSupported() )
|
|
{
|
|
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_plotTitle : QString( "" );
|
|
QwtText plotTitle = m_plot->title();
|
|
plotTitle.setRenderFlags( Qt::AlignHCenter | Qt::TextSingleLine );
|
|
if ( plotTitleToApply != plotTitle.text() )
|
|
{
|
|
plotTitle.setText( plotTitleToApply );
|
|
m_plot->setTitle( plotTitle );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
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 );
|
|
std::set<const QwtPlotItem*> plotItems = { closestItem };
|
|
highlightPlotItems( plotItems );
|
|
auto plotItem = std::make_shared<RiuQwtPlotItem>( closestItem );
|
|
emit plotItemSelected( plotItem, toggleItemInSelection, distanceFromClick < 10 ? closestCurvePoint : -1 );
|
|
}
|
|
else
|
|
{
|
|
resetPlotItemHighlighting();
|
|
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::highlightPlotItems( const std::set<const QwtPlotItem*>& closestItems )
|
|
{
|
|
auto plotItemList = m_plot->itemList();
|
|
for ( QwtPlotItem* plotItem : plotItemList )
|
|
{
|
|
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 ( closestItems.count( plotCurve ) > 0 )
|
|
{
|
|
existingPen.setWidth( penWidth + highlightItemWidthAdjustment() );
|
|
plotCurve->setPen( existingPen );
|
|
plotCurve->setZ( zValue + 100.0 );
|
|
highlightPlotAxes( plotCurve->xAxis(), plotCurve->yAxis() );
|
|
}
|
|
else
|
|
{
|
|
QColor blendedColor = RiaColorTools::blendQColors( bgColor, curveColor, 3, 1 );
|
|
QColor blendedSymbolColor = RiaColorTools::blendQColors( bgColor, symbolColor, 3, 1 );
|
|
QColor blendedSymbolLineColor = RiaColorTools::blendQColors( bgColor, symbolLineColor, 3, 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;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
}
|
|
|
|
updateCurveOrder();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RiuQwtPlotWidget::resetPlotItemHighlighting( bool doUpdateCurveOrder )
|
|
{
|
|
if ( !m_originalZValues.empty() )
|
|
{
|
|
auto plotItemList = m_plot->itemList();
|
|
for ( QwtPlotItem* plotItem : plotItemList )
|
|
{
|
|
if ( auto* plotCurve = dynamic_cast<QwtPlotCurve*>( plotItem ) )
|
|
{
|
|
auto* riuPlotCurve = dynamic_cast<RiuPlotCurve*>( plotItem );
|
|
|
|
if ( auto rimPlotCurve =
|
|
dynamic_cast<RimPlotCurve*>( m_plotDefinition->findPdmObjectFromPlotCurve( riuPlotCurve ) ) )
|
|
{
|
|
rimPlotCurve->updateCurveAppearance();
|
|
double zValue = m_originalZValues[plotCurve];
|
|
riuPlotCurve->setZ( zValue );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
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 );
|
|
}
|
|
}
|
|
m_originalZValues.clear();
|
|
}
|
|
|
|
resetPlotAxisHighlighting();
|
|
|
|
if ( doUpdateCurveOrder ) updateCurveOrder();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
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( "text-color" );
|
|
QString style =
|
|
QString( "color: rgb(%1, %2, %3);" ).arg( textColor.red() ).arg( textColor.green() ).arg( textColor.blue() );
|
|
|
|
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<const QwtPlotItem*> plotItems;
|
|
auto plotItemList = m_plot->itemList();
|
|
for ( QwtPlotItem* plotItem : plotItemList )
|
|
{
|
|
auto* plotCurve = dynamic_cast<QwtPlotCurve*>( plotItem );
|
|
if ( plotCurve )
|
|
{
|
|
QwtAxisId xAxis = plotCurve->xAxis();
|
|
QwtAxisId yAxis = plotCurve->yAxis();
|
|
if ( xAxis == axisId || yAxis == axisId )
|
|
{
|
|
plotItems.insert( plotItem );
|
|
}
|
|
}
|
|
}
|
|
|
|
highlightPlotItems( plotItems );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
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 );
|
|
axisWidget->setStyleSheet( "color: #D9D9D9" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
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, const QColor& color )
|
|
{
|
|
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 );
|
|
|
|
std::set<const QwtPlotItem*> items;
|
|
items.insert( plotItem );
|
|
highlightPlotItems( items );
|
|
|
|
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();
|
|
}
|