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