QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
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#include "moc_qgsgraduatedhistogramwidget.cpp"
22#include "qgsapplication.h"
23#include "qgsvectorlayer.h"
25
26#include <QSettings>
27#include <QObject>
28#include <QMouseEvent>
29
30// QWT Charting widget
31#include <qwt_global.h>
32#include <qwt_plot_canvas.h>
33#include <qwt_plot.h>
34#include <qwt_plot_curve.h>
35#include <qwt_plot_grid.h>
36#include <qwt_plot_marker.h>
37#include <qwt_plot_picker.h>
38#include <qwt_picker_machine.h>
39#include <qwt_plot_layout.h>
40#include <qwt_plot_renderer.h>
41#include <qwt_plot_histogram.h>
42#include <qwt_scale_map.h>
43
44
46 : QgsHistogramWidget( parent )
47{
48 //clear x axis title to make more room for graph
49 setXAxisTitle( QString() );
50
51 mFilter = new QgsGraduatedHistogramEventFilter( mPlot );
52
53 connect( mFilter, &QgsGraduatedHistogramEventFilter::mousePress, this, &QgsGraduatedHistogramWidget::mousePress );
54 connect( mFilter, &QgsGraduatedHistogramEventFilter::mouseRelease, this, &QgsGraduatedHistogramWidget::mouseRelease );
55
56 mHistoPicker = new QwtPlotPicker( mPlot->canvas() );
57 mHistoPicker->setTrackerMode( QwtPicker::ActiveOnly );
58 mHistoPicker->setRubberBand( QwtPicker::VLineRubberBand );
59 mHistoPicker->setStateMachine( new QwtPickerDragPointMachine );
60}
61
63{
64 mRenderer = renderer;
65}
66
68{
69 if ( !mRenderer )
70 return;
71
72 bool pickerEnabled = false;
73 if ( mRenderer->rangesOverlap() )
74 {
75 setToolTip( tr( "Ranges are overlapping and can't be edited by the histogram" ) );
77 }
78 else if ( mRenderer->rangesHaveGaps() )
79 {
80 setToolTip( tr( "Ranges have gaps and can't be edited by the histogram" ) );
82 }
83 else if ( mRenderer->ranges().isEmpty() )
84 {
85 setToolTip( QString() );
87 }
88 else
89 {
90 setToolTip( QString() );
91 setGraduatedRanges( mRenderer->ranges() );
92 pickerEnabled = true;
93 }
95
96 // histo picker
97 mHistoPicker->setEnabled( pickerEnabled );
98 mFilter->blockSignals( !pickerEnabled );
99}
100
101void QgsGraduatedHistogramWidget::mousePress( double value )
102{
103 mPressedValue = value;
104
105 int closestRangeIndex = 0;
106 int minPixelDistance = 9999;
107 findClosestRange( mPressedValue, closestRangeIndex, minPixelDistance );
108
109 if ( minPixelDistance <= 6 )
110 {
111 //moving a break, so hide the break line
112 mRangeMarkers.at( closestRangeIndex )->hide();
113 mPlot->replot();
114 }
115}
116
117void QgsGraduatedHistogramWidget::mouseRelease( double value )
118{
119 int closestRangeIndex = 0;
120 int minPixelDistance = 9999;
121 findClosestRange( mPressedValue, closestRangeIndex, minPixelDistance );
122
123 if ( minPixelDistance <= 6 )
124 {
125 //do a sanity check - don't allow users to drag a break line
126 //into the middle of a different range. Doing so causes overlap
127 //of the ranges
128
129 //new value needs to be within range covered by closestRangeIndex or
130 //closestRangeIndex + 1
131 if ( value <= mRenderer->ranges().at( closestRangeIndex ).lowerValue() ||
132 value >= mRenderer->ranges().at( closestRangeIndex + 1 ).upperValue() )
133 {
134 refresh();
135 return;
136 }
137
138 mRenderer->updateRangeUpperValue( closestRangeIndex, value );
139 mRenderer->updateRangeLowerValue( closestRangeIndex + 1, value );
140 emit rangesModified( false );
141 }
142 else
143 {
144 //if distance from markers is too big, add a break
145 mRenderer->addBreak( value );
146 // to fix the deprecated call to reset() in QgsGraduatedSymbolRendererWidget::refreshRanges,
147 // this class should work on the model in the widget rather than adding break via the renderer.
148 emit rangesModified( true );
149 }
150
151 refresh();
152}
153
154void QgsGraduatedHistogramWidget::findClosestRange( double value, int &closestRangeIndex, int &pixelDistance ) const
155{
156 const QgsRangeList &ranges = mRenderer->ranges();
157
158 double minDistance = std::numeric_limits<double>::max();
159 const int pressedPixel = mPlot->canvasMap( QwtPlot::xBottom ).transform( value );
160 for ( int i = 0; i < ranges.count() - 1; ++i )
161 {
162 if ( std::fabs( mPressedValue - ranges.at( i ).upperValue() ) < minDistance )
163 {
164 closestRangeIndex = i;
165 minDistance = std::fabs( mPressedValue - ranges.at( i ).upperValue() );
166 pixelDistance = std::fabs( pressedPixel - mPlot->canvasMap( QwtPlot::xBottom ).transform( ranges.at( i ).upperValue() ) );
167 }
168 }
169}
170
172
173QgsGraduatedHistogramEventFilter::QgsGraduatedHistogramEventFilter( QwtPlot *plot )
174 : QObject( plot )
175 , mPlot( plot )
176{
177 mPlot->canvas()->installEventFilter( this );
178}
179
180bool QgsGraduatedHistogramEventFilter::eventFilter( QObject *object, QEvent *event )
181{
182 if ( !mPlot->isEnabled() )
183 return QObject::eventFilter( object, event );
184
185 switch ( event->type() )
186 {
187 case QEvent::MouseButtonPress:
188 {
189 const QMouseEvent *mouseEvent = static_cast<QMouseEvent * >( event );
190 if ( mouseEvent->button() == Qt::LeftButton )
191 {
192 emit mousePress( posToValue( mouseEvent->pos() ) );
193 }
194 break;
195 }
196 case QEvent::MouseButtonRelease:
197 {
198 const QMouseEvent *mouseEvent = static_cast<QMouseEvent * >( event );
199 if ( mouseEvent->button() == Qt::LeftButton )
200 {
201 emit mouseRelease( posToValue( mouseEvent->pos() ) );
202 }
203 break;
204 }
205 default:
206 break;
207 }
208
209 return QObject::eventFilter( object, event );
210}
211
212double QgsGraduatedHistogramEventFilter::posToValue( QPointF point ) const
213{
214 if ( !mPlot )
215 return -99999999;
216
217 return mPlot->canvasMap( QwtPlot::xBottom ).invTransform( point.x() );
218}
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.
void addBreak(double breakValue, bool updateSymbols=true)
Add a breakpoint by splitting existing classes so that the specified value becomes a break between tw...
bool rangesOverlap() const
Tests whether classes assigned to the renderer have ranges which overlap.
bool updateRangeUpperValue(int rangeIndex, double value)
bool updateRangeLowerValue(int rangeIndex, double value)
const QgsRangeList & ranges() const
Returns a list of all ranges used in the classification.
bool rangesHaveGaps() const
Tests whether classes assigned to the renderer have gaps between the ranges.
Graphical histogram for displaying distributions of field values.
void refresh()
Redraws the histogram.
QList< QwtPlotMarker * > mRangeMarkers
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