29#include "moc_qgscurveeditorwidget.cpp"
31using namespace Qt::StringLiterals;
34#include <qwt_global.h>
35#include <qwt_plot_canvas.h>
37#include <qwt_plot_curve.h>
38#include <qwt_plot_grid.h>
39#include <qwt_plot_marker.h>
40#include <qwt_plot_picker.h>
41#include <qwt_picker_machine.h>
42#include <qwt_plot_layout.h>
43#include <qwt_symbol.h>
44#include <qwt_legend.h>
45#include <qwt_scale_div.h>
46#include <qwt_scale_map.h>
48#include <qwt_plot_renderer.h>
49#include <qwt_plot_histogram.h>
55 mPlot =
new QwtPlot();
56 mPlot->setMinimumSize( QSize( 0, 100 ) );
57 mPlot->setAxisScale( QwtPlot::yLeft, 0, 1 );
58 mPlot->setAxisScale( QwtPlot::yRight, 0, 1 );
59 mPlot->setAxisScale( QwtPlot::xBottom, 0, 1 );
60 mPlot->setAxisScale( QwtPlot::xTop, 0, 1 );
62 QVBoxLayout *vlayout =
new QVBoxLayout();
63 vlayout->addWidget( mPlot );
67 mPlot->setFrameStyle( QFrame::NoFrame );
68 QFrame *plotCanvasFrame =
dynamic_cast<QFrame *
>( mPlot->canvas() );
69 if ( plotCanvasFrame )
70 plotCanvasFrame->setFrameStyle( QFrame::NoFrame );
72 mPlot->enableAxis( QwtPlot::yLeft,
false );
73 mPlot->enableAxis( QwtPlot::xBottom,
false );
76 QwtPlotGrid *grid =
new QwtPlotGrid();
77 const QwtScaleDiv gridDiv( 0.0, 1.0, QList<double>(), QList<double>(), QList<double>() << 0.2 << 0.4 << 0.6 << 0.8 );
78 grid->setXDiv( gridDiv );
79 grid->setYDiv( gridDiv );
80 grid->setPen( QPen( QColor( 0, 0, 0, 50 ) ) );
81 grid->attach( mPlot );
83 mPlotCurve =
new QwtPlotCurve();
84 mPlotCurve->setTitle( u
"Curve"_s );
85 mPlotCurve->setPen( QPen( QColor( 30, 30, 30 ), 0.0 ) ),
86 mPlotCurve->setRenderHint( QwtPlotItem::RenderAntialiased,
true );
87 mPlotCurve->attach( mPlot );
89 mPlotFilter =
new QgsCurveEditorPlotEventFilter( mPlot );
90 connect( mPlotFilter, &QgsCurveEditorPlotEventFilter::mousePress,
this, &QgsCurveEditorWidget::plotMousePress );
91 connect( mPlotFilter, &QgsCurveEditorPlotEventFilter::mouseRelease,
this, &QgsCurveEditorWidget::plotMouseRelease );
92 connect( mPlotFilter, &QgsCurveEditorPlotEventFilter::mouseMove,
this, &QgsCurveEditorWidget::plotMouseMove );
94 mPlotCurve->setVisible(
true );
100 if ( mGatherer && mGatherer->isRunning() )
102 connect( mGatherer.get(), &QgsHistogramValuesGatherer::finished, mGatherer.get(), &QgsHistogramValuesGatherer::deleteLater );
104 ( void ) mGatherer.release();
119 mGatherer = std::make_unique<QgsHistogramValuesGatherer>();
120 connect( mGatherer.get(), &QgsHistogramValuesGatherer::calculatedHistogram,
this, [
this] {
121 mHistogram = std::make_unique<QgsHistogram>( mGatherer->histogram() );
126 const bool changed = mGatherer->layer() != layer || mGatherer->expression() != expression;
129 mGatherer->setExpression( expression );
130 mGatherer->setLayer( layer );
132 if ( mGatherer->isRunning() )
136 while ( mGatherer->isRunning() )
138 QCoreApplication::processEvents();
151 mMinValueRange = minValueRange;
157 mMaxValueRange = maxValueRange;
163 if ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace )
165 QList<QgsPointXY> cp = mCurve.controlPoints();
166 if ( mCurrentPlotMarkerIndex > 0 && mCurrentPlotMarkerIndex < cp.count() - 1 )
168 cp.removeAt( mCurrentPlotMarkerIndex );
169 mCurve.setControlPoints( cp );
176void QgsCurveEditorWidget::plotMousePress( QPointF point )
178 mCurrentPlotMarkerIndex = findNearestControlPoint( point );
179 if ( mCurrentPlotMarkerIndex < 0 )
183 mCurrentPlotMarkerIndex = findNearestControlPoint( point );
190int QgsCurveEditorWidget::findNearestControlPoint( QPointF point )
const
192 double minDist = 3.0 / mPlot->width();
193 int currentPlotMarkerIndex = -1;
195 const QList<QgsPointXY> controlPoints = mCurve.controlPoints();
197 for (
int i = 0; i < controlPoints.count(); ++i )
199 const QgsPointXY currentPoint = controlPoints.at( i );
201 currentDist = std::pow( point.x() - currentPoint.
x(), 2.0 ) + std::pow( point.y() - currentPoint.
y(), 2.0 );
202 if ( currentDist < minDist )
204 minDist = currentDist;
205 currentPlotMarkerIndex = i;
208 return currentPlotMarkerIndex;
212void QgsCurveEditorWidget::plotMouseRelease( QPointF )
216void QgsCurveEditorWidget::plotMouseMove( QPointF point )
218 if ( mCurrentPlotMarkerIndex < 0 )
221 QList<QgsPointXY> cp = mCurve.controlPoints();
222 bool removePoint =
false;
223 if ( mCurrentPlotMarkerIndex == 0 )
225 point.setX( std::min( point.x(), cp.at( 1 ).x() - 0.01 ) );
229 removePoint = point.x() <= cp.at( mCurrentPlotMarkerIndex - 1 ).x();
231 if ( mCurrentPlotMarkerIndex == cp.count() - 1 )
233 point.setX( std::max( point.x(), cp.at( mCurrentPlotMarkerIndex - 1 ).x() + 0.01 ) );
238 removePoint = removePoint || point.x() >= cp.at( mCurrentPlotMarkerIndex + 1 ).x();
243 cp.removeAt( mCurrentPlotMarkerIndex );
244 mCurrentPlotMarkerIndex = -1;
248 cp[mCurrentPlotMarkerIndex] = QgsPointXY( point.x(), point.y() );
250 mCurve.setControlPoints( cp );
255void QgsCurveEditorWidget::addPlotMarker(
double x,
double y,
bool isSelected )
257 const QColor borderColor( 0, 0, 0 );
259 const QColor brushColor = isSelected ? borderColor : QColor( 255, 255, 255, 0 );
261 QwtPlotMarker *marker =
new QwtPlotMarker();
262 marker->setSymbol(
new QwtSymbol( QwtSymbol::Ellipse, QBrush( brushColor ), QPen( borderColor, isSelected ? 2 : 1 ), QSize( 8, 8 ) ) );
263 marker->setValue( x, y );
264 marker->attach( mPlot );
265 marker->setRenderHint( QwtPlotItem::RenderAntialiased,
true );
269void QgsCurveEditorWidget::updateHistogram()
275 const QBrush histoBrush( QColor( 0, 0, 0, 70 ) );
277 delete mPlotHistogram;
278 mPlotHistogram = createPlotHistogram( histoBrush );
279 QVector<QwtIntervalSample> dataHisto;
282 QList<double> edges = mHistogram->binEdges( bins );
283 const QList<int> counts = mHistogram->counts( bins );
286 const double max = *std::max_element( counts.constBegin(), counts.constEnd() );
291 std::transform( edges.begin(), edges.end(), edges.begin(), [
this](
double d ) ->
double { return ( d - mMinValueRange ) / ( mMaxValueRange - mMinValueRange ); } );
294 for (
int bin = 0; bin < bins; ++bin )
296 const double binValue = counts.at( bin ) / max;
298 const double upperEdge = edges.at( bin + 1 );
300 dataHisto << QwtIntervalSample( binValue, edges.at( bin ), upperEdge );
303 mPlotHistogram->setSamples( dataHisto );
304 mPlotHistogram->attach( mPlot );
308void QgsCurveEditorWidget::updatePlot()
311 const auto constMMarkers = mMarkers;
312 for ( QwtPlotMarker *marker : constMMarkers )
319 QPolygonF curvePoints;
323 const auto constControlPoints = mCurve.controlPoints();
324 for (
const QgsPointXY &point : constControlPoints )
327 addPlotMarker( point.x(), point.y(), mCurrentPlotMarkerIndex == i );
333 for (
double p = 0; p <= 1.0; p += 0.01 )
337 std::sort( x.begin(), x.end() );
338 const QVector<double> y = mCurve.y( x );
340 for (
int j = 0; j < x.count(); ++j )
342 curvePoints << QPointF( x.at( j ), y.at( j ) );
345 mPlotCurve->setSamples( curvePoints );
349QwtPlotHistogram *QgsCurveEditorWidget::createPlotHistogram(
const QBrush &brush,
const QPen &pen )
const
351 QwtPlotHistogram *histogram =
new QwtPlotHistogram( QString() );
352 histogram->setBrush( brush );
353 if ( pen != Qt::NoPen )
355 histogram->setPen( pen );
357 else if ( brush.color().lightness() > 200 )
360 p.setColor( brush.color().darker( 150 ) );
362 p.setCosmetic(
true );
363 histogram->setPen( p );
367 histogram->setPen( QPen( Qt::NoPen ) );
374QgsCurveEditorPlotEventFilter::QgsCurveEditorPlotEventFilter( QwtPlot *plot )
378 mPlot->canvas()->installEventFilter(
this );
381bool QgsCurveEditorPlotEventFilter::eventFilter( QObject *
object, QEvent *event )
383 if ( !mPlot->isEnabled() )
384 return QObject::eventFilter(
object, event );
386 switch ( event->type() )
388 case QEvent::MouseButtonPress:
390 const QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
391 if ( mouseEvent->button() == Qt::LeftButton )
393 emit mousePress( mapPoint( mouseEvent->pos() ) );
397 case QEvent::MouseMove:
399 const QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
400 if ( mouseEvent->buttons() & Qt::LeftButton )
403 emit mouseMove( mapPoint( mouseEvent->pos() ) );
407 case QEvent::MouseButtonRelease:
409 const QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
410 if ( mouseEvent->button() == Qt::LeftButton )
412 emit mouseRelease( mapPoint( mouseEvent->pos() ) );
420 return QObject::eventFilter(
object, event );
423QPointF QgsCurveEditorPlotEventFilter::mapPoint( QPointF point )
const
428 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).