QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgsgraduatedhistogramwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgraduatedhistogramwidget.cpp
3 -------------------------------
4 begin : May 2015
5 copyright : (C) 2015 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include "qgsapplication.h"
24#include "qgsvectorlayer.h"
25
26#include <QMouseEvent>
27#include <QObject>
28#include <QSettings>
29
30#include "moc_qgsgraduatedhistogramwidget.cpp"
31
32// QWT Charting widget
33#include <qwt_global.h>
34#include <qwt_plot_canvas.h>
35#include <qwt_plot.h>
36#include <qwt_plot_curve.h>
37#include <qwt_plot_grid.h>
38#include <qwt_plot_marker.h>
39#include <qwt_plot_picker.h>
40#include <qwt_picker_machine.h>
41#include <qwt_plot_layout.h>
42#include <qwt_plot_renderer.h>
43#include <qwt_plot_histogram.h>
44#include <qwt_scale_map.h>
45
46
48 : QgsHistogramWidget( parent )
49{
50 //clear x axis title to make more room for graph
51 setXAxisTitle( QString() );
52
53 mFilter = new QgsGraduatedHistogramEventFilter( mPlot );
54
55 connect( mFilter, &QgsGraduatedHistogramEventFilter::mousePress, this, &QgsGraduatedHistogramWidget::mousePress );
56 connect( mFilter, &QgsGraduatedHistogramEventFilter::mouseRelease, this, &QgsGraduatedHistogramWidget::mouseRelease );
57
58 mHistoPicker = new QwtPlotPicker( mPlot->canvas() );
59 mHistoPicker->setTrackerMode( QwtPicker::ActiveOnly );
60 mHistoPicker->setRubberBand( QwtPicker::VLineRubberBand );
61 mHistoPicker->setStateMachine( new QwtPickerDragPointMachine );
62}
63
65{
66 mRenderer = renderer;
67}
68
70{
71 if ( !mRenderer )
72 return;
73
74 bool pickerEnabled = false;
75 if ( mRenderer->rangesOverlap() )
76 {
77 setToolTip( tr( "Ranges are overlapping and can't be edited by the histogram" ) );
79 }
80 else if ( mRenderer->rangesHaveGaps() )
81 {
82 setToolTip( tr( "Ranges have gaps and can't be edited by the histogram" ) );
84 }
85 else if ( mRenderer->ranges().isEmpty() )
86 {
87 setToolTip( QString() );
89 }
90 else
91 {
92 setToolTip( QString() );
93 setGraduatedRanges( mRenderer->ranges() );
94 pickerEnabled = true;
95 }
97
98 // histo picker
99 mHistoPicker->setEnabled( pickerEnabled );
100 mFilter->blockSignals( !pickerEnabled );
101}
102
103void QgsGraduatedHistogramWidget::mousePress( double value )
104{
105 mPressedValue = value;
106
107 int closestRangeIndex = 0;
108 int minPixelDistance = 9999;
109 findClosestRange( mPressedValue, closestRangeIndex, minPixelDistance );
110
111 if ( minPixelDistance <= 6 )
112 {
113 //moving a break, so hide the break line
114 mRangeMarkers.at( closestRangeIndex )->hide();
115 mPlot->replot();
116 }
117}
118
119void QgsGraduatedHistogramWidget::mouseRelease( double value )
120{
121 int closestRangeIndex = 0;
122 int minPixelDistance = 9999;
123 findClosestRange( mPressedValue, closestRangeIndex, minPixelDistance );
124
125 if ( minPixelDistance <= 6 )
126 {
127 //do a sanity check - don't allow users to drag a break line
128 //into the middle of a different range. Doing so causes overlap
129 //of the ranges
130
131 //new value needs to be within range covered by closestRangeIndex or
132 //closestRangeIndex + 1
133 if ( value <= mRenderer->ranges().at( closestRangeIndex ).lowerValue() || value >= mRenderer->ranges().at( closestRangeIndex + 1 ).upperValue() )
134 {
135 refresh();
136 return;
137 }
138
139 mRenderer->updateRangeUpperValue( closestRangeIndex, value );
140 mRenderer->updateRangeLowerValue( closestRangeIndex + 1, value );
141 emit rangesModified( false );
142 }
143 else
144 {
145 //if distance from markers is too big, add a break
146 mRenderer->addBreak( value );
147 // to fix the deprecated call to reset() in QgsGraduatedSymbolRendererWidget::refreshRanges,
148 // this class should work on the model in the widget rather than adding break via the renderer.
149 emit rangesModified( true );
150 }
151
152 refresh();
153}
154
155void QgsGraduatedHistogramWidget::findClosestRange( double value, int &closestRangeIndex, int &pixelDistance ) const
156{
157 const QgsRangeList &ranges = mRenderer->ranges();
158
159 double minDistance = std::numeric_limits<double>::max();
160 const int pressedPixel = mPlot->canvasMap( QwtPlot::xBottom ).transform( value );
161 for ( int i = 0; i < ranges.count() - 1; ++i )
162 {
163 if ( std::fabs( mPressedValue - ranges.at( i ).upperValue() ) < minDistance )
164 {
165 closestRangeIndex = i;
166 minDistance = std::fabs( mPressedValue - ranges.at( i ).upperValue() );
167 pixelDistance = std::fabs( pressedPixel - mPlot->canvasMap( QwtPlot::xBottom ).transform( ranges.at( i ).upperValue() ) );
168 }
169 }
170}
171
173
174QgsGraduatedHistogramEventFilter::QgsGraduatedHistogramEventFilter( QwtPlot *plot )
175 : QObject( plot )
176 , mPlot( plot )
177{
178 mPlot->canvas()->installEventFilter( this );
179}
180
181bool QgsGraduatedHistogramEventFilter::eventFilter( QObject *object, QEvent *event )
182{
183 if ( !mPlot->isEnabled() )
184 return QObject::eventFilter( object, event );
185
186 switch ( event->type() )
187 {
188 case QEvent::MouseButtonPress:
189 {
190 const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>( event );
191 if ( mouseEvent->button() == Qt::LeftButton )
192 {
193 emit mousePress( posToValue( mouseEvent->pos() ) );
194 }
195 break;
196 }
197 case QEvent::MouseButtonRelease:
198 {
199 const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>( event );
200 if ( mouseEvent->button() == Qt::LeftButton )
201 {
202 emit mouseRelease( posToValue( mouseEvent->pos() ) );
203 }
204 break;
205 }
206 default:
207 break;
208 }
209
210 return QObject::eventFilter( object, event );
211}
212
213double QgsGraduatedHistogramEventFilter::posToValue( QPointF point ) const
214{
215 if ( !mPlot )
216 return -99999999;
217
218 return mPlot->canvasMap( QwtPlot::xBottom ).invTransform( point.x() );
219}
void rangesModified(bool rangesAdded)
Emitted when the user modifies the graduated ranges using the histogram widget.
QgsGraduatedHistogramWidget(QWidget *parent=nullptr)
QgsGraduatedHistogramWidget constructor.
void setRenderer(QgsGraduatedSymbolRenderer *renderer)
Sets the QgsGraduatedSymbolRenderer renderer associated with the histogram.
void drawHistogram() override
Updates and redraws the histogram.
A vector feature renderer which uses numeric attributes to classify features into different ranges.
QList< QwtPlotMarker * > mRangeMarkers
void refresh()
Redraws the histogram.
QgsHistogramWidget(QWidget *parent=nullptr, QgsVectorLayer *layer=nullptr, const QString &fieldOrExp=QString())
QgsHistogramWidget constructor.
void setXAxisTitle(const QString &title)
Sets the title for the histogram's x-axis.
virtual void drawHistogram()
Updates and redraws the histogram.
void setGraduatedRanges(const QgsRangeList &ranges)
Sets the graduated ranges associated with the histogram.
QList< QgsRendererRange > QgsRangeList