QGIS API Documentation  2.12.0-Lyon
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 
21 #include "qgsapplication.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsstatisticalsummary.h"
24 
25 #include <QSettings>
26 #include <QObject>
27 #include <QMouseEvent>
28 
29 // QWT Charting widget
30 #include <qwt_global.h>
31 #include <qwt_plot_canvas.h>
32 #include <qwt_plot.h>
33 #include <qwt_plot_curve.h>
34 #include <qwt_plot_grid.h>
35 #include <qwt_plot_marker.h>
36 #include <qwt_plot_picker.h>
37 #include <qwt_picker_machine.h>
38 #include <qwt_plot_layout.h>
39 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
40 #include <qwt_plot_renderer.h>
41 #include <qwt_plot_histogram.h>
42 #else
43 #include "../raster/qwt5_histogram_item.h"
44 #endif
45 
46 
48  : QgsHistogramWidget( parent )
49  , mRenderer( 0 )
50  , mHistoPicker( 0 )
51  , mPressedValue( 0 )
52 {
53  //clear x axis title to make more room for graph
55 
56  mFilter = new QgsGraduatedHistogramEventFilter( mPlot );
57 
58  connect( mFilter, SIGNAL( mousePress( double ) ), this, SLOT( mousePress( double ) ) );
59  connect( mFilter, SIGNAL( mouseRelease( double ) ), this, SLOT( mouseRelease( double ) ) );
60 
61  mHistoPicker = new QwtPlotPicker( mPlot->canvas() );
62  mHistoPicker->setTrackerMode( QwtPicker::ActiveOnly );
63  mHistoPicker->setRubberBand( QwtPicker::VLineRubberBand );
64 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
65  mHistoPicker->setStateMachine( new QwtPickerDragPointMachine );
66 #else
67  mHistoPicker->setSelectionFlags( QwtPicker::PointSelection | QwtPicker::DragSelection );
68 #endif
69 }
70 
71 
73 {
74 }
75 
77 {
78  mRenderer = renderer;
79 }
80 
82 {
83  if ( !mRenderer )
84  return;
85 
86  bool pickerEnabled = false;
87  if ( mRenderer->rangesOverlap() )
88  {
89  setToolTip( tr( "Ranges are overlapping and can't be edited by the histogram" ) );
91  }
92  else if ( mRenderer->rangesHaveGaps() )
93  {
94  setToolTip( tr( "Ranges have gaps and can't be edited by the histogram" ) );
96  }
97  else if ( mRenderer->ranges().isEmpty() )
98  {
99  setToolTip( QString() );
101  }
102  else
103  {
104  setToolTip( QString() );
105  setGraduatedRanges( mRenderer->ranges() );
106  pickerEnabled = true;
107  }
109 
110  // histo picker
111  mHistoPicker->setEnabled( pickerEnabled );
112  mFilter->blockSignals( !pickerEnabled );
113 }
114 
115 void QgsGraduatedHistogramWidget::mousePress( double value )
116 {
117  mPressedValue = value;
118 
119  int closestRangeIndex = 0;
120  int minPixelDistance = 9999;
121  findClosestRange( mPressedValue, closestRangeIndex, minPixelDistance );
122 
123  if ( minPixelDistance <= 6 )
124  {
125  //moving a break, so hide the break line
126  mRangeMarkers.at( closestRangeIndex )->hide();
127  mPlot->replot();
128  }
129 }
130 
131 void QgsGraduatedHistogramWidget::mouseRelease( double value )
132 {
133  int closestRangeIndex = 0;
134  int minPixelDistance = 9999;
135  findClosestRange( mPressedValue, closestRangeIndex, minPixelDistance );
136 
137  if ( minPixelDistance <= 6 )
138  {
139  //do a sanity check - don't allow users to drag a break line
140  //into the middle of a different range. Doing so causes overlap
141  //of the ranges
142 
143  //new value needs to be within range covered by closestRangeIndex or
144  //closestRangeIndex + 1
145  if ( value <= mRenderer->ranges().at( closestRangeIndex ).lowerValue() ||
146  value >= mRenderer->ranges().at( closestRangeIndex + 1 ).upperValue() )
147  {
148  refresh();
149  return;
150  }
151 
152  mRenderer->updateRangeUpperValue( closestRangeIndex, value );
153  mRenderer->updateRangeLowerValue( closestRangeIndex + 1, value );
154  emit rangesModified( false );
155  }
156  else
157  {
158  //if distance from markers is too big, add a break
159  mRenderer->addBreak( value );
160  emit rangesModified( true );
161  }
162 
163  refresh();
164 }
165 
166 void QgsGraduatedHistogramWidget::findClosestRange( double value, int &closestRangeIndex, int& pixelDistance ) const
167 {
168  const QgsRangeList& ranges = mRenderer->ranges();
169 
170  double minDistance = DBL_MAX;
171  int pressedPixel = mPlot->canvasMap( QwtPlot::xBottom ).transform( value );
172  for ( int i = 0; i < ranges.count() - 1; ++i )
173  {
174  if ( qAbs( mPressedValue - ranges.at( i ).upperValue() ) < minDistance )
175  {
176  closestRangeIndex = i;
177  minDistance = qAbs( mPressedValue - ranges.at( i ).upperValue() );
178  pixelDistance = qAbs( pressedPixel - mPlot->canvasMap( QwtPlot::xBottom ).transform( ranges.at( i ).upperValue() ) );
179  }
180  }
181 }
182 
184  : QObject( plot )
185  , mPlot( plot )
186 {
187  mPlot->canvas()->installEventFilter( this );
188 }
189 
191 {
192  if ( !mPlot->isEnabled() )
193  return QObject::eventFilter( object, event );
194 
195  switch ( event->type() )
196  {
197  case QEvent::MouseButtonPress:
198  {
199  const QMouseEvent* mouseEvent = static_cast<QMouseEvent* >( event );
200  if ( mouseEvent->button() == Qt::LeftButton )
201  {
202  emit mousePress( posToValue( mouseEvent->pos() ) );
203  }
204  break;
205  }
206  case QEvent::MouseButtonRelease:
207  {
208  const QMouseEvent* mouseEvent = static_cast<QMouseEvent* >( event );
209  if ( mouseEvent->button() == Qt::LeftButton )
210  {
211  emit mouseRelease( posToValue( mouseEvent->pos() ) );
212  }
213  break;
214  }
215  default:
216  break;
217  }
218 
219  return QObject::eventFilter( object, event );
220 }
221 
222 double QgsGraduatedHistogramEventFilter::posToValue( const QPointF &point ) const
223 {
224  if ( !mPlot )
225  return -99999999;
226 
227  return mPlot->canvasMap( QwtPlot::xBottom ).invTransform( point.x() );
228 }
QgsGraduatedHistogramWidget(QWidget *parent=0)
QgsGraduatedHistogramWidget constructor.
virtual bool eventFilter(QObject *object, QEvent *event) override
QList< QgsRendererRangeV2 > QgsRangeList
Type type() const
void addBreak(double breakValue, bool updateSymbols=true)
Add a breakpoint by splitting existing classes so that the specified value becomes a break between tw...
const T & at(int i) const
void rangesModified(bool rangesAdded)
Emitted when the user modifies the graduated ranges using the histogram widget.
QString tr(const char *sourceText, const char *disambiguation, int n)
QList< QwtPlotMarker * > mRangeMarkers
virtual bool event(QEvent *e)
bool rangesHaveGaps() const
Tests whether classes assigned to the renderer have gaps between the ranges.
int count(const T &value) const
qreal x() const
void setGraduatedRanges(const QgsRangeList &ranges)
Sets the graduated ranges associated with the histogram.
Qt::MouseButton button() const
bool isEmpty() const
bool updateRangeLowerValue(int rangeIndex, double value)
virtual bool eventFilter(QObject *watched, QEvent *event)
bool blockSignals(bool block)
void refresh()
Redraws the histogram.
virtual void drawHistogram() override
Updates and redraws the histogram.
bool rangesOverlap() const
Tests whether classes assigned to the renderer have ranges which overlap.
virtual void drawHistogram()
Updates and redraws the histogram.
const QgsRangeList & ranges() const
bool updateRangeUpperValue(int rangeIndex, double value)
Graphical histogram for displaying distributions of field values.
const QPoint & pos() const
void setToolTip(const QString &)
void setXAxisTitle(const QString &title)
Sets the title for the histogram's x-axis.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setRenderer(QgsGraduatedSymbolRendererV2 *renderer)
Sets the QgsGraduatedSymbolRendererV2 renderer associated with the histogram.