21 #include <QVBoxLayout> 
   22 #include <QMouseEvent> 
   26 #include <qwt_global.h> 
   27 #include <qwt_plot_canvas.h> 
   29 #include <qwt_plot_curve.h> 
   30 #include <qwt_plot_grid.h> 
   31 #include <qwt_plot_marker.h> 
   32 #include <qwt_plot_picker.h> 
   33 #include <qwt_picker_machine.h> 
   34 #include <qwt_plot_layout.h> 
   35 #include <qwt_symbol.h> 
   36 #include <qwt_legend.h> 
   37 #include <qwt_scale_div.h> 
   38 #include <qwt_scale_map.h> 
   40 #include <qwt_plot_renderer.h> 
   41 #include <qwt_plot_histogram.h> 
   47   mPlot = 
new QwtPlot();
 
   48   mPlot->setMinimumSize( QSize( 0, 100 ) );
 
   49   mPlot->setAxisScale( QwtPlot::yLeft, 0, 1 );
 
   50   mPlot->setAxisScale( QwtPlot::yRight, 0, 1 );
 
   51   mPlot->setAxisScale( QwtPlot::xBottom, 0, 1 );
 
   52   mPlot->setAxisScale( QwtPlot::xTop, 0, 1 );
 
   54   QVBoxLayout *vlayout = 
new QVBoxLayout();
 
   55   vlayout->addWidget( mPlot );
 
   59   mPlot->setFrameStyle( QFrame::NoFrame );
 
   60   QFrame *plotCanvasFrame = 
dynamic_cast<QFrame *
>( mPlot->canvas() );
 
   61   if ( plotCanvasFrame )
 
   62     plotCanvasFrame->setFrameStyle( QFrame::NoFrame );
 
   64   mPlot->enableAxis( QwtPlot::yLeft, 
false );
 
   65   mPlot->enableAxis( QwtPlot::xBottom, 
false );
 
   68   QwtPlotGrid *grid = 
new QwtPlotGrid();
 
   69   const QwtScaleDiv gridDiv( 0.0, 1.0, QList<double>(), QList<double>(), QList<double>() << 0.2 << 0.4 << 0.6 << 0.8 );
 
   70   grid->setXDiv( gridDiv );
 
   71   grid->setYDiv( gridDiv );
 
   72   grid->setPen( QPen( QColor( 0, 0, 0, 50 ) ) );
 
   73   grid->attach( mPlot );
 
   75   mPlotCurve = 
new QwtPlotCurve();
 
   76   mPlotCurve->setTitle( QStringLiteral( 
"Curve" ) );
 
   77   mPlotCurve->setPen( QPen( QColor( 30, 30, 30 ), 0.0 ) ),
 
   78              mPlotCurve->setRenderHint( QwtPlotItem::RenderAntialiased, 
true );
 
   79   mPlotCurve->attach( mPlot );
 
   81   mPlotFilter = 
new QgsCurveEditorPlotEventFilter( mPlot );
 
   82   connect( mPlotFilter, &QgsCurveEditorPlotEventFilter::mousePress, 
this, &QgsCurveEditorWidget::plotMousePress );
 
   83   connect( mPlotFilter, &QgsCurveEditorPlotEventFilter::mouseRelease, 
this, &QgsCurveEditorWidget::plotMouseRelease );
 
   84   connect( mPlotFilter, &QgsCurveEditorPlotEventFilter::mouseMove, 
this, &QgsCurveEditorWidget::plotMouseMove );
 
   86   mPlotCurve->setVisible( 
true );
 
   92   if ( mGatherer && mGatherer->isRunning() )
 
   94     connect( mGatherer.get(), &QgsHistogramValuesGatherer::finished, mGatherer.get(), &QgsHistogramValuesGatherer::deleteLater );
 
   96     ( void )mGatherer.release();
 
  111     mGatherer.reset( 
new QgsHistogramValuesGatherer() );
 
  112     connect( mGatherer.get(), &QgsHistogramValuesGatherer::calculatedHistogram, 
this, [ = ]
 
  114       mHistogram.reset( new QgsHistogram( mGatherer->histogram() ) );
 
  119   const bool changed = mGatherer->layer() != layer || mGatherer->expression() != expression;
 
  122     mGatherer->setExpression( expression );
 
  123     mGatherer->setLayer( layer );
 
  125     if ( mGatherer->isRunning() )
 
  129       while ( mGatherer->isRunning() )
 
  131         QCoreApplication::processEvents();
 
  144   mMinValueRange = minValueRange;
 
  150   mMaxValueRange = maxValueRange;
 
  156   if ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace )
 
  159     if ( mCurrentPlotMarkerIndex > 0 && mCurrentPlotMarkerIndex < cp.count() - 1 )
 
  161       cp.removeAt( mCurrentPlotMarkerIndex );
 
  169 void QgsCurveEditorWidget::plotMousePress( QPointF point )
 
  171   mCurrentPlotMarkerIndex = findNearestControlPoint( point );
 
  172   if ( mCurrentPlotMarkerIndex < 0 )
 
  176     mCurrentPlotMarkerIndex = findNearestControlPoint( point );
 
  183 int QgsCurveEditorWidget::findNearestControlPoint( QPointF point )
 const 
  185   double minDist = 3.0 / mPlot->width();
 
  186   int currentPlotMarkerIndex = -1;
 
  188   const QList< QgsPointXY > controlPoints = mCurve.
controlPoints();
 
  190   for ( 
int i = 0; i < controlPoints.count(); ++i )
 
  192     const QgsPointXY currentPoint = controlPoints.at( i );
 
  194     currentDist = std::pow( point.x() - currentPoint.
x(), 2.0 ) + std::pow( point.y() - currentPoint.
y(), 2.0 );
 
  195     if ( currentDist < minDist )
 
  197       minDist = currentDist;
 
  198       currentPlotMarkerIndex = i;
 
  201   return currentPlotMarkerIndex;
 
  205 void QgsCurveEditorWidget::plotMouseRelease( QPointF )
 
  209 void QgsCurveEditorWidget::plotMouseMove( QPointF point )
 
  211   if ( mCurrentPlotMarkerIndex < 0 )
 
  215   bool removePoint = 
false;
 
  216   if ( mCurrentPlotMarkerIndex == 0 )
 
  218     point.setX( std::min( point.x(), cp.at( 1 ).x() - 0.01 ) );
 
  222     removePoint = point.x() <= cp.at( mCurrentPlotMarkerIndex - 1 ).x();
 
  224   if ( mCurrentPlotMarkerIndex == cp.count() - 1 )
 
  226     point.setX( std::max( point.x(), cp.at( mCurrentPlotMarkerIndex - 1 ).x() + 0.01 ) );
 
  231     removePoint = removePoint || point.x() >= cp.at( mCurrentPlotMarkerIndex + 1 ).x();
 
  236     cp.removeAt( mCurrentPlotMarkerIndex );
 
  237     mCurrentPlotMarkerIndex = -1;
 
  241     cp[ mCurrentPlotMarkerIndex ] = 
QgsPointXY( point.x(), point.y() );
 
  248 void QgsCurveEditorWidget::addPlotMarker( 
double x, 
double y, 
bool isSelected )
 
  250   const QColor borderColor( 0, 0, 0 );
 
  252   const QColor brushColor = isSelected ? borderColor : QColor( 255, 255, 255, 0 );
 
  254   QwtPlotMarker *marker = 
new QwtPlotMarker();
 
  255   marker->setSymbol( 
new QwtSymbol( QwtSymbol::Ellipse,  QBrush( brushColor ), QPen( borderColor, isSelected ? 2 : 1 ), QSize( 8, 8 ) ) );
 
  256   marker->setValue( x, y );
 
  257   marker->attach( mPlot );
 
  258   marker->setRenderHint( QwtPlotItem::RenderAntialiased, 
true );
 
  262 void QgsCurveEditorWidget::updateHistogram()
 
  268   const QBrush histoBrush( QColor( 0, 0, 0, 70 ) );
 
  270   delete mPlotHistogram;
 
  271   mPlotHistogram = createPlotHistogram( histoBrush );
 
  272   QVector<QwtIntervalSample> dataHisto;
 
  275   QList<double> edges = mHistogram->binEdges( bins );
 
  276   const QList<int> counts = mHistogram->counts( bins );
 
  279   const double max = *std::max_element( counts.constBegin(), counts.constEnd() );
 
  284     std::transform( edges.begin(), edges.end(), edges.begin(),
 
  285                     [
this]( 
double d ) -> 
double { return ( d - mMinValueRange ) / ( mMaxValueRange - mMinValueRange ); } );
 
  288   for ( 
int bin = 0; bin < bins; ++bin )
 
  290     const double binValue = counts.at( bin ) / max;
 
  292     const double upperEdge = edges.at( bin + 1 );
 
  294     dataHisto << QwtIntervalSample( binValue, edges.at( bin ), upperEdge );
 
  297   mPlotHistogram->setSamples( dataHisto );
 
  298   mPlotHistogram->attach( mPlot );
 
  302 void QgsCurveEditorWidget::updatePlot()
 
  305   const auto constMMarkers = mMarkers;
 
  306   for ( QwtPlotMarker *marker : constMMarkers )
 
  313   QPolygonF curvePoints;
 
  318   for ( 
const QgsPointXY &point : constControlPoints )
 
  321     addPlotMarker( point.x(), point.y(), mCurrentPlotMarkerIndex == i );
 
  327   for ( 
double p = 0; p <= 1.0; p += 0.01 )
 
  331   std::sort( x.begin(), x.end() );
 
  332   const QVector< double > y = mCurve.
y( x );
 
  334   for ( 
int j = 0; j < x.count(); ++j )
 
  336     curvePoints << QPointF( x.at( j ), y.at( j ) );
 
  339   mPlotCurve->setSamples( curvePoints );
 
  343 QwtPlotHistogram *QgsCurveEditorWidget::createPlotHistogram( 
const QBrush &brush, 
const QPen &pen )
 const 
  345   QwtPlotHistogram *histogram = 
new QwtPlotHistogram( QString() );
 
  346   histogram->setBrush( brush );
 
  347   if ( pen != Qt::NoPen )
 
  349     histogram->setPen( pen );
 
  351   else if ( brush.color().lightness() > 200 )
 
  354     p.setColor( brush.color().darker( 150 ) );
 
  356     p.setCosmetic( 
true );
 
  357     histogram->setPen( p );
 
  361     histogram->setPen( QPen( Qt::NoPen ) );
 
  368 QgsCurveEditorPlotEventFilter::QgsCurveEditorPlotEventFilter( QwtPlot *plot )
 
  372   mPlot->canvas()->installEventFilter( 
this );
 
  375 bool QgsCurveEditorPlotEventFilter::eventFilter( QObject *
object, QEvent *event )
 
  377   if ( !mPlot->isEnabled() )
 
  378     return QObject::eventFilter( 
object, event );
 
  380   switch ( event->type() )
 
  382     case QEvent::MouseButtonPress:
 
  384       const QMouseEvent *mouseEvent = 
static_cast<QMouseEvent * 
>( event );
 
  385       if ( mouseEvent->button() == Qt::LeftButton )
 
  387         emit mousePress( mapPoint( mouseEvent->pos() ) );
 
  391     case QEvent::MouseMove:
 
  393       const QMouseEvent *mouseEvent = 
static_cast<QMouseEvent * 
>( event );
 
  394       if ( mouseEvent->buttons() & Qt::LeftButton )
 
  397         emit mouseMove( mapPoint( mouseEvent->pos() ) );
 
  401     case QEvent::MouseButtonRelease:
 
  403       const QMouseEvent *mouseEvent = 
static_cast<QMouseEvent * 
>( event );
 
  404       if ( mouseEvent->button() == Qt::LeftButton )
 
  406         emit mouseRelease( mapPoint( mouseEvent->pos() ) );
 
  414   return QObject::eventFilter( 
object, event );
 
  417 QPointF QgsCurveEditorPlotEventFilter::mapPoint( QPointF point )
 const 
  422   return QPointF( mPlot->canvasMap( QwtPlot::xBottom ).invTransform( point.x() ),
 
  423                   mPlot->canvasMap( QwtPlot::yLeft ).invTransform( point.y() ) );
 
A class to represent a 2D point.
Represents a vector layer which manages a vector based data sets.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)