QGIS API Documentation 3.99.0-Master (21b3aa880ba)
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"
21#include "qgssettings.h"
23#include "qgsvectorlayer.h"
24#include "qgsvectorlayerutils.h"
25
26#include <QMouseEvent>
27#include <QObject>
28
29#include "moc_qgshistogramwidget.cpp"
30
31// QWT Charting widget
32#include <qwt_global.h>
33#include <qwt_plot_canvas.h>
34#include <qwt_plot.h>
35#include <qwt_plot_curve.h>
36#include <qwt_plot_grid.h>
37#include <qwt_plot_marker.h>
38#include <qwt_plot_picker.h>
39#include <qwt_picker_machine.h>
40#include <qwt_plot_layout.h>
41#include <qwt_plot_renderer.h>
42#include <qwt_plot_histogram.h>
43#include <qwt_text.h>
44
45
46QgsHistogramWidget::QgsHistogramWidget( QWidget *parent, QgsVectorLayer *layer, const QString &fieldOrExp )
47 : QWidget( parent )
48 , mVectorLayer( layer )
49 , mSourceFieldExp( fieldOrExp )
50 , mXAxisTitle( QObject::tr( "Value" ) )
51 , mYAxisTitle( QObject::tr( "Count" ) )
52{
53 setupUi( this );
54
55 mPlot = mpPlot;
56
57 // hide the ugly canvas frame
58 QFrame *plotCanvasFrame = dynamic_cast<QFrame *>( mpPlot->canvas() );
59 if ( plotCanvasFrame )
60 plotCanvasFrame->setFrameStyle( QFrame::NoFrame );
61
62 QgsSettings settings;
63 mMeanCheckBox->setChecked( settings.value( QStringLiteral( "HistogramWidget/showMean" ), false ).toBool() );
64 mStdevCheckBox->setChecked( settings.value( QStringLiteral( "HistogramWidget/showStdev" ), false ).toBool() );
65
66 connect( mBinsSpinBox, static_cast<void ( QSpinBox::* )( int )>( &QSpinBox::valueChanged ), this, &QgsHistogramWidget::refresh );
67 connect( mMeanCheckBox, &QAbstractButton::toggled, this, &QgsHistogramWidget::refresh );
68 connect( mStdevCheckBox, &QAbstractButton::toggled, this, &QgsHistogramWidget::refresh );
69 connect( mLoadValuesButton, &QAbstractButton::clicked, this, &QgsHistogramWidget::refreshValues );
70
71 mGridPen = QPen( QColor( 0, 0, 0, 40 ) );
72 mMeanPen = QPen( QColor( 10, 10, 10, 220 ) );
73 mMeanPen.setStyle( Qt::DashLine );
74 mStdevPen = QPen( QColor( 30, 30, 30, 200 ) );
75 mStdevPen.setStyle( Qt::DashLine );
76
77 if ( layer && !mSourceFieldExp.isEmpty() )
78 {
79 refresh();
80 }
81}
82
84{
85 QgsSettings settings;
86 settings.setValue( QStringLiteral( "HistogramWidget/showMean" ), mMeanCheckBox->isChecked() );
87 settings.setValue( QStringLiteral( "HistogramWidget/showStdev" ), mStdevCheckBox->isChecked() );
88}
89
90static bool _rangesByLower( const QgsRendererRange &a, const QgsRendererRange &b )
91{
92 return a.lowerValue() < b.lowerValue();
93}
94
96{
97 mRanges = ranges;
98 std::sort( mRanges.begin(), mRanges.end(), _rangesByLower );
99}
100
102{
103 mValues.clear();
104
105 if ( !mVectorLayer || mSourceFieldExp.isEmpty() )
106 return;
107
108 QApplication::setOverrideCursor( Qt::WaitCursor );
109
110 bool ok;
111 mValues = QgsVectorLayerUtils::getDoubleValues( mVectorLayer, mSourceFieldExp, ok );
112
113 if ( !ok )
114 {
115 QApplication::restoreOverrideCursor();
116 return;
117 }
118
119
120 std::sort( mValues.begin(), mValues.end() );
121 mHistogram.setValues( mValues );
122 mBinsSpinBox->blockSignals( true );
123 mBinsSpinBox->setValue( std::max( mHistogram.optimalNumberBins(), 30 ) );
124 mBinsSpinBox->blockSignals( false );
125
126 mStats.setStatistics( Qgis::Statistic::StDev );
127 mStats.calculate( mValues );
128
129 mpPlot->setEnabled( true );
130 mMeanCheckBox->setEnabled( true );
131 mStdevCheckBox->setEnabled( true );
132 mBinsSpinBox->setEnabled( true );
133
134 QApplication::restoreOverrideCursor();
135
136 //also force a redraw
137 refresh();
138}
139
144
146{
147 if ( layer == mVectorLayer )
148 return;
149
150 mVectorLayer = layer;
151 clearHistogram();
152}
153
154void QgsHistogramWidget::clearHistogram()
155{
156 mValues.clear();
157 mHistogram.setValues( mValues );
158 refresh();
159
160 mpPlot->setEnabled( false );
161 mMeanCheckBox->setEnabled( false );
162 mStdevCheckBox->setEnabled( false );
163 mBinsSpinBox->setEnabled( false );
164}
165
166void QgsHistogramWidget::setSourceFieldExp( const QString &fieldOrExp )
167{
168 if ( fieldOrExp == mSourceFieldExp )
169 return;
170
171 mSourceFieldExp = fieldOrExp;
172 clearHistogram();
173}
174
176{
177 // clear plot
178 mpPlot->detachItems();
179
180 //ensure all children get removed
181 mpPlot->setAutoDelete( true );
182 // Set axis titles
183 if ( !mXAxisTitle.isEmpty() )
184 mpPlot->setAxisTitle( QwtPlot::xBottom, mXAxisTitle );
185 if ( !mYAxisTitle.isEmpty() )
186 mpPlot->setAxisTitle( QwtPlot::yLeft, mYAxisTitle );
187 mpPlot->setAxisFont( QwtPlot::xBottom, this->font() );
188 mpPlot->setAxisFont( QwtPlot::yLeft, this->font() );
189 QFont titleFont = this->font();
190 titleFont.setBold( true );
191 QwtText xAxisText = mpPlot->axisTitle( QwtPlot::xBottom );
192 xAxisText.setFont( titleFont );
193 mpPlot->setAxisTitle( QwtPlot::xBottom, xAxisText );
194 QwtText yAxisText = mpPlot->axisTitle( QwtPlot::yLeft );
195 yAxisText.setFont( titleFont );
196 mpPlot->setAxisTitle( QwtPlot::yLeft, yAxisText );
197 mpPlot->setAxisAutoScale( QwtPlot::yLeft );
198 mpPlot->setAxisAutoScale( QwtPlot::xBottom );
199
200 // add a grid
201 QwtPlotGrid *grid = new QwtPlotGrid();
202 grid->enableX( false );
203 grid->setPen( mGridPen );
204 grid->attach( mpPlot );
205
206 // make colors list
207 mHistoColors.clear();
208 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
209 {
210 mHistoColors << ( range.symbol() ? range.symbol()->color() : Qt::black );
211 }
212
213 //draw histogram
214 QwtPlotHistogram *plotHistogram = nullptr;
215 plotHistogram = createPlotHistogram( !mRanges.isEmpty() ? mRanges.at( 0 ).label() : QString(), !mRanges.isEmpty() ? QBrush( mHistoColors.at( 0 ) ) : mBrush, !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}
@ StDev
Standard deviation of values.
Definition qgis.h:5854
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.
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.
Stores settings for use within QGIS.
Definition qgssettings.h:65
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.
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