QGIS API Documentation 3.41.0-Master (3440c17df1d)
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(),
214 !mRanges.isEmpty() ? QBrush( mHistoColors.at( 0 ) ) : mBrush,
215 !mRanges.isEmpty() ? Qt::NoPen : mPen );
216 QVector<QwtIntervalSample> dataHisto;
217
218 int bins = mBinsSpinBox->value();
219 QList<double> edges = mHistogram.binEdges( bins );
220 QList<int> counts = mHistogram.counts( bins );
221
222 int rangeIndex = 0;
223 int lastValue = 0;
224
225 for ( int bin = 0; bin < bins; ++bin )
226 {
227 int binValue = counts.at( bin );
228
229 //current bin crosses two graduated ranges, so we split between
230 //two histogram items
231 if ( rangeIndex < mRanges.count() - 1 && edges.at( bin ) > mRanges.at( rangeIndex ).upperValue() )
232 {
233 rangeIndex++;
234 plotHistogram->setSamples( dataHisto );
235 plotHistogram->attach( mpPlot );
236 plotHistogram = createPlotHistogram( mRanges.at( rangeIndex ).label(), mHistoColors.at( rangeIndex ) );
237 dataHisto.clear();
238 dataHisto << QwtIntervalSample( lastValue, mRanges.at( rangeIndex - 1 ).upperValue(), edges.at( bin ) );
239 }
240
241 double upperEdge = !mRanges.isEmpty() ? std::min( edges.at( bin + 1 ), mRanges.at( rangeIndex ).upperValue() )
242 : edges.at( bin + 1 );
243
244 dataHisto << QwtIntervalSample( binValue, edges.at( bin ), upperEdge );
245
246 lastValue = binValue;
247 }
248
249 plotHistogram->setSamples( dataHisto );
250 plotHistogram->attach( mpPlot );
251
252 mRangeMarkers.clear();
253 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
254 {
255 QwtPlotMarker *rangeMarker = new QwtPlotMarker();
256 rangeMarker->attach( mpPlot );
257 rangeMarker->setLineStyle( QwtPlotMarker::VLine );
258 rangeMarker->setXValue( range.upperValue() );
259 rangeMarker->setLabel( QLocale().toString( range.upperValue() ) );
260 rangeMarker->setLabelOrientation( Qt::Vertical );
261 rangeMarker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
262 rangeMarker->show();
263 mRangeMarkers << rangeMarker;
264 }
265
266 if ( mMeanCheckBox->isChecked() )
267 {
268 QwtPlotMarker *meanMarker = new QwtPlotMarker();
269 meanMarker->attach( mpPlot );
270 meanMarker->setLineStyle( QwtPlotMarker::VLine );
271 meanMarker->setLinePen( mMeanPen );
272 meanMarker->setXValue( mStats.mean() );
273 meanMarker->setLabel( QString( QChar( 956 ) ) );
274 meanMarker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
275 meanMarker->show();
276 }
277
278 if ( mStdevCheckBox->isChecked() )
279 {
280 QwtPlotMarker *stdev1Marker = new QwtPlotMarker();
281 stdev1Marker->attach( mpPlot );
282 stdev1Marker->setLineStyle( QwtPlotMarker::VLine );
283 stdev1Marker->setLinePen( mStdevPen );
284 stdev1Marker->setXValue( mStats.mean() - mStats.stDev() );
285 stdev1Marker->setLabel( QString( QChar( 963 ) ) );
286 stdev1Marker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
287 stdev1Marker->show();
288
289 QwtPlotMarker *stdev2Marker = new QwtPlotMarker();
290 stdev2Marker->attach( mpPlot );
291 stdev2Marker->setLineStyle( QwtPlotMarker::VLine );
292 stdev2Marker->setLinePen( mStdevPen );
293 stdev2Marker->setXValue( mStats.mean() + mStats.stDev() );
294 stdev2Marker->setLabel( QString( QChar( 963 ) ) );
295 stdev2Marker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
296 stdev2Marker->show();
297 }
298
299 mpPlot->setEnabled( true );
300 mpPlot->replot();
301}
302
303QwtPlotHistogram *QgsHistogramWidget::createPlotHistogram( const QString &title, const QBrush &brush, const QPen &pen ) const
304{
305 QwtPlotHistogram *histogram = new QwtPlotHistogram( title );
306 histogram->setBrush( brush );
307 if ( pen != Qt::NoPen )
308 {
309 histogram->setPen( pen );
310 }
311 else if ( brush.color().lightness() > 200 )
312 {
313 QPen p;
314 p.setColor( brush.color().darker( 150 ) );
315 p.setWidth( 0 );
316 p.setCosmetic( true );
317 histogram->setPen( p );
318 }
319 else
320 {
321 histogram->setPen( QPen( Qt::NoPen ) );
322 }
323 return histogram;
324}
325
@ StDev
Standard deviation of values.
void refresh()
Redraws the histogram.
QList< QwtPlotMarker * > mRangeMarkers
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