18#include "moc_qgscurveeditorwidget.cpp" 
   27#include <qwt_global.h> 
   28#include <qwt_plot_canvas.h> 
   30#include <qwt_plot_curve.h> 
   31#include <qwt_plot_grid.h> 
   32#include <qwt_plot_marker.h> 
   33#include <qwt_plot_picker.h> 
   34#include <qwt_picker_machine.h> 
   35#include <qwt_plot_layout.h> 
   36#include <qwt_symbol.h> 
   37#include <qwt_legend.h> 
   38#include <qwt_scale_div.h> 
   39#include <qwt_scale_map.h> 
   41#include <qwt_plot_renderer.h> 
   42#include <qwt_plot_histogram.h> 
   48  mPlot = 
new QwtPlot();
 
   49  mPlot->setMinimumSize( QSize( 0, 100 ) );
 
   50  mPlot->setAxisScale( QwtPlot::yLeft, 0, 1 );
 
   51  mPlot->setAxisScale( QwtPlot::yRight, 0, 1 );
 
   52  mPlot->setAxisScale( QwtPlot::xBottom, 0, 1 );
 
   53  mPlot->setAxisScale( QwtPlot::xTop, 0, 1 );
 
   55  QVBoxLayout *vlayout = 
new QVBoxLayout();
 
   56  vlayout->addWidget( mPlot );
 
   60  mPlot->setFrameStyle( QFrame::NoFrame );
 
   61  QFrame *plotCanvasFrame = 
dynamic_cast<QFrame *
>( mPlot->canvas() );
 
   62  if ( plotCanvasFrame )
 
   63    plotCanvasFrame->setFrameStyle( QFrame::NoFrame );
 
   65  mPlot->enableAxis( QwtPlot::yLeft, 
false );
 
   66  mPlot->enableAxis( QwtPlot::xBottom, 
false );
 
   69  QwtPlotGrid *grid = 
new QwtPlotGrid();
 
   70  const QwtScaleDiv gridDiv( 0.0, 1.0, QList<double>(), QList<double>(), QList<double>() << 0.2 << 0.4 << 0.6 << 0.8 );
 
   71  grid->setXDiv( gridDiv );
 
   72  grid->setYDiv( gridDiv );
 
   73  grid->setPen( QPen( QColor( 0, 0, 0, 50 ) ) );
 
   74  grid->attach( mPlot );
 
   76  mPlotCurve = 
new QwtPlotCurve();
 
   77  mPlotCurve->setTitle( QStringLiteral( 
"Curve" ) );
 
   78  mPlotCurve->setPen( QPen( QColor( 30, 30, 30 ), 0.0 ) ),
 
   79    mPlotCurve->setRenderHint( QwtPlotItem::RenderAntialiased, 
true );
 
   80  mPlotCurve->attach( mPlot );
 
   82  mPlotFilter = 
new QgsCurveEditorPlotEventFilter( mPlot );
 
   83  connect( mPlotFilter, &QgsCurveEditorPlotEventFilter::mousePress, 
this, &QgsCurveEditorWidget::plotMousePress );
 
   84  connect( mPlotFilter, &QgsCurveEditorPlotEventFilter::mouseRelease, 
this, &QgsCurveEditorWidget::plotMouseRelease );
 
   85  connect( mPlotFilter, &QgsCurveEditorPlotEventFilter::mouseMove, 
this, &QgsCurveEditorWidget::plotMouseMove );
 
   87  mPlotCurve->setVisible( 
true );
 
 
   93  if ( mGatherer && mGatherer->isRunning() )
 
   95    connect( mGatherer.get(), &QgsHistogramValuesGatherer::finished, mGatherer.get(), &QgsHistogramValuesGatherer::deleteLater );
 
   97    ( void ) mGatherer.release();
 
 
  112    mGatherer.reset( 
new QgsHistogramValuesGatherer() );
 
  113    connect( mGatherer.get(), &QgsHistogramValuesGatherer::calculatedHistogram, 
this, [
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 );
 
 
  169void QgsCurveEditorWidget::plotMousePress( QPointF point )
 
  171  mCurrentPlotMarkerIndex = findNearestControlPoint( point );
 
  172  if ( mCurrentPlotMarkerIndex < 0 )
 
  176    mCurrentPlotMarkerIndex = findNearestControlPoint( point );
 
  183int 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;
 
  205void QgsCurveEditorWidget::plotMouseRelease( QPointF )
 
  209void 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() );
 
  248void 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 );
 
  262void 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(), [
this]( 
double d ) -> 
double { return ( d - mMinValueRange ) / ( mMaxValueRange - mMinValueRange ); } );
 
  287  for ( 
int bin = 0; bin < bins; ++bin )
 
  289    const double binValue = counts.at( bin ) / max;
 
  291    const double upperEdge = edges.at( bin + 1 );
 
  293    dataHisto << QwtIntervalSample( binValue, edges.at( bin ), upperEdge );
 
  296  mPlotHistogram->setSamples( dataHisto );
 
  297  mPlotHistogram->attach( mPlot );
 
  301void QgsCurveEditorWidget::updatePlot()
 
  304  const auto constMMarkers = mMarkers;
 
  305  for ( QwtPlotMarker *marker : constMMarkers )
 
  312  QPolygonF curvePoints;
 
  317  for ( 
const QgsPointXY &point : constControlPoints )
 
  320    addPlotMarker( point.x(), point.y(), mCurrentPlotMarkerIndex == i );
 
  326  for ( 
double p = 0; p <= 1.0; p += 0.01 )
 
  330  std::sort( x.begin(), x.end() );
 
  331  const QVector<double> y = mCurve.
y( x );
 
  333  for ( 
int j = 0; j < x.count(); ++j )
 
  335    curvePoints << QPointF( x.at( j ), y.at( j ) );
 
  338  mPlotCurve->setSamples( curvePoints );
 
  342QwtPlotHistogram *QgsCurveEditorWidget::createPlotHistogram( 
const QBrush &brush, 
const QPen &pen )
 const 
  344  QwtPlotHistogram *histogram = 
new QwtPlotHistogram( QString() );
 
  345  histogram->setBrush( brush );
 
  346  if ( pen != Qt::NoPen )
 
  348    histogram->setPen( pen );
 
  350  else if ( brush.color().lightness() > 200 )
 
  353    p.setColor( brush.color().darker( 150 ) );
 
  355    p.setCosmetic( 
true );
 
  356    histogram->setPen( p );
 
  360    histogram->setPen( QPen( Qt::NoPen ) );
 
  367QgsCurveEditorPlotEventFilter::QgsCurveEditorPlotEventFilter( QwtPlot *plot )
 
  371  mPlot->canvas()->installEventFilter( 
this );
 
  374bool QgsCurveEditorPlotEventFilter::eventFilter( QObject *
object, QEvent *event )
 
  376  if ( !mPlot->isEnabled() )
 
  377    return QObject::eventFilter( 
object, event );
 
  379  switch ( event->type() )
 
  381    case QEvent::MouseButtonPress:
 
  383      const QMouseEvent *mouseEvent = 
static_cast<QMouseEvent *
>( event );
 
  384      if ( mouseEvent->button() == Qt::LeftButton )
 
  386        emit mousePress( mapPoint( mouseEvent->pos() ) );
 
  390    case QEvent::MouseMove:
 
  392      const QMouseEvent *mouseEvent = 
static_cast<QMouseEvent *
>( event );
 
  393      if ( mouseEvent->buttons() & Qt::LeftButton )
 
  396        emit mouseMove( mapPoint( mouseEvent->pos() ) );
 
  400    case QEvent::MouseButtonRelease:
 
  402      const QMouseEvent *mouseEvent = 
static_cast<QMouseEvent *
>( event );
 
  403      if ( mouseEvent->button() == Qt::LeftButton )
 
  405        emit mouseRelease( mapPoint( mouseEvent->pos() ) );
 
  413  return QObject::eventFilter( 
object, event );
 
  416QPointF QgsCurveEditorPlotEventFilter::mapPoint( QPointF point )
 const 
  421  return QPointF( mPlot->canvasMap( QwtPlot::xBottom ).invTransform( point.x() ), mPlot->canvasMap( QwtPlot::yLeft ).invTransform( point.y() ) );
 
Represents a vector layer which manages a vector based dataset.
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)