20 #include <QVBoxLayout> 21 #include <QMouseEvent> 25 #include <qwt_global.h> 26 #include <qwt_plot_canvas.h> 28 #include <qwt_plot_curve.h> 29 #include <qwt_plot_grid.h> 30 #include <qwt_plot_marker.h> 31 #include <qwt_plot_picker.h> 32 #include <qwt_picker_machine.h> 33 #include <qwt_plot_layout.h> 34 #include <qwt_symbol.h> 35 #include <qwt_legend.h> 37 #include <qwt_plot_renderer.h> 38 #include <qwt_plot_histogram.h> 44 mPlot =
new QwtPlot();
45 mPlot->setMinimumSize( QSize( 0, 100 ) );
46 mPlot->setAxisScale( QwtPlot::yLeft, 0, 1 );
47 mPlot->setAxisScale( QwtPlot::yRight, 0, 1 );
48 mPlot->setAxisScale( QwtPlot::xBottom, 0, 1 );
49 mPlot->setAxisScale( QwtPlot::xTop, 0, 1 );
51 QVBoxLayout *vlayout =
new QVBoxLayout();
52 vlayout->addWidget( mPlot );
56 mPlot->setFrameStyle( QFrame::NoFrame );
57 QFrame *plotCanvasFrame =
dynamic_cast<QFrame *
>( mPlot->canvas() );
58 if ( plotCanvasFrame )
59 plotCanvasFrame->setFrameStyle( QFrame::NoFrame );
61 mPlot->enableAxis( QwtPlot::yLeft,
false );
62 mPlot->enableAxis( QwtPlot::xBottom,
false );
65 QwtPlotGrid *grid =
new QwtPlotGrid();
66 QwtScaleDiv gridDiv( 0.0, 1.0, QList<double>(), QList<double>(), QList<double>() << 0.2 << 0.4 << 0.6 << 0.8 );
67 grid->setXDiv( gridDiv );
68 grid->setYDiv( gridDiv );
69 grid->setPen( QPen( QColor( 0, 0, 0, 50 ) ) );
70 grid->attach( mPlot );
72 mPlotCurve =
new QwtPlotCurve();
73 mPlotCurve->setTitle( QStringLiteral(
"Curve" ) );
74 mPlotCurve->setPen( QPen( QColor( 30, 30, 30 ), 0.0 ) ),
75 mPlotCurve->setRenderHint( QwtPlotItem::RenderAntialiased,
true );
76 mPlotCurve->attach( mPlot );
78 mPlotFilter =
new QgsCurveEditorPlotEventFilter( mPlot );
79 connect( mPlotFilter, &QgsCurveEditorPlotEventFilter::mousePress,
this, &QgsCurveEditorWidget::plotMousePress );
80 connect( mPlotFilter, &QgsCurveEditorPlotEventFilter::mouseRelease,
this, &QgsCurveEditorWidget::plotMouseRelease );
81 connect( mPlotFilter, &QgsCurveEditorPlotEventFilter::mouseMove,
this, &QgsCurveEditorWidget::plotMouseMove );
83 mPlotCurve->setVisible(
true );
89 if ( mGatherer && mGatherer->isRunning() )
91 connect( mGatherer.get(), &QgsHistogramValuesGatherer::finished, mGatherer.get(), &QgsHistogramValuesGatherer::deleteLater );
93 ( void )mGatherer.release();
108 mGatherer.reset(
new QgsHistogramValuesGatherer() );
109 connect( mGatherer.get(), &QgsHistogramValuesGatherer::calculatedHistogram,
this, [ = ]
111 mHistogram.reset(
new QgsHistogram( mGatherer->histogram() ) );
116 bool changed = mGatherer->layer() != layer || mGatherer->expression() != expression;
119 mGatherer->setExpression( expression );
120 mGatherer->setLayer( layer );
122 if ( mGatherer->isRunning() )
126 while ( mGatherer->isRunning() )
128 QCoreApplication::processEvents();
141 mMinValueRange = minValueRange;
147 mMaxValueRange = maxValueRange;
153 if ( event->key() == Qt::Key_Delete ||
event->key() == Qt::Key_Backspace )
156 if ( mCurrentPlotMarkerIndex > 0 && mCurrentPlotMarkerIndex < cp.count() - 1 )
158 cp.removeAt( mCurrentPlotMarkerIndex );
166 void QgsCurveEditorWidget::plotMousePress( QPointF point )
168 mCurrentPlotMarkerIndex = findNearestControlPoint( point );
169 if ( mCurrentPlotMarkerIndex < 0 )
173 mCurrentPlotMarkerIndex = findNearestControlPoint( point );
180 int QgsCurveEditorWidget::findNearestControlPoint( QPointF point )
const 182 double minDist = 3.0 / mPlot->width();
183 int currentPlotMarkerIndex = -1;
187 for (
int i = 0; i < controlPoints.count(); ++i )
189 QgsPointXY currentPoint = controlPoints.at( i );
191 currentDist = std::pow( point.x() - currentPoint.
x(), 2.0 ) + std::pow( point.y() - currentPoint.
y(), 2.0 );
192 if ( currentDist < minDist )
194 minDist = currentDist;
195 currentPlotMarkerIndex = i;
198 return currentPlotMarkerIndex;
202 void QgsCurveEditorWidget::plotMouseRelease( QPointF )
206 void QgsCurveEditorWidget::plotMouseMove( QPointF point )
208 if ( mCurrentPlotMarkerIndex < 0 )
212 bool removePoint =
false;
213 if ( mCurrentPlotMarkerIndex == 0 )
215 point.setX( std::min( point.x(), cp.at( 1 ).x() - 0.01 ) );
219 removePoint = point.x() <= cp.at( mCurrentPlotMarkerIndex - 1 ).x();
221 if ( mCurrentPlotMarkerIndex == cp.count() - 1 )
223 point.setX( std::max( point.x(), cp.at( mCurrentPlotMarkerIndex - 1 ).x() + 0.01 ) );
228 removePoint = removePoint || point.x() >= cp.at( mCurrentPlotMarkerIndex + 1 ).x();
233 cp.removeAt( mCurrentPlotMarkerIndex );
234 mCurrentPlotMarkerIndex = -1;
238 cp[ mCurrentPlotMarkerIndex ] =
QgsPointXY( point.x(), point.y() );
245 void QgsCurveEditorWidget::addPlotMarker(
double x,
double y,
bool isSelected )
247 QColor borderColor( 0, 0, 0 );
249 QColor brushColor = isSelected ? borderColor : QColor( 255, 255, 255, 0 );
251 QwtPlotMarker *marker =
new QwtPlotMarker();
252 marker->setSymbol(
new QwtSymbol( QwtSymbol::Ellipse, QBrush( brushColor ), QPen( borderColor, isSelected ? 2 : 1 ), QSize( 8, 8 ) ) );
253 marker->setValue( x, y );
254 marker->attach( mPlot );
255 marker->setRenderHint( QwtPlotItem::RenderAntialiased,
true );
259 void QgsCurveEditorWidget::updateHistogram()
265 QBrush histoBrush( QColor( 0, 0, 0, 70 ) );
267 delete mPlotHistogram;
268 mPlotHistogram = createPlotHistogram( histoBrush );
269 QVector<QwtIntervalSample> dataHisto;
272 QList<double> edges = mHistogram->binEdges( bins );
273 QList<int> counts = mHistogram->counts( bins );
276 double max = *std::max_element( counts.constBegin(), counts.constEnd() );
281 std::transform( edges.begin(), edges.end(), edges.begin(),
282 [
this](
double d ) ->
double {
return ( d - mMinValueRange ) / ( mMaxValueRange - mMinValueRange ); } );
285 for (
int bin = 0; bin < bins; ++bin )
287 double binValue = counts.at( bin ) / max;
289 double upperEdge = edges.at( bin + 1 );
291 dataHisto << QwtIntervalSample( binValue, edges.at( bin ), upperEdge );
294 mPlotHistogram->setSamples( dataHisto );
295 mPlotHistogram->attach( mPlot );
299 void QgsCurveEditorWidget::updatePlot()
302 Q_FOREACH ( QwtPlotMarker *marker, mMarkers )
309 QPolygonF curvePoints;
316 addPlotMarker( point.
x(), point.
y(), mCurrentPlotMarkerIndex == i );
322 for (
double p = 0; p <= 1.0; p += 0.01 )
326 std::sort( x.begin(), x.end() );
327 QVector< double > y = mCurve.
y( x );
329 for (
int j = 0; j < x.count(); ++j )
331 curvePoints << QPointF( x.at( j ), y.at( j ) );
334 mPlotCurve->setSamples( curvePoints );
338 QwtPlotHistogram *QgsCurveEditorWidget::createPlotHistogram(
const QBrush &brush,
const QPen &pen )
const 340 QwtPlotHistogram *histogram =
new QwtPlotHistogram( QString() );
341 histogram->setBrush( brush );
342 if ( pen != Qt::NoPen )
344 histogram->setPen( pen );
346 else if ( brush.color().lightness() > 200 )
349 p.setColor( brush.color().darker( 150 ) );
351 p.setCosmetic(
true );
352 histogram->setPen( p );
356 histogram->setPen( QPen( Qt::NoPen ) );
363 QgsCurveEditorPlotEventFilter::QgsCurveEditorPlotEventFilter( QwtPlot *plot )
367 mPlot->canvas()->installEventFilter(
this );
370 bool QgsCurveEditorPlotEventFilter::eventFilter( QObject *
object, QEvent *event )
372 if ( !mPlot->isEnabled() )
373 return QObject::eventFilter(
object, event );
375 switch ( event->type() )
377 case QEvent::MouseButtonPress:
379 const QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
380 if ( mouseEvent->button() == Qt::LeftButton )
382 emit mousePress( mapPoint( mouseEvent->pos() ) );
386 case QEvent::MouseMove:
388 const QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
389 if ( mouseEvent->buttons() & Qt::LeftButton )
392 emit mouseMove( mapPoint( mouseEvent->pos() ) );
396 case QEvent::MouseButtonRelease:
398 const QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
399 if ( mouseEvent->button() == Qt::LeftButton )
401 emit mouseRelease( mapPoint( mouseEvent->pos() ) );
409 return QObject::eventFilter(
object, event );
412 QPointF QgsCurveEditorPlotEventFilter::mapPoint( QPointF point )
const 417 return QPointF( mPlot->canvasMap( QwtPlot::xBottom ).invTransform( point.x() ),
418 mPlot->canvasMap( QwtPlot::yLeft ).invTransform( point.y() ) );
A class to represent a 2D point.
Calculator for a numeric histogram from a list of values.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Represents a vector layer which manages a vector based data sets.