QGIS API Documentation 4.1.0-Master (376402f9aeb)
Loading...
Searching...
No Matches
qgshistogramwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgshistogramwidget.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
18#include "qgshistogramwidget.h"
19
20#include "qgsapplication.h"
22#include "qgssettingstree.h"
24#include "qgsvectorlayer.h"
25#include "qgsvectorlayerutils.h"
26
27#include <QMouseEvent>
28#include <QObject>
29#include <QString>
30
31#include "moc_qgshistogramwidget.cpp"
32
33using namespace Qt::StringLiterals;
34
37
38// QWT Charting widget
39#include <qwt_global.h>
40#include <qwt_plot_canvas.h>
41#include <qwt_plot.h>
42#include <qwt_plot_curve.h>
43#include <qwt_plot_grid.h>
44#include <qwt_plot_marker.h>
45#include <qwt_plot_picker.h>
46#include <qwt_picker_machine.h>
47#include <qwt_plot_layout.h>
48#include <qwt_plot_renderer.h>
49#include <qwt_plot_histogram.h>
50#include <qwt_text.h>
51
52
53QgsHistogramWidget::QgsHistogramWidget( QWidget *parent, QgsVectorLayer *layer, const QString &fieldOrExp )
54 : QWidget( parent )
55 , mVectorLayer( layer )
56 , mSourceFieldExp( fieldOrExp )
57 , mXAxisTitle( QObject::tr( "Value" ) )
58 , mYAxisTitle( QObject::tr( "Count" ) )
59{
60 setupUi( this );
61
62 mPlot = mpPlot;
63
64 // hide the ugly canvas frame
65 QFrame *plotCanvasFrame = dynamic_cast<QFrame *>( mpPlot->canvas() );
66 if ( plotCanvasFrame )
67 plotCanvasFrame->setFrameStyle( QFrame::NoFrame );
68
69 mMeanCheckBox->setChecked( settingsHistogramShowMean->value() );
70 mStdevCheckBox->setChecked( settingsHistogramShowStdev->value() );
71
72 connect( mBinsSpinBox, static_cast<void ( QSpinBox::* )( int )>( &QSpinBox::valueChanged ), this, &QgsHistogramWidget::refresh );
73 connect( mMeanCheckBox, &QAbstractButton::toggled, this, &QgsHistogramWidget::refresh );
74 connect( mStdevCheckBox, &QAbstractButton::toggled, this, &QgsHistogramWidget::refresh );
75 connect( mLoadValuesButton, &QAbstractButton::clicked, this, &QgsHistogramWidget::refreshValues );
76
77 mGridPen = QPen( QColor( 0, 0, 0, 40 ) );
78 mMeanPen = QPen( QColor( 10, 10, 10, 220 ) );
79 mMeanPen.setStyle( Qt::DashLine );
80 mStdevPen = QPen( QColor( 30, 30, 30, 200 ) );
81 mStdevPen.setStyle( Qt::DashLine );
82
83 if ( layer && !mSourceFieldExp.isEmpty() )
84 {
85 refresh();
86 }
87}
88
90{
91 settingsHistogramShowMean->setValue( mMeanCheckBox->isChecked() );
92 settingsHistogramShowStdev->setValue( mStdevCheckBox->isChecked() );
93}
94
95static bool _rangesByLower( const QgsRendererRange &a, const QgsRendererRange &b )
96{
97 return a.lowerValue() < b.lowerValue();
98}
99
101{
102 mRanges = ranges;
103 std::sort( mRanges.begin(), mRanges.end(), _rangesByLower );
104}
105
107{
108 mValues.clear();
109
110 if ( !mVectorLayer || mSourceFieldExp.isEmpty() )
111 return;
112
113 QApplication::setOverrideCursor( Qt::WaitCursor );
114
115 bool ok;
116 mValues = QgsVectorLayerUtils::getDoubleValues( mVectorLayer, mSourceFieldExp, ok );
117
118 if ( !ok )
119 {
120 QApplication::restoreOverrideCursor();
121 return;
122 }
123
124
125 std::sort( mValues.begin(), mValues.end() );
126 mHistogram.setValues( mValues );
127 mBinsSpinBox->blockSignals( true );
128 mBinsSpinBox->setValue( std::max( mHistogram.optimalNumberBins(), 30 ) );
129 mBinsSpinBox->blockSignals( false );
130
131 mStats.setStatistics( Qgis::Statistic::StDev );
132 mStats.calculate( mValues );
133
134 mpPlot->setEnabled( true );
135 mMeanCheckBox->setEnabled( true );
136 mStdevCheckBox->setEnabled( true );
137 mBinsSpinBox->setEnabled( true );
138
139 QApplication::restoreOverrideCursor();
140
141 //also force a redraw
142 refresh();
143}
144
149
151{
152 if ( layer == mVectorLayer )
153 return;
154
155 mVectorLayer = layer;
156 clearHistogram();
157}
158
159void QgsHistogramWidget::clearHistogram()
160{
161 mValues.clear();
162 mHistogram.setValues( mValues );
163 refresh();
164
165 mpPlot->setEnabled( false );
166 mMeanCheckBox->setEnabled( false );
167 mStdevCheckBox->setEnabled( false );
168 mBinsSpinBox->setEnabled( false );
169}
170
171void QgsHistogramWidget::setSourceFieldExp( const QString &fieldOrExp )
172{
173 if ( fieldOrExp == mSourceFieldExp )
174 return;
175
176 mSourceFieldExp = fieldOrExp;
177 clearHistogram();
178}
179
181{
182 // clear plot
183 mpPlot->detachItems();
184
185 //ensure all children get removed
186 mpPlot->setAutoDelete( true );
187 // Set axis titles
188 if ( !mXAxisTitle.isEmpty() )
189 mpPlot->setAxisTitle( QwtPlot::xBottom, mXAxisTitle );
190 if ( !mYAxisTitle.isEmpty() )
191 mpPlot->setAxisTitle( QwtPlot::yLeft, mYAxisTitle );
192 mpPlot->setAxisFont( QwtPlot::xBottom, this->font() );
193 mpPlot->setAxisFont( QwtPlot::yLeft, this->font() );
194 QFont titleFont = this->font();
195 titleFont.setBold( true );
196 QwtText xAxisText = mpPlot->axisTitle( QwtPlot::xBottom );
197 xAxisText.setFont( titleFont );
198 mpPlot->setAxisTitle( QwtPlot::xBottom, xAxisText );
199 QwtText yAxisText = mpPlot->axisTitle( QwtPlot::yLeft );
200 yAxisText.setFont( titleFont );
201 mpPlot->setAxisTitle( QwtPlot::yLeft, yAxisText );
202 mpPlot->setAxisAutoScale( QwtPlot::yLeft );
203 mpPlot->setAxisAutoScale( QwtPlot::xBottom );
204
205 // add a grid
206 QwtPlotGrid *grid = new QwtPlotGrid();
207 grid->enableX( false );
208 grid->setPen( mGridPen );
209 grid->attach( mpPlot );
210
211 // make colors list
212 mHistoColors.clear();
213 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
214 {
215 mHistoColors << ( range.symbol() ? range.symbol()->color() : Qt::black );
216 }
217
218 //draw histogram
219 QwtPlotHistogram *plotHistogram = nullptr;
220 plotHistogram = createPlotHistogram( !mRanges.isEmpty() ? mRanges.at( 0 ).label() : QString(), !mRanges.isEmpty() ? QBrush( mHistoColors.at( 0 ) ) : mBrush, !mRanges.isEmpty() ? Qt::NoPen : mPen );
221 QVector<QwtIntervalSample> dataHisto;
222
223 int bins = mBinsSpinBox->value();
224 QList<double> edges = mHistogram.binEdges( bins );
225 QList<int> counts = mHistogram.counts( bins );
226
227 int rangeIndex = 0;
228 int lastValue = 0;
229
230 for ( int bin = 0; bin < bins; ++bin )
231 {
232 int binValue = counts.at( bin );
233
234 //current bin crosses two graduated ranges, so we split between
235 //two histogram items
236 if ( rangeIndex < mRanges.count() - 1 && edges.at( bin ) > mRanges.at( rangeIndex ).upperValue() )
237 {
238 rangeIndex++;
239 plotHistogram->setSamples( dataHisto );
240 plotHistogram->attach( mpPlot );
241 plotHistogram = createPlotHistogram( mRanges.at( rangeIndex ).label(), mHistoColors.at( rangeIndex ) );
242 dataHisto.clear();
243 dataHisto << QwtIntervalSample( lastValue, mRanges.at( rangeIndex - 1 ).upperValue(), edges.at( bin ) );
244 }
245
246 double upperEdge = !mRanges.isEmpty() ? std::min( edges.at( bin + 1 ), mRanges.at( rangeIndex ).upperValue() ) : edges.at( bin + 1 );
247
248 dataHisto << QwtIntervalSample( binValue, edges.at( bin ), upperEdge );
249
250 lastValue = binValue;
251 }
252
253 plotHistogram->setSamples( dataHisto );
254 plotHistogram->attach( mpPlot );
255
256 mRangeMarkers.clear();
257 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
258 {
259 QwtPlotMarker *rangeMarker = new QwtPlotMarker();
260 rangeMarker->attach( mpPlot );
261 rangeMarker->setLineStyle( QwtPlotMarker::VLine );
262 rangeMarker->setXValue( range.upperValue() );
263 rangeMarker->setLabel( QLocale().toString( range.upperValue() ) );
264 rangeMarker->setLabelOrientation( Qt::Vertical );
265 rangeMarker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
266 rangeMarker->show();
267 mRangeMarkers << rangeMarker;
268 }
269
270 if ( mMeanCheckBox->isChecked() )
271 {
272 QwtPlotMarker *meanMarker = new QwtPlotMarker();
273 meanMarker->attach( mpPlot );
274 meanMarker->setLineStyle( QwtPlotMarker::VLine );
275 meanMarker->setLinePen( mMeanPen );
276 meanMarker->setXValue( mStats.mean() );
277 meanMarker->setLabel( QString( QChar( 956 ) ) );
278 meanMarker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
279 meanMarker->show();
280 }
281
282 if ( mStdevCheckBox->isChecked() )
283 {
284 QwtPlotMarker *stdev1Marker = new QwtPlotMarker();
285 stdev1Marker->attach( mpPlot );
286 stdev1Marker->setLineStyle( QwtPlotMarker::VLine );
287 stdev1Marker->setLinePen( mStdevPen );
288 stdev1Marker->setXValue( mStats.mean() - mStats.stDev() );
289 stdev1Marker->setLabel( QString( QChar( 963 ) ) );
290 stdev1Marker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
291 stdev1Marker->show();
292
293 QwtPlotMarker *stdev2Marker = new QwtPlotMarker();
294 stdev2Marker->attach( mpPlot );
295 stdev2Marker->setLineStyle( QwtPlotMarker::VLine );
296 stdev2Marker->setLinePen( mStdevPen );
297 stdev2Marker->setXValue( mStats.mean() + mStats.stDev() );
298 stdev2Marker->setLabel( QString( QChar( 963 ) ) );
299 stdev2Marker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
300 stdev2Marker->show();
301 }
302
303 mpPlot->setEnabled( true );
304 mpPlot->replot();
305}
306
307QwtPlotHistogram *QgsHistogramWidget::createPlotHistogram( const QString &title, const QBrush &brush, const QPen &pen ) const
308{
309 QwtPlotHistogram *histogram = new QwtPlotHistogram( title );
310 histogram->setBrush( brush );
311 if ( pen != Qt::NoPen )
312 {
313 histogram->setPen( pen );
314 }
315 else if ( brush.color().lightness() > 200 )
316 {
317 QPen p;
318 p.setColor( brush.color().darker( 150 ) );
319 p.setWidth( 0 );
320 p.setCosmetic( true );
321 histogram->setPen( p );
322 }
323 else
324 {
325 histogram->setPen( QPen( Qt::NoPen ) );
326 }
327 return histogram;
328}
@ StDev
Standard deviation of values.
Definition qgis.h:6368
QList< QwtPlotMarker * > mRangeMarkers
void refresh()
Redraws the histogram.
static const QgsSettingsEntryBool * settingsHistogramShowStdev
QPen pen() const
Returns the pen used when drawing histogram bars.
static const QgsSettingsEntryBool * settingsHistogramShowMean
QgsHistogramWidget(QWidget *parent=nullptr, QgsVectorLayer *layer=nullptr, const QString &fieldOrExp=QString())
QgsHistogramWidget constructor.
virtual void drawHistogram()
Updates and redraws the histogram.
void setLayer(QgsVectorLayer *layer)
Sets the vector layer associated with the histogram.
void setGraduatedRanges(const QgsRangeList &ranges)
Sets the graduated ranges associated with the histogram.
QBrush brush() const
Returns the brush used when drawing histogram bars.
QgsVectorLayer * layer()
Returns the layer currently associated with the widget.
void setSourceFieldExp(const QString &fieldOrExp)
Sets the source field or expression to use for values in the histogram.
void refreshValues()
Refreshes the values for the histogram by fetching them from the layer.
void setValues(const QList< double > &values)
Assigns numeric source values for the histogram.
Represents a value range for a QgsGraduatedSymbolRenderer.
double lowerValue() const
Returns the lower bound of the range.
A boolean settings entry.
static QgsSettingsTreeNode * sTreeHistogram
static QList< double > getDoubleValues(const QgsVectorLayer *layer, const QString &fieldOrExpression, bool &ok, bool selectedOnly=false, int *nullCount=nullptr, QgsFeedback *feedback=nullptr)
Fetches all double values from a specified field name or expression.
Represents a vector layer which manages a vector based dataset.
QList< QgsRendererRange > QgsRangeList