QGIS API Documentation 3.41.0-Master (cea29feecf2)
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#include "moc_qgshistogramwidget.cpp"
20#include "qgsapplication.h"
21#include "qgsvectorlayer.h"
22#include "qgsvectorlayerutils.h"
24#include "qgssettings.h"
25
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#include <qwt_plot_renderer.h>
40#include <qwt_plot_histogram.h>
41#include <qwt_text.h>
42
43
44QgsHistogramWidget::QgsHistogramWidget( QWidget *parent, QgsVectorLayer *layer, const QString &fieldOrExp )
45 : QWidget( parent )
46 , mVectorLayer( layer )
47 , mSourceFieldExp( fieldOrExp )
48 , mXAxisTitle( QObject::tr( "Value" ) )
49 , mYAxisTitle( QObject::tr( "Count" ) )
50{
51 setupUi( this );
52
53 mPlot = mpPlot;
54
55 // hide the ugly canvas frame
56 QFrame *plotCanvasFrame = dynamic_cast<QFrame *>( mpPlot->canvas() );
57 if ( plotCanvasFrame )
58 plotCanvasFrame->setFrameStyle( QFrame::NoFrame );
59
60 QgsSettings settings;
61 mMeanCheckBox->setChecked( settings.value( QStringLiteral( "HistogramWidget/showMean" ), false ).toBool() );
62 mStdevCheckBox->setChecked( settings.value( QStringLiteral( "HistogramWidget/showStdev" ), false ).toBool() );
63
64 connect( mBinsSpinBox, static_cast<void ( QSpinBox::* )( int )>( &QSpinBox::valueChanged ), this, &QgsHistogramWidget::refresh );
65 connect( mMeanCheckBox, &QAbstractButton::toggled, this, &QgsHistogramWidget::refresh );
66 connect( mStdevCheckBox, &QAbstractButton::toggled, this, &QgsHistogramWidget::refresh );
67 connect( mLoadValuesButton, &QAbstractButton::clicked, this, &QgsHistogramWidget::refreshValues );
68
69 mGridPen = QPen( QColor( 0, 0, 0, 40 ) );
70 mMeanPen = QPen( QColor( 10, 10, 10, 220 ) );
71 mMeanPen.setStyle( Qt::DashLine );
72 mStdevPen = QPen( QColor( 30, 30, 30, 200 ) );
73 mStdevPen.setStyle( Qt::DashLine );
74
75 if ( layer && !mSourceFieldExp.isEmpty() )
76 {
77 refresh();
78 }
79}
80
82{
83 QgsSettings settings;
84 settings.setValue( QStringLiteral( "HistogramWidget/showMean" ), mMeanCheckBox->isChecked() );
85 settings.setValue( QStringLiteral( "HistogramWidget/showStdev" ), mStdevCheckBox->isChecked() );
86}
87
88static bool _rangesByLower( const QgsRendererRange &a, const QgsRendererRange &b )
89{
90 return a.lowerValue() < b.lowerValue();
91}
92
94{
95 mRanges = ranges;
96 std::sort( mRanges.begin(), mRanges.end(), _rangesByLower );
97}
98
100{
101 mValues.clear();
102
103 if ( !mVectorLayer || mSourceFieldExp.isEmpty() )
104 return;
105
106 QApplication::setOverrideCursor( Qt::WaitCursor );
107
108 bool ok;
109 mValues = QgsVectorLayerUtils::getDoubleValues( mVectorLayer, mSourceFieldExp, ok );
110
111 if ( !ok )
112 {
113 QApplication::restoreOverrideCursor();
114 return;
115 }
116
117
118 std::sort( mValues.begin(), mValues.end() );
119 mHistogram.setValues( mValues );
120 mBinsSpinBox->blockSignals( true );
121 mBinsSpinBox->setValue( std::max( mHistogram.optimalNumberBins(), 30 ) );
122 mBinsSpinBox->blockSignals( false );
123
125 mStats.calculate( mValues );
126
127 mpPlot->setEnabled( true );
128 mMeanCheckBox->setEnabled( true );
129 mStdevCheckBox->setEnabled( true );
130 mBinsSpinBox->setEnabled( true );
131
132 QApplication::restoreOverrideCursor();
133
134 //also force a redraw
135 refresh();
136}
137
142
144{
145 if ( layer == mVectorLayer )
146 return;
147
148 mVectorLayer = layer;
149 clearHistogram();
150}
151
152void QgsHistogramWidget::clearHistogram()
153{
154 mValues.clear();
155 mHistogram.setValues( mValues );
156 refresh();
157
158 mpPlot->setEnabled( false );
159 mMeanCheckBox->setEnabled( false );
160 mStdevCheckBox->setEnabled( false );
161 mBinsSpinBox->setEnabled( false );
162}
163
164void QgsHistogramWidget::setSourceFieldExp( const QString &fieldOrExp )
165{
166 if ( fieldOrExp == mSourceFieldExp )
167 return;
168
169 mSourceFieldExp = fieldOrExp;
170 clearHistogram();
171}
172
174{
175 // clear plot
176 mpPlot->detachItems();
177
178 //ensure all children get removed
179 mpPlot->setAutoDelete( true );
180 // Set axis titles
181 if ( !mXAxisTitle.isEmpty() )
182 mpPlot->setAxisTitle( QwtPlot::xBottom, mXAxisTitle );
183 if ( !mYAxisTitle.isEmpty() )
184 mpPlot->setAxisTitle( QwtPlot::yLeft, mYAxisTitle );
185 mpPlot->setAxisFont( QwtPlot::xBottom, this->font() );
186 mpPlot->setAxisFont( QwtPlot::yLeft, this->font() );
187 QFont titleFont = this->font();
188 titleFont.setBold( true );
189 QwtText xAxisText = mpPlot->axisTitle( QwtPlot::xBottom );
190 xAxisText.setFont( titleFont );
191 mpPlot->setAxisTitle( QwtPlot::xBottom, xAxisText );
192 QwtText yAxisText = mpPlot->axisTitle( QwtPlot::yLeft );
193 yAxisText.setFont( titleFont );
194 mpPlot->setAxisTitle( QwtPlot::yLeft, yAxisText );
195 mpPlot->setAxisAutoScale( QwtPlot::yLeft );
196 mpPlot->setAxisAutoScale( QwtPlot::xBottom );
197
198 // add a grid
199 QwtPlotGrid *grid = new QwtPlotGrid();
200 grid->enableX( false );
201 grid->setPen( mGridPen );
202 grid->attach( mpPlot );
203
204 // make colors list
205 mHistoColors.clear();
206 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
207 {
208 mHistoColors << ( range.symbol() ? range.symbol()->color() : Qt::black );
209 }
210
211 //draw histogram
212 QwtPlotHistogram *plotHistogram = nullptr;
213 plotHistogram = createPlotHistogram( !mRanges.isEmpty() ? mRanges.at( 0 ).label() : QString(), !mRanges.isEmpty() ? QBrush( mHistoColors.at( 0 ) ) : mBrush, !mRanges.isEmpty() ? Qt::NoPen : mPen );
214 QVector<QwtIntervalSample> dataHisto;
215
216 int bins = mBinsSpinBox->value();
217 QList<double> edges = mHistogram.binEdges( bins );
218 QList<int> counts = mHistogram.counts( bins );
219
220 int rangeIndex = 0;
221 int lastValue = 0;
222
223 for ( int bin = 0; bin < bins; ++bin )
224 {
225 int binValue = counts.at( bin );
226
227 //current bin crosses two graduated ranges, so we split between
228 //two histogram items
229 if ( rangeIndex < mRanges.count() - 1 && edges.at( bin ) > mRanges.at( rangeIndex ).upperValue() )
230 {
231 rangeIndex++;
232 plotHistogram->setSamples( dataHisto );
233 plotHistogram->attach( mpPlot );
234 plotHistogram = createPlotHistogram( mRanges.at( rangeIndex ).label(), mHistoColors.at( rangeIndex ) );
235 dataHisto.clear();
236 dataHisto << QwtIntervalSample( lastValue, mRanges.at( rangeIndex - 1 ).upperValue(), edges.at( bin ) );
237 }
238
239 double upperEdge = !mRanges.isEmpty() ? std::min( edges.at( bin + 1 ), mRanges.at( rangeIndex ).upperValue() )
240 : edges.at( bin + 1 );
241
242 dataHisto << QwtIntervalSample( binValue, edges.at( bin ), upperEdge );
243
244 lastValue = binValue;
245 }
246
247 plotHistogram->setSamples( dataHisto );
248 plotHistogram->attach( mpPlot );
249
250 mRangeMarkers.clear();
251 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
252 {
253 QwtPlotMarker *rangeMarker = new QwtPlotMarker();
254 rangeMarker->attach( mpPlot );
255 rangeMarker->setLineStyle( QwtPlotMarker::VLine );
256 rangeMarker->setXValue( range.upperValue() );
257 rangeMarker->setLabel( QLocale().toString( range.upperValue() ) );
258 rangeMarker->setLabelOrientation( Qt::Vertical );
259 rangeMarker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
260 rangeMarker->show();
261 mRangeMarkers << rangeMarker;
262 }
263
264 if ( mMeanCheckBox->isChecked() )
265 {
266 QwtPlotMarker *meanMarker = new QwtPlotMarker();
267 meanMarker->attach( mpPlot );
268 meanMarker->setLineStyle( QwtPlotMarker::VLine );
269 meanMarker->setLinePen( mMeanPen );
270 meanMarker->setXValue( mStats.mean() );
271 meanMarker->setLabel( QString( QChar( 956 ) ) );
272 meanMarker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
273 meanMarker->show();
274 }
275
276 if ( mStdevCheckBox->isChecked() )
277 {
278 QwtPlotMarker *stdev1Marker = new QwtPlotMarker();
279 stdev1Marker->attach( mpPlot );
280 stdev1Marker->setLineStyle( QwtPlotMarker::VLine );
281 stdev1Marker->setLinePen( mStdevPen );
282 stdev1Marker->setXValue( mStats.mean() - mStats.stDev() );
283 stdev1Marker->setLabel( QString( QChar( 963 ) ) );
284 stdev1Marker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
285 stdev1Marker->show();
286
287 QwtPlotMarker *stdev2Marker = new QwtPlotMarker();
288 stdev2Marker->attach( mpPlot );
289 stdev2Marker->setLineStyle( QwtPlotMarker::VLine );
290 stdev2Marker->setLinePen( mStdevPen );
291 stdev2Marker->setXValue( mStats.mean() + mStats.stDev() );
292 stdev2Marker->setLabel( QString( QChar( 963 ) ) );
293 stdev2Marker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
294 stdev2Marker->show();
295 }
296
297 mpPlot->setEnabled( true );
298 mpPlot->replot();
299}
300
301QwtPlotHistogram *QgsHistogramWidget::createPlotHistogram( const QString &title, const QBrush &brush, const QPen &pen ) const
302{
303 QwtPlotHistogram *histogram = new QwtPlotHistogram( title );
304 histogram->setBrush( brush );
305 if ( pen != Qt::NoPen )
306 {
307 histogram->setPen( pen );
308 }
309 else if ( brush.color().lightness() > 200 )
310 {
311 QPen p;
312 p.setColor( brush.color().darker( 150 ) );
313 p.setWidth( 0 );
314 p.setCosmetic( true );
315 histogram->setPen( p );
316 }
317 else
318 {
319 histogram->setPen( QPen( Qt::NoPen ) );
320 }
321 return histogram;
322}
@ StDev
Standard deviation of values.
QList< QwtPlotMarker * > mRangeMarkers
void refresh()
Redraws the histogram.
QPen pen() const
Returns the pen used when drawing histogram bars.
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.
QList< int > counts(int bins) const
Returns the calculated list of the counts for the histogram bins.
int optimalNumberBins() const
Returns the optimal number of bins for the source values, calculated using the Freedman-Diaconis rule...
QList< double > binEdges(int bins) const
Returns a list of edges for the histogram for a specified number of bins.
void setValues(const QList< double > &values)
Assigns numeric source values for the histogram.
double lowerValue() const
Returns the lower bound of the range.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
void calculate(const QList< double > &values)
Calculates summary statistics for a list of values.
double mean() const
Returns calculated mean of values.
double stDev() const
Returns population standard deviation.
void setStatistics(Qgis::Statistics stats)
Sets flags which specify which statistics will be calculated.
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 data sets.
QList< QgsRendererRange > QgsRangeList