QGIS API Documentation 3.99.0-Master (d270888f95f)
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() )
245 : edges.at( bin + 1 );
246
247 dataHisto << QwtIntervalSample( binValue, edges.at( bin ), upperEdge );
248
249 lastValue = binValue;
250 }
251
252 plotHistogram->setSamples( dataHisto );
253 plotHistogram->attach( mpPlot );
254
255 mRangeMarkers.clear();
256 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
257 {
258 QwtPlotMarker *rangeMarker = new QwtPlotMarker();
259 rangeMarker->attach( mpPlot );
260 rangeMarker->setLineStyle( QwtPlotMarker::VLine );
261 rangeMarker->setXValue( range.upperValue() );
262 rangeMarker->setLabel( QLocale().toString( range.upperValue() ) );
263 rangeMarker->setLabelOrientation( Qt::Vertical );
264 rangeMarker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
265 rangeMarker->show();
266 mRangeMarkers << rangeMarker;
267 }
268
269 if ( mMeanCheckBox->isChecked() )
270 {
271 QwtPlotMarker *meanMarker = new QwtPlotMarker();
272 meanMarker->attach( mpPlot );
273 meanMarker->setLineStyle( QwtPlotMarker::VLine );
274 meanMarker->setLinePen( mMeanPen );
275 meanMarker->setXValue( mStats.mean() );
276 meanMarker->setLabel( QString( QChar( 956 ) ) );
277 meanMarker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
278 meanMarker->show();
279 }
280
281 if ( mStdevCheckBox->isChecked() )
282 {
283 QwtPlotMarker *stdev1Marker = new QwtPlotMarker();
284 stdev1Marker->attach( mpPlot );
285 stdev1Marker->setLineStyle( QwtPlotMarker::VLine );
286 stdev1Marker->setLinePen( mStdevPen );
287 stdev1Marker->setXValue( mStats.mean() - mStats.stDev() );
288 stdev1Marker->setLabel( QString( QChar( 963 ) ) );
289 stdev1Marker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
290 stdev1Marker->show();
291
292 QwtPlotMarker *stdev2Marker = new QwtPlotMarker();
293 stdev2Marker->attach( mpPlot );
294 stdev2Marker->setLineStyle( QwtPlotMarker::VLine );
295 stdev2Marker->setLinePen( mStdevPen );
296 stdev2Marker->setXValue( mStats.mean() + mStats.stDev() );
297 stdev2Marker->setLabel( QString( QChar( 963 ) ) );
298 stdev2Marker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
299 stdev2Marker->show();
300 }
301
302 mpPlot->setEnabled( true );
303 mpPlot->replot();
304}
305
306QwtPlotHistogram *QgsHistogramWidget::createPlotHistogram( const QString &title, const QBrush &brush, const QPen &pen ) const
307{
308 QwtPlotHistogram *histogram = new QwtPlotHistogram( title );
309 histogram->setBrush( brush );
310 if ( pen != Qt::NoPen )
311 {
312 histogram->setPen( pen );
313 }
314 else if ( brush.color().lightness() > 200 )
315 {
316 QPen p;
317 p.setColor( brush.color().darker( 150 ) );
318 p.setWidth( 0 );
319 p.setCosmetic( true );
320 histogram->setPen( p );
321 }
322 else
323 {
324 histogram->setPen( QPen( Qt::NoPen ) );
325 }
326 return histogram;
327}
@ StDev
Standard deviation of values.
Definition qgis.h:6120
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