[FEATURE] Add a graphical histogram for the graduated renderer

This adds a new histogram tab to the graduated renderer, which
shows an interactive histogram of the values from the assigned
field or expression. Class breaks can be moved or added using
the histogram widget.

A base class, QgsHistogramWidget, has been created to display
histograms for a field or expression. In future this could be
used to show a histogram within a "selection statistics" panel.

Sponsored by ADUGA (http://www.aduga.org)
This commit is contained in:
Nyall Dawson 2015-04-13 19:39:09 +10:00
parent 17962ef47b
commit 183286a006
17 changed files with 1902 additions and 559 deletions

View File

@ -56,6 +56,7 @@
%Include qgsfilterlineedit.sip %Include qgsfilterlineedit.sip
%Include qgsformannotationitem.sip %Include qgsformannotationitem.sip
%Include qgsgenericprojectionselector.sip %Include qgsgenericprojectionselector.sip
%Include qgshistogramwidget.sip
%Include qgshtmlannotationitem.sip %Include qgshtmlannotationitem.sip
%Include qgsidentifymenu.sip %Include qgsidentifymenu.sip
%Include qgslegendinterface.sip %Include qgslegendinterface.sip
@ -173,6 +174,7 @@
%Include symbology-ng/qgsdatadefinedsymboldialog.sip %Include symbology-ng/qgsdatadefinedsymboldialog.sip
%Include symbology-ng/qgsstylev2exportimportdialog.sip %Include symbology-ng/qgsstylev2exportimportdialog.sip
%Include symbology-ng/qgssvgselectorwidget.sip %Include symbology-ng/qgssvgselectorwidget.sip
%Include symbology-ng/qgsgraduatedhistogramwidget.sip
%Include effects/qgseffectdrawmodecombobox.sip %Include effects/qgseffectdrawmodecombobox.sip
%Include effects/qgspainteffectpropertieswidget.sip %Include effects/qgspainteffectpropertieswidget.sip

View File

@ -0,0 +1,107 @@
/** \ingroup gui
* \class QgsHistogramWidget
* \brief Graphical histogram for displaying distributions of field values.
*
* \note Added in version 2.9
*/
class QgsHistogramWidget : QWidget
{
%TypeHeaderCode
#include <qgshistogramwidget.h>
%End
public:
/** QgsHistogramWidget constructor. If layer and fieldOrExp are specified then the histogram
* will be initially populated with the corresponding values.
* @param parent parent widget
* @param layer source vector layer
* @param fieldOrExp field name or expression string
*/
QgsHistogramWidget( QWidget *parent /TransferThis/ = 0, QgsVectorLayer* layer = 0, const QString& fieldOrExp = QString() );
~QgsHistogramWidget();
/** Returns the layer currently associated with the widget.
* @see setLayer
* @see sourceFieldExp
*/
QgsVectorLayer* layer();
/** Returns the source field name or expression used to calculate values displayed
* in the histogram.
* @see setSourceFieldExp
* @see layer
*/
QString sourceFieldExp() const;
/** Sets the pen to use when drawing histogram bars. If set to Qt::NoPen then the
* pen will be automatically calculated. If ranges have been set using @link setGraduatedRanges @endlink
* then the pen and brush will have no effect.
* @param pen histogram pen
* @see pen
* @see setBrush
*/
void setPen( const QPen& pen );
/** Returns the pen used when drawing histogram bars.
* @see setPen
* @see brush
*/
QPen pen() const;
/** Sets the brush used for drawing histogram bars. If ranges have been set using @link setGraduatedRanges @endlink
* then the pen and brush will have no effect.
* @param brush histogram brush
* @see brush
* @see setPen
*/
void setBrush( const QBrush& brush );
/** Returns the brush used when drawing histogram bars.
* @see setBrush
* @see pen
*/
QBrush brush() const;
/** Sets the graduated ranges associated with the histogram. If set, the ranges will be used to colour the histogram
* bars and for showing vertical dividers at the histogram breaks.
* @param ranges graduated range list
*/
void setGraduatedRanges( const QgsRangeList& ranges );
/** Returns the graduated ranges associated with the histogram. If set, the ranges will be used to colour the histogram
* bars and for showing vertical dividers at the histogram breaks.
* @returns graduated range list
* @see setGraduatedRanges
*/
QgsRangeList graduatedRanges() const;
public slots:
/** Triggers a refresh of the histogram when the widget is next repainted.
*/
void refreshHistogram();
/** Sets the vector layer associated with the histogram.
* @param layer source vector layer
* @see setSourceFieldExp
*/
void setLayer( QgsVectorLayer* layer );
/** Sets the source field or expression to use for values in the histogram.
* @param fieldOrExp field name or expression string
* @see setLayer
*/
void setSourceFieldExp( const QString& fieldOrExp );
protected:
/** Updates and redraws the histogram.
*/
virtual void drawHistogram();
virtual void paintEvent( QPaintEvent * event );
};

View File

@ -0,0 +1,42 @@
/** \ingroup gui
* \class QgsGraduatedHistogramWidget
* \brief Graphical histogram for displaying distribution of field values and
* editing range breaks for a QgsGraduatedSymbolRendererV2 renderer.
*
* \note Added in version 2.9
*/
class QgsGraduatedHistogramWidget : QWidget
{
%TypeHeaderCode
#include <qgsgraduatedhistogramwidget.h>
%End
public:
/** QgsGraduatedHistogramWidget constructor
* @param parent parent widget
*/
QgsGraduatedHistogramWidget( QWidget *parent /TransferThis/ = 0 );
~QgsGraduatedHistogramWidget();
/** Sets the QgsGraduatedSymbolRendererV2 renderer associated with the histogram.
* The histogram will fetch the ranges from the renderer before every refresh.
* @param renderer associated QgsGraduatedSymbolRendererV2
*/
void setRenderer( QgsGraduatedSymbolRendererV2* renderer );
signals:
/** Emitted when the user modifies the graduated ranges using the histogram widget.
* @param rangesAdded true if the user has added ranges, false if the user has just
* modified existing range breaks
*/
void rangesModified( bool rangesAdded );
protected:
virtual void drawHistogram();
};

View File

@ -103,7 +103,7 @@ QList<int> QgsHistogram::counts( int bins ) const
for ( int i = 0; i < bins; ++i ) for ( int i = 0; i < bins; ++i )
{ {
int count = 0; int count = 0;
while ( mValues.at( currentValueIndex ) < edges.at( i + 1 ) ) while ( currentValueIndex < mValues.count() && mValues.at( currentValueIndex ) < edges.at( i + 1 ) )
{ {
count++; count++;
currentValueIndex++; currentValueIndex++;

View File

@ -1357,6 +1357,37 @@ void QgsGraduatedSymbolRendererV2::addClass( double lower, double upper )
mRanges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) ); mRanges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
} }
void QgsGraduatedSymbolRendererV2::addBreak( double breakValue, bool updateSymbols )
{
QMutableListIterator< QgsRendererRangeV2 > it( mRanges );
while ( it.hasNext() )
{
QgsRendererRangeV2 range = it.next();
if ( range.lowerValue() < breakValue && range.upperValue() > breakValue )
{
QgsRendererRangeV2 newRange = QgsRendererRangeV2();
newRange.setLowerValue( breakValue );
newRange.setUpperValue( range.upperValue() );
newRange.setLabel( mLabelFormat.labelForRange( newRange ) );
newRange.setSymbol( mSourceSymbol->clone() );
//update old range
bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
range.setUpperValue( breakValue );
if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range.lowerValue(), breakValue ) );
it.setValue( range );
it.insert( newRange );
break;
}
}
if ( updateSymbols && mGraduatedMethod == GraduatedColor )
{
updateColorRamp( mSourceColorRamp.data(), mInvertedColorRamp );
}
}
void QgsGraduatedSymbolRendererV2::addClass( QgsRendererRangeV2 range ) void QgsGraduatedSymbolRendererV2::addClass( QgsRendererRangeV2 range )
{ {
mRanges.append( range ); mRanges.append( range );

View File

@ -157,6 +157,16 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
void addClass( QgsRendererRangeV2 range ); void addClass( QgsRendererRangeV2 range );
//! @note available in python bindings as addClassLowerUpper //! @note available in python bindings as addClassLowerUpper
void addClass( double lower, double upper ); void addClass( double lower, double upper );
/** Add a breakpoint by splitting existing classes so that the specified
* value becomes a break between two classes.
* @param breakValue position to insert break
* @param updateSymbols set to true to reapply ramp colors to the new
* symbol ranges
* @note added in QGIS 2.9
*/
void addBreak( double breakValue, bool updateSymbols = true );
void deleteClass( int idx ); void deleteClass( int idx );
void deleteAllClasses(); void deleteAllClasses();

View File

@ -8,6 +8,7 @@ raster/qgspalettedrendererwidget.cpp
raster/qgssinglebandgrayrendererwidget.cpp raster/qgssinglebandgrayrendererwidget.cpp
raster/qgssinglebandpseudocolorrendererwidget.cpp raster/qgssinglebandpseudocolorrendererwidget.cpp
raster/qgsrasterhistogramwidget.cpp raster/qgsrasterhistogramwidget.cpp
raster/qwt5_histogram_item.cpp
symbology-ng/qgsbrushstylecombobox.cpp symbology-ng/qgsbrushstylecombobox.cpp
symbology-ng/qgscolorrampcombobox.cpp symbology-ng/qgscolorrampcombobox.cpp
@ -19,6 +20,7 @@ symbology-ng/qgsrendererv2widget.cpp
symbology-ng/qgssinglesymbolrendererv2widget.cpp symbology-ng/qgssinglesymbolrendererv2widget.cpp
symbology-ng/qgscategorizedsymbolrendererv2widget.cpp symbology-ng/qgscategorizedsymbolrendererv2widget.cpp
symbology-ng/qgsgraduatedsymbolrendererv2widget.cpp symbology-ng/qgsgraduatedsymbolrendererv2widget.cpp
symbology-ng/qgsgraduatedhistogramwidget.cpp
symbology-ng/qgsrulebasedrendererv2widget.cpp symbology-ng/qgsrulebasedrendererv2widget.cpp
symbology-ng/qgsheatmaprendererwidget.cpp symbology-ng/qgsheatmaprendererwidget.cpp
symbology-ng/qgsinvertedpolygonrendererwidget.cpp symbology-ng/qgsinvertedpolygonrendererwidget.cpp
@ -174,6 +176,7 @@ qgsfilterlineedit.cpp
qgsformannotationitem.cpp qgsformannotationitem.cpp
qgsgenericprojectionselector.cpp qgsgenericprojectionselector.cpp
qgshighlight.cpp qgshighlight.cpp
qgshistogramwidget.cpp
qgsidentifymenu.cpp qgsidentifymenu.cpp
qgshtmlannotationitem.cpp qgshtmlannotationitem.cpp
qgslegendinterface.cpp qgslegendinterface.cpp
@ -287,6 +290,7 @@ SET(QGIS_GUI_MOC_HDRS
qgsfilterlineedit.h qgsfilterlineedit.h
qgsformannotationitem.h qgsformannotationitem.h
qgsgenericprojectionselector.h qgsgenericprojectionselector.h
qgshistogramwidget.h
qgshtmlannotationitem.h qgshtmlannotationitem.h
qgsidentifymenu.h qgsidentifymenu.h
qgslegendinterface.h qgslegendinterface.h
@ -352,6 +356,7 @@ SET(QGIS_GUI_MOC_HDRS
symbology-ng/qgsdatadefinedsymboldialog.h symbology-ng/qgsdatadefinedsymboldialog.h
symbology-ng/qgsellipsesymbollayerv2widget.h symbology-ng/qgsellipsesymbollayerv2widget.h
symbology-ng/qgsgraduatedsymbolrendererv2widget.h symbology-ng/qgsgraduatedsymbolrendererv2widget.h
symbology-ng/qgsgraduatedhistogramwidget.h
symbology-ng/qgsheatmaprendererwidget.h symbology-ng/qgsheatmaprendererwidget.h
symbology-ng/qgsinvertedpolygonrendererwidget.h symbology-ng/qgsinvertedpolygonrendererwidget.h
symbology-ng/qgslayerpropertieswidget.h symbology-ng/qgslayerpropertieswidget.h

View File

@ -0,0 +1,353 @@
/***************************************************************************
qgshistogramwidget.cpp
----------------------
begin : May 2015
copyright : (C) 2015 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgshistogramwidget.h"
#include "qgsapplication.h"
#include "qgsvectorlayer.h"
#include "qgsstatisticalsummary.h"
#include <QSettings>
#include <QObject>
#include <QMouseEvent>
// QWT Charting widget
#include <qwt_global.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_marker.h>
#include <qwt_plot_picker.h>
#include <qwt_picker_machine.h>
#include <qwt_plot_layout.h>
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
#include <qwt_plot_renderer.h>
#include <qwt_plot_histogram.h>
#else
#include "../raster/qwt5_histogram_item.h"
#endif
QgsHistogramWidget::QgsHistogramWidget( QWidget *parent, QgsVectorLayer* layer, const QString& fieldOrExp )
: QWidget( parent )
, mRedrawRequired( true )
, mVectorLayer( layer )
, mSourceFieldExp( fieldOrExp )
{
setupUi( this );
mPlot = mpPlot;
// hide the ugly canvas frame
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
QFrame* plotCanvasFrame = dynamic_cast<QFrame*>( mpPlot->canvas() );
if ( plotCanvasFrame )
plotCanvasFrame->setFrameStyle( QFrame::NoFrame );
#else
mpPlot->canvas()->setFrameStyle( QFrame::NoFrame );
#endif
QSettings settings;
mMeanCheckBox->setChecked( settings.value( "/HistogramWidget/showMean", false ).toBool() );
mStdevCheckBox->setChecked( settings.value( "/HistogramWidget/showStdev", false ).toBool() );
connect( mBinsSpinBox, SIGNAL( valueChanged( int ) ), this, SLOT( refreshHistogram() ) );
connect( mMeanCheckBox, SIGNAL( toggled( bool ) ), this, SLOT( refreshHistogram() ) );
connect( mStdevCheckBox, SIGNAL( toggled( bool ) ), this, SLOT( refreshHistogram() ) );
mGridPen = QPen( QColor( 0, 0, 0, 40 ) );
mMeanPen = QPen( QColor( 10, 10, 10, 220 ) );
mMeanPen.setStyle( Qt::DashLine );
mStdevPen = QPen( QColor( 30, 30, 30, 200 ) );
mStdevPen.setStyle( Qt::DashLine );
if ( layer && !mSourceFieldExp.isEmpty() )
{
refreshHistogram();
}
}
QgsHistogramWidget::~QgsHistogramWidget()
{
QSettings settings;
settings.setValue( "/HistogramWidget/showMean", mMeanCheckBox->isChecked() );
settings.setValue( "/HistogramWidget/showStdev", mStdevCheckBox->isChecked() );
}
void QgsHistogramWidget::setLayer( QgsVectorLayer *layer )
{
if ( layer == mVectorLayer )
return;
mVectorLayer = layer;
mValues.clear();
mRedrawRequired = true;
}
void QgsHistogramWidget::refreshHistogram()
{
mRedrawRequired = true;
}
void QgsHistogramWidget::setSourceFieldExp( const QString &fieldOrExp )
{
if ( fieldOrExp == mSourceFieldExp )
return;
mSourceFieldExp = fieldOrExp;
mValues.clear();
mRedrawRequired = true;
}
void QgsHistogramWidget::drawHistogram()
{
if ( !mVectorLayer || mSourceFieldExp.isEmpty() )
return;
QApplication::setOverrideCursor( Qt::WaitCursor );
if ( mValues.empty() )
{
bool ok;
mValues = mVectorLayer->getDoubleValues( mSourceFieldExp, ok );
if ( ! ok )
{
QApplication::restoreOverrideCursor();
return;
}
qSort( mValues.begin(), mValues.end() );
mHistogram.setValues( mValues );
mBinsSpinBox->blockSignals( true );
mBinsSpinBox->setValue( qMax( mHistogram.optimalNumberBins(), 30 ) );
mBinsSpinBox->blockSignals( false );
mStats.setStatistics( QgsStatisticalSummary::StDev );
mStats.calculate( mValues );
}
// clear plot
mpPlot->detachItems();
//ensure all children get removed
mpPlot->setAutoDelete( true );
// Set axis titles
mpPlot->setAxisTitle( QwtPlot::xBottom, QObject::tr( "Value" ) );
mpPlot->setAxisTitle( QwtPlot::yLeft, QObject::tr( "Count" ) );
mpPlot->setAxisAutoScale( QwtPlot::yLeft );
mpPlot->setAxisAutoScale( QwtPlot::xBottom );
// add a grid
QwtPlotGrid * grid = new QwtPlotGrid();
grid->enableX( false );
grid->setPen( mGridPen );
grid->attach( mpPlot );
// make colors list
mHistoColors.clear();
foreach ( QgsRendererRangeV2 range, mRanges )
{
mHistoColors << ( range.symbol() ? range.symbol()->color() : Qt::black );
}
//draw histogram
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
QwtPlotHistogram * plotHistogram = 0;
plotHistogram = createPlotHistogram( mRanges.count() > 0 ? mRanges.at( 0 ).label() : QString(),
mRanges.count() > 0 ? QBrush( mHistoColors.at( 0 ) ) : mBrush,
mRanges.count() > 0 ? Qt::NoPen : mPen );
#else
HistogramItem *plotHistogramItem = 0;
plotHistogramItem = createHistoItem( mRanges.count() > 0 ? mRanges.at( 0 ).label() : QString(),
mRanges.count() > 0 ? QBrush( mHistoColors.at( 0 ) ) : mBrush,
mRanges.count() > 0 ? Qt::NoPen : mPen );
#endif
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
QVector<QwtIntervalSample> dataHisto;
#else
// we safely assume that QT>=4.0 (min version is 4.7), therefore QwtArray is a QVector, so don't set size here
QwtArray<QwtDoubleInterval> intervalsHisto;
QwtArray<double> valuesHisto;
#endif
int bins = mBinsSpinBox->value();
QList<double> edges = mHistogram.binEdges( bins );
QList<int> counts = mHistogram.counts( bins );
int rangeIndex = 0;
int lastValue = 0;
for ( int bin = 0; bin < bins; ++bin )
{
int binValue = counts.at( bin );
//current bin crosses two graduated ranges, so we split between
//two histogram items
if ( rangeIndex < mRanges.count() - 1 && edges.at( bin ) > mRanges.at( rangeIndex ).upperValue() )
{
rangeIndex++;
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
plotHistogram->setSamples( dataHisto );
plotHistogram->attach( mpPlot );
plotHistogram = createPlotHistogram( mRanges.at( rangeIndex ).label(), mHistoColors.at( rangeIndex ) );
dataHisto.clear();
dataHisto << QwtIntervalSample( lastValue, mRanges.at( rangeIndex - 1 ).upperValue(), edges.at( bin ) );
#else
plotHistogramItem->setData( QwtIntervalData( intervalsHisto, valuesHisto ) );
plotHistogramItem->attach( mpPlot );
plotHistogramItem = createHistoItem( mRanges.at( rangeIndex ).label(), mHistoColors.at( rangeIndex ) );
intervalsHisto.clear();
valuesHisto.clear();
intervalsHisto.append( QwtDoubleInterval( mRanges.at( rangeIndex - 1 ).upperValue(), edges.at( bin ) ) );
valuesHisto.append( lastValue );
#endif
}
double upperEdge = mRanges.count() > 0 ? qMin( edges.at( bin + 1 ), mRanges.at( rangeIndex ).upperValue() )
: edges.at( bin + 1 );
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
dataHisto << QwtIntervalSample( binValue, edges.at( bin ), upperEdge );
#else
intervalsHisto.append( QwtDoubleInterval( edges.at( bin ), upperEdge ) );
valuesHisto.append( double( binValue ) );
#endif
lastValue = binValue;
}
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
plotHistogram->setSamples( dataHisto );
plotHistogram->attach( mpPlot );
#else
plotHistogramItem->setData( QwtIntervalData( intervalsHisto, valuesHisto ) );
plotHistogramItem->attach( mpPlot );
#endif
mRangeMarkers.clear();
foreach ( QgsRendererRangeV2 range, mRanges )
{
QwtPlotMarker* rangeMarker = new QwtPlotMarker();
rangeMarker->attach( mpPlot );
rangeMarker->setLineStyle( QwtPlotMarker::VLine );
rangeMarker->setXValue( range.upperValue() );
rangeMarker->setLabel( QString::number( range.upperValue() ) );
rangeMarker->setLabelOrientation( Qt::Vertical );
rangeMarker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
rangeMarker->show();
mRangeMarkers << rangeMarker;
}
if ( mMeanCheckBox->isChecked() )
{
QwtPlotMarker* meanMarker = new QwtPlotMarker();
meanMarker->attach( mpPlot );
meanMarker->setLineStyle( QwtPlotMarker::VLine );
meanMarker->setLinePen( mMeanPen );
meanMarker->setXValue( mStats.mean() );
meanMarker->setLabel( QString( QChar( 956 ) ) );
meanMarker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
meanMarker->show();
}
if ( mStdevCheckBox->isChecked() )
{
QwtPlotMarker* stdev1Marker = new QwtPlotMarker();
stdev1Marker->attach( mpPlot );
stdev1Marker->setLineStyle( QwtPlotMarker::VLine );
stdev1Marker->setLinePen( mStdevPen );
stdev1Marker->setXValue( mStats.mean() - mStats.stDev() );
stdev1Marker->setLabel( QString( QChar( 963 ) ) );
stdev1Marker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
stdev1Marker->show();
QwtPlotMarker* stdev2Marker = new QwtPlotMarker();
stdev2Marker->attach( mpPlot );
stdev2Marker->setLineStyle( QwtPlotMarker::VLine );
stdev2Marker->setLinePen( mStdevPen );
stdev2Marker->setXValue( mStats.mean() + mStats.stDev() );
stdev2Marker->setLabel( QString( QChar( 963 ) ) );
stdev2Marker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
stdev2Marker->show();
}
mpPlot->setEnabled( true );
mpPlot->replot();
QApplication::restoreOverrideCursor();
mRedrawRequired = false;
}
void QgsHistogramWidget::paintEvent( QPaintEvent *event )
{
if ( mRedrawRequired )
{
drawHistogram();
}
QWidget::paintEvent( event );
}
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
QwtPlotHistogram* QgsHistogramWidget::createPlotHistogram( const QString& title, const QBrush& brush, const QPen& pen ) const
{
QwtPlotHistogram* histogram = new QwtPlotHistogram( title );
histogram->setBrush( brush );
if ( pen != Qt::NoPen )
{
histogram->setPen( pen );
}
else if ( brush.color().lightness() > 200 )
{
QPen p;
p.setColor( brush.color().darker( 150 ) );
p.setWidth( 0 );
p.setCosmetic( true );
histogram->setPen( p );
}
else
{
histogram->setPen( QPen( Qt::NoPen ) );
}
return histogram;
}
#else
HistogramItem * QgsHistogramWidget::createHistoItem( const QString& title, const QBrush& brush, const QPen& pen ) const
{
HistogramItem* item = new HistogramItem( title );
item->setColor( brush.color() );
item->setFlat( true );
item->setSpacing( 0 );
if ( pen != Qt::NoPen )
{
item->setPen( pen );
}
else if ( brush.color().lightness() > 200 )
{
QPen p;
p.setColor( brush.color().darker( 150 ) );
p.setWidth( 0 );
p.setCosmetic( true );
item->setPen( p );
}
return item;
}
#endif

View File

@ -0,0 +1,174 @@
/***************************************************************************
qgshistogramwidget.h
--------------------
begin : May 2015
copyright : (C) 2015 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSHISTOGRAMWIDGET_H
#define QGSHISTOGRAMWIDGET_H
#include "ui_qgshistogramwidgetbase.h"
#include "qgshistogram.h"
#include "qgsstatisticalsummary.h"
#include "qgsgraduatedsymbolrendererv2.h"
#include <QPen>
#include <QBrush>
class QgsVectorLayer;
class QgsGraduatedSymbolRendererV2;
class QwtPlotPicker;
class QwtPlotMarker;
class QwtPlot;
class HistogramItem;
class QwtPlotHistogram;
// fix for qwt5/qwt6 QwtDoublePoint vs. QPointF
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
typedef QPointF QwtDoublePoint;
#endif
/** \ingroup gui
* \class QgsHistogramWidget
* \brief Graphical histogram for displaying distributions of field values.
*
* \note Added in version 2.9
*/
class GUI_EXPORT QgsHistogramWidget : public QWidget, private Ui::QgsHistogramWidgetBase
{
Q_OBJECT
public:
/** QgsHistogramWidget constructor. If layer and fieldOrExp are specified then the histogram
* will be initially populated with the corresponding values.
* @param parent parent widget
* @param layer source vector layer
* @param fieldOrExp field name or expression string
*/
QgsHistogramWidget( QWidget *parent = 0, QgsVectorLayer* layer = 0, const QString& fieldOrExp = QString() );
~QgsHistogramWidget();
/** Returns the layer currently associated with the widget.
* @see setLayer
* @see sourceFieldExp
*/
QgsVectorLayer* layer() { return mVectorLayer; }
/** Returns the source field name or expression used to calculate values displayed
* in the histogram.
* @see setSourceFieldExp
* @see layer
*/
QString sourceFieldExp() const { return mSourceFieldExp; }
/** Sets the pen to use when drawing histogram bars. If set to Qt::NoPen then the
* pen will be automatically calculated. If ranges have been set using @link setGraduatedRanges @endlink
* then the pen and brush will have no effect.
* @param pen histogram pen
* @see pen
* @see setBrush
*/
void setPen( const QPen& pen ) { mPen = pen; }
/** Returns the pen used when drawing histogram bars.
* @see setPen
* @see brush
*/
QPen pen() const { return mPen; }
/** Sets the brush used for drawing histogram bars. If ranges have been set using @link setGraduatedRanges @endlink
* then the pen and brush will have no effect.
* @param brush histogram brush
* @see brush
* @see setPen
*/
void setBrush( const QBrush& brush ) { mBrush = brush; }
/** Returns the brush used when drawing histogram bars.
* @see setBrush
* @see pen
*/
QBrush brush() const { return mBrush; }
/** Sets the graduated ranges associated with the histogram. If set, the ranges will be used to colour the histogram
* bars and for showing vertical dividers at the histogram breaks.
* @param ranges graduated range list
* @see graduatedRanges
*/
void setGraduatedRanges( const QgsRangeList& ranges ) { mRanges = ranges; }
/** Returns the graduated ranges associated with the histogram. If set, the ranges will be used to colour the histogram
* bars and for showing vertical dividers at the histogram breaks.
* @returns graduated range list
* @see setGraduatedRanges
*/
QgsRangeList graduatedRanges() const { return mRanges; }
public slots:
/** Triggers a refresh of the histogram when the widget is next repainted.
*/
void refreshHistogram();
/** Sets the vector layer associated with the histogram.
* @param layer source vector layer
* @see setSourceFieldExp
*/
void setLayer( QgsVectorLayer* layer );
/** Sets the source field or expression to use for values in the histogram.
* @param fieldOrExp field name or expression string
* @see setLayer
*/
void setSourceFieldExp( const QString& fieldOrExp );
protected:
/** Updates and redraws the histogram.
*/
virtual void drawHistogram();
virtual void paintEvent( QPaintEvent * event ) override;
QwtPlot* mPlot;
QgsRangeList mRanges;
QList< QwtPlotMarker* > mRangeMarkers;
bool mRedrawRequired;
private:
QgsVectorLayer * mVectorLayer;
QString mSourceFieldExp;
QList<double> mValues;
QgsStatisticalSummary mStats;
QgsHistogram mHistogram;
QVector<QColor> mHistoColors;
QPen mPen;
QBrush mBrush;
QPen mMeanPen;
QPen mStdevPen;
QPen mGridPen;
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
QwtPlotHistogram* createPlotHistogram( const QString& title, const QBrush &brush, const QPen &pen = Qt::NoPen ) const;
#else
HistogramItem* createHistoItem( const QString& title, const QBrush& brush, const QPen& pen = Qt::NoPen ) const;
#endif
};
#endif //QGSHISTOGRAMWIDGET_H

View File

@ -0,0 +1,397 @@
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* Qwt Widget Library
* Copyright (C) 1997 Josef Wilgen
* Copyright (C) 2002 Uwe Rathmann
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the Qwt License, Version 1.0
*****************************************************************************/
#include <qwt_global.h>
#if defined( QWT_VERSION ) && QWT_VERSION<0x060000
#include <qglobal.h>
#include <qcolor.h>
#include "qwt5_histogram_item.h"
HistogramItem::HistogramItem( const QwtText &title ):
QwtPlotItem( title )
{
init();
}
HistogramItem::HistogramItem( const QString &title ):
QwtPlotItem( QwtText( title ) )
{
init();
}
HistogramItem::~HistogramItem()
{
delete d_data;
}
void HistogramItem::init()
{
d_data = new PrivateData();
d_data->reference = 0.0;
d_data->attributes = HistogramItem::Auto;
d_data->flat = false;
d_data->spacing = 1;
d_data->pen = Qt::NoPen;
setItemAttribute( QwtPlotItem::AutoScale, true );
setItemAttribute( QwtPlotItem::Legend, true );
setZ( 20.0 );
}
void HistogramItem::setBaseline( double reference )
{
if ( d_data->reference != reference )
{
d_data->reference = reference;
itemChanged();
}
}
double HistogramItem::baseline() const
{
return d_data->reference;
}
void HistogramItem::setData( const QwtIntervalData &data )
{
d_data->data = data;
itemChanged();
}
const QwtIntervalData &HistogramItem::data() const
{
return d_data->data;
}
void HistogramItem::setColor( const QColor &color )
{
if ( d_data->color != color )
{
d_data->color = color;
itemChanged();
}
}
QColor HistogramItem::color() const
{
return d_data->color;
}
void HistogramItem::setFlat( bool flat )
{
if ( d_data->flat != flat )
{
d_data->flat = flat;
itemChanged();
}
}
bool HistogramItem::flat() const
{
return d_data->flat;
}
void HistogramItem::setSpacing( int spacing )
{
if ( d_data->spacing != spacing )
{
d_data->spacing = spacing;
itemChanged();
}
}
int HistogramItem::spacing() const
{
return d_data->spacing;
}
void HistogramItem::setPen( const QPen &pen )
{
if ( d_data->pen != pen )
{
d_data->pen = pen;
itemChanged();
}
}
QPen HistogramItem::pen() const
{
return d_data->pen;
}
QwtDoubleRect HistogramItem::boundingRect() const
{
QwtDoubleRect rect = d_data->data.boundingRect();
if ( !rect.isValid() )
return rect;
if ( d_data->attributes & Xfy )
{
rect = QwtDoubleRect( rect.y(), rect.x(),
rect.height(), rect.width() );
if ( rect.left() > d_data->reference )
rect.setLeft( d_data->reference );
else if ( rect.right() < d_data->reference )
rect.setRight( d_data->reference );
}
else
{
if ( rect.bottom() < d_data->reference )
rect.setBottom( d_data->reference );
else if ( rect.top() > d_data->reference )
rect.setTop( d_data->reference );
}
return rect;
}
int HistogramItem::rtti() const
{
return QwtPlotItem::Rtti_PlotHistogram;
}
void HistogramItem::setHistogramAttribute( HistogramAttribute attribute, bool on )
{
if ( bool( d_data->attributes & attribute ) == on )
return;
if ( on )
d_data->attributes |= attribute;
else
d_data->attributes &= ~attribute;
itemChanged();
}
bool HistogramItem::testHistogramAttribute( HistogramAttribute attribute ) const
{
return d_data->attributes & attribute;
}
void HistogramItem::draw( QPainter *painter, const QwtScaleMap &xMap,
const QwtScaleMap &yMap, const QRect & ) const
{
const QwtIntervalData &iData = d_data->data;
const int x0 = xMap.transform( baseline() );
const int y0 = yMap.transform( baseline() );
for ( int i = 0; i < ( int )iData.size(); i++ )
{
if ( d_data->attributes & HistogramItem::Xfy )
{
const int x2 = xMap.transform( iData.value( i ) );
if ( x2 == x0 )
continue;
int y1 = yMap.transform( iData.interval( i ).minValue() );
int y2 = yMap.transform( iData.interval( i ).maxValue() );
if ( y1 > y2 )
qSwap( y1, y2 );
if ( i < ( int )iData.size() - 2 )
{
const int yy1 = yMap.transform( iData.interval( i + 1 ).minValue() );
const int yy2 = yMap.transform( iData.interval( i + 1 ).maxValue() );
if ( y2 == qwtMin( yy1, yy2 ) )
{
const int xx2 = xMap.transform(
iData.interval( i + 1 ).minValue() );
if ( xx2 != x0 && (( xx2 < x0 && x2 < x0 ) ||
( xx2 > x0 && x2 > x0 ) ) )
{
// distance between neighboured bars
y2 += d_data->spacing;
}
}
}
drawBar( painter, Qt::Horizontal,
QRect( x0, y1, x2 - x0, y2 - y1 ) );
}
else
{
const int y2 = yMap.transform( iData.value( i ) );
if ( y2 == y0 )
continue;
int x1 = xMap.transform( iData.interval( i ).minValue() );
int x2 = xMap.transform( iData.interval( i ).maxValue() );
if ( x1 > x2 )
qSwap( x1, x2 );
if ( i < ( int )iData.size() - 2 )
{
const int xx1 = xMap.transform( iData.interval( i + 1 ).minValue() );
const int xx2 = xMap.transform( iData.interval( i + 1 ).maxValue() );
if ( x2 == qwtMin( xx1, xx2 ) )
{
const int yy2 = yMap.transform( iData.value( i + 1 ) );
if ( yy2 != y0 && (( yy2 < y0 && y2 < y0 ) ||
( yy2 > y0 && y2 > y0 ) ) )
{
//distance between neighboured bars
x2 -= d_data->spacing;
}
}
}
drawBar( painter, Qt::Vertical,
QRect( x1, y0, x2 - x1, y2 - y0 ) );
}
}
}
void HistogramItem::drawBar( QPainter *painter,
Qt::Orientation, const QRect& rect ) const
{
painter->save();
#if QT_VERSION >= 0x040000
const QRect r = rect.normalized();
#else
const QRect r = rect.normalize();
#endif
painter->setBrush( d_data->color );
if ( d_data->flat )
{
painter->setPen( d_data->pen );
int penWidth = d_data->pen == Qt::NoPen ? 0 :
( d_data->pen.isCosmetic() ? 1 : d_data->pen.width() );
QwtPainter::drawRect( painter, r.x(), r.y(),
r.width(), r.height() - penWidth );
}
else
{
const int factor = 125;
const QColor light( d_data->color.light( factor ) );
const QColor dark( d_data->color.dark( factor ) );
QwtPainter::drawRect( painter, r.x() + 1, r.y() + 1,
r.width() - 2, r.height() - 2 );
painter->setBrush( Qt::NoBrush );
painter->setPen( QPen( light, 2 ) );
#if QT_VERSION >= 0x040000
QwtPainter::drawLine( painter,
r.left() + 1, r.top() + 2, r.right() + 1, r.top() + 2 );
#else
QwtPainter::drawLine( painter,
r.left(), r.top() + 2, r.right() + 1, r.top() + 2 );
#endif
painter->setPen( QPen( dark, 2 ) );
#if QT_VERSION >= 0x040000
QwtPainter::drawLine( painter,
r.left() + 1, r.bottom(), r.right() + 1, r.bottom() );
#else
QwtPainter::drawLine( painter,
r.left(), r.bottom(), r.right() + 1, r.bottom() );
#endif
painter->setPen( QPen( light, 1 ) );
#if QT_VERSION >= 0x040000
QwtPainter::drawLine( painter,
r.left(), r.top() + 1, r.left(), r.bottom() );
QwtPainter::drawLine( painter,
r.left() + 1, r.top() + 2, r.left() + 1, r.bottom() - 1 );
#else
QwtPainter::drawLine( painter,
r.left(), r.top() + 1, r.left(), r.bottom() + 1 );
QwtPainter::drawLine( painter,
r.left() + 1, r.top() + 2, r.left() + 1, r.bottom() );
#endif
painter->setPen( QPen( dark, 1 ) );
#if QT_VERSION >= 0x040000
QwtPainter::drawLine( painter,
r.right() + 1, r.top() + 1, r.right() + 1, r.bottom() );
QwtPainter::drawLine( painter,
r.right(), r.top() + 2, r.right(), r.bottom() - 1 );
#else
QwtPainter::drawLine( painter,
r.right() + 1, r.top() + 1, r.right() + 1, r.bottom() + 1 );
QwtPainter::drawLine( painter,
r.right(), r.top() + 2, r.right(), r.bottom() );
#endif
}
painter->restore();
}
//! Update the widget that represents the curve on the legend
// this was adapted from QwtPlotCurve::updateLegend()
void HistogramItem::updateLegend( QwtLegend *legend ) const
{
if ( !legend )
return;
QwtPlotItem::updateLegend( legend );
QWidget *widget = legend->find( this );
if ( !widget || !widget->inherits( "QwtLegendItem" ) )
return;
QwtLegendItem *legendItem = ( QwtLegendItem * )widget;
#if QT_VERSION < 0x040000
const bool doUpdate = legendItem->isUpdatesEnabled();
#else
const bool doUpdate = legendItem->updatesEnabled();
#endif
legendItem->setUpdatesEnabled( false );
const int policy = legend->displayPolicy();
if ( policy == QwtLegend::FixedIdentifier )
{
int mode = legend->identifierMode();
legendItem->setCurvePen( QPen( color() ) );
if ( mode & QwtLegendItem::ShowText )
legendItem->setText( title() );
else
legendItem->setText( QwtText() );
legendItem->setIdentifierMode( mode );
}
else if ( policy == QwtLegend::AutoIdentifier )
{
int mode = 0;
legendItem->setCurvePen( QPen( color() ) );
mode |= QwtLegendItem::ShowLine;
if ( !title().isEmpty() )
{
legendItem->setText( title() );
mode |= QwtLegendItem::ShowText;
}
else
{
legendItem->setText( QwtText() );
}
legendItem->setIdentifierMode( mode );
}
legendItem->setUpdatesEnabled( doUpdate );
legendItem->update();
}
#endif

View File

@ -6,6 +6,9 @@
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the Qwt License, Version 1.0 * modify it under the terms of the Qwt License, Version 1.0
*****************************************************************************/ *****************************************************************************/
#include <qwt_global.h>
#if defined(QWT_VERSION) && QWT_VERSION<0x060000
#ifndef HISTOGRAM_ITEM_H #ifndef HISTOGRAM_ITEM_H
#define HISTOGRAM_ITEM_H #define HISTOGRAM_ITEM_H
@ -31,6 +34,15 @@ class HistogramItem: public QwtPlotItem
void setColor( const QColor & ); void setColor( const QColor & );
QColor color() const; QColor color() const;
void setFlat( bool flat );
bool flat() const;
void setSpacing( int spacing );
int spacing() const;
void setPen( const QPen& pen );
QPen pen() const;
virtual QwtDoubleRect boundingRect() const; virtual QwtDoubleRect boundingRect() const;
virtual int rtti() const; virtual int rtti() const;
@ -77,333 +89,11 @@ class HistogramItem::PrivateData
int attributes; int attributes;
QwtIntervalData data; QwtIntervalData data;
QColor color; QColor color;
QPen pen;
double reference; double reference;
bool flat;
int spacing;
}; };
HistogramItem::HistogramItem( const QwtText &title ):
QwtPlotItem( title )
{
init();
}
HistogramItem::HistogramItem( const QString &title ):
QwtPlotItem( QwtText( title ) )
{
init();
}
HistogramItem::~HistogramItem()
{
delete d_data;
}
void HistogramItem::init()
{
d_data = new PrivateData();
d_data->reference = 0.0;
d_data->attributes = HistogramItem::Auto;
setItemAttribute( QwtPlotItem::AutoScale, true );
setItemAttribute( QwtPlotItem::Legend, true );
setZ( 20.0 );
}
void HistogramItem::setBaseline( double reference )
{
if ( d_data->reference != reference )
{
d_data->reference = reference;
itemChanged();
}
}
double HistogramItem::baseline() const
{
return d_data->reference;
}
void HistogramItem::setData( const QwtIntervalData &data )
{
d_data->data = data;
itemChanged();
}
const QwtIntervalData &HistogramItem::data() const
{
return d_data->data;
}
void HistogramItem::setColor( const QColor &color )
{
if ( d_data->color != color )
{
d_data->color = color;
itemChanged();
}
}
QColor HistogramItem::color() const
{
return d_data->color;
}
QwtDoubleRect HistogramItem::boundingRect() const
{
QwtDoubleRect rect = d_data->data.boundingRect();
if ( !rect.isValid() )
return rect;
if ( d_data->attributes & Xfy )
{
rect = QwtDoubleRect( rect.y(), rect.x(),
rect.height(), rect.width() );
if ( rect.left() > d_data->reference )
rect.setLeft( d_data->reference );
else if ( rect.right() < d_data->reference )
rect.setRight( d_data->reference );
}
else
{
if ( rect.bottom() < d_data->reference )
rect.setBottom( d_data->reference );
else if ( rect.top() > d_data->reference )
rect.setTop( d_data->reference );
}
return rect;
}
int HistogramItem::rtti() const
{
return QwtPlotItem::Rtti_PlotHistogram;
}
void HistogramItem::setHistogramAttribute( HistogramAttribute attribute, bool on )
{
if ( bool( d_data->attributes & attribute ) == on )
return;
if ( on )
d_data->attributes |= attribute;
else
d_data->attributes &= ~attribute;
itemChanged();
}
bool HistogramItem::testHistogramAttribute( HistogramAttribute attribute ) const
{
return d_data->attributes & attribute;
}
void HistogramItem::draw( QPainter *painter, const QwtScaleMap &xMap,
const QwtScaleMap &yMap, const QRect & ) const
{
const QwtIntervalData &iData = d_data->data;
painter->setPen( QPen( d_data->color ) );
const int x0 = xMap.transform( baseline() );
const int y0 = yMap.transform( baseline() );
for ( int i = 0; i < ( int )iData.size(); i++ )
{
if ( d_data->attributes & HistogramItem::Xfy )
{
const int x2 = xMap.transform( iData.value( i ) );
if ( x2 == x0 )
continue;
int y1 = yMap.transform( iData.interval( i ).minValue() );
int y2 = yMap.transform( iData.interval( i ).maxValue() );
if ( y1 > y2 )
qSwap( y1, y2 );
if ( i < ( int )iData.size() - 2 )
{
const int yy1 = yMap.transform( iData.interval( i + 1 ).minValue() );
const int yy2 = yMap.transform( iData.interval( i + 1 ).maxValue() );
if ( y2 == qwtMin( yy1, yy2 ) )
{
const int xx2 = xMap.transform(
iData.interval( i + 1 ).minValue() );
if ( xx2 != x0 && (( xx2 < x0 && x2 < x0 ) ||
( xx2 > x0 && x2 > x0 ) ) )
{
// One pixel distance between neighboured bars
y2++;
}
}
}
drawBar( painter, Qt::Horizontal,
QRect( x0, y1, x2 - x0, y2 - y1 ) );
}
else
{
const int y2 = yMap.transform( iData.value( i ) );
if ( y2 == y0 )
continue;
int x1 = xMap.transform( iData.interval( i ).minValue() );
int x2 = xMap.transform( iData.interval( i ).maxValue() );
if ( x1 > x2 )
qSwap( x1, x2 );
if ( i < ( int )iData.size() - 2 )
{
const int xx1 = xMap.transform( iData.interval( i + 1 ).minValue() );
const int xx2 = xMap.transform( iData.interval( i + 1 ).maxValue() );
if ( x2 == qwtMin( xx1, xx2 ) )
{
const int yy2 = yMap.transform( iData.value( i + 1 ) );
if ( yy2 != y0 && (( yy2 < y0 && y2 < y0 ) ||
( yy2 > y0 && y2 > y0 ) ) )
{
// One pixel distance between neighboured bars
x2--;
}
}
}
drawBar( painter, Qt::Vertical,
QRect( x1, y0, x2 - x1, y2 - y0 ) );
}
}
}
void HistogramItem::drawBar( QPainter *painter,
Qt::Orientation, const QRect& rect ) const
{
painter->save();
const QColor color( painter->pen().color() );
#if QT_VERSION >= 0x040000
const QRect r = rect.normalized();
#else
const QRect r = rect.normalize();
#endif #endif
const int factor = 125;
const QColor light( color.light( factor ) );
const QColor dark( color.dark( factor ) );
painter->setBrush( color );
painter->setPen( Qt::NoPen );
QwtPainter::drawRect( painter, r.x() + 1, r.y() + 1,
r.width() - 2, r.height() - 2 );
painter->setBrush( Qt::NoBrush );
painter->setPen( QPen( light, 2 ) );
#if QT_VERSION >= 0x040000
QwtPainter::drawLine( painter,
r.left() + 1, r.top() + 2, r.right() + 1, r.top() + 2 );
#else
QwtPainter::drawLine( painter,
r.left(), r.top() + 2, r.right() + 1, r.top() + 2 );
#endif
painter->setPen( QPen( dark, 2 ) );
#if QT_VERSION >= 0x040000
QwtPainter::drawLine( painter,
r.left() + 1, r.bottom(), r.right() + 1, r.bottom() );
#else
QwtPainter::drawLine( painter,
r.left(), r.bottom(), r.right() + 1, r.bottom() );
#endif
painter->setPen( QPen( light, 1 ) );
#if QT_VERSION >= 0x040000
QwtPainter::drawLine( painter,
r.left(), r.top() + 1, r.left(), r.bottom() );
QwtPainter::drawLine( painter,
r.left() + 1, r.top() + 2, r.left() + 1, r.bottom() - 1 );
#else
QwtPainter::drawLine( painter,
r.left(), r.top() + 1, r.left(), r.bottom() + 1 );
QwtPainter::drawLine( painter,
r.left() + 1, r.top() + 2, r.left() + 1, r.bottom() );
#endif
painter->setPen( QPen( dark, 1 ) );
#if QT_VERSION >= 0x040000
QwtPainter::drawLine( painter,
r.right() + 1, r.top() + 1, r.right() + 1, r.bottom() );
QwtPainter::drawLine( painter,
r.right(), r.top() + 2, r.right(), r.bottom() - 1 );
#else
QwtPainter::drawLine( painter,
r.right() + 1, r.top() + 1, r.right() + 1, r.bottom() + 1 );
QwtPainter::drawLine( painter,
r.right(), r.top() + 2, r.right(), r.bottom() );
#endif
painter->restore();
}
//! Update the widget that represents the curve on the legend
// this was adapted from QwtPlotCurve::updateLegend()
void HistogramItem::updateLegend( QwtLegend *legend ) const
{
if ( !legend )
return;
QwtPlotItem::updateLegend( legend );
QWidget *widget = legend->find( this );
if ( !widget || !widget->inherits( "QwtLegendItem" ) )
return;
QwtLegendItem *legendItem = ( QwtLegendItem * )widget;
#if QT_VERSION < 0x040000
const bool doUpdate = legendItem->isUpdatesEnabled();
#else
const bool doUpdate = legendItem->updatesEnabled();
#endif
legendItem->setUpdatesEnabled( false );
const int policy = legend->displayPolicy();
if ( policy == QwtLegend::FixedIdentifier )
{
int mode = legend->identifierMode();
legendItem->setCurvePen( QPen( color() ) );
if ( mode & QwtLegendItem::ShowText )
legendItem->setText( title() );
else
legendItem->setText( QwtText() );
legendItem->setIdentifierMode( mode );
}
else if ( policy == QwtLegend::AutoIdentifier )
{
int mode = 0;
legendItem->setCurvePen( QPen( color() ) );
mode |= QwtLegendItem::ShowLine;
if ( !title().isEmpty() )
{
legendItem->setText( title() );
mode |= QwtLegendItem::ShowText;
}
else
{
legendItem->setText( QwtText() );
}
legendItem->setIdentifierMode( mode );
}
legendItem->setUpdatesEnabled( doUpdate );
legendItem->update();
}
#endif #endif

View File

@ -0,0 +1,203 @@
/***************************************************************************
qgsgraduatedhistogramwidget.cpp
-------------------------------
begin : May 2015
copyright : (C) 2015 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsgraduatedhistogramwidget.h"
#include "qgsgraduatedsymbolrendererv2.h"
#include "qgsgraduatedsymbolrendererv2widget.h"
#include "qgsapplication.h"
#include "qgsvectorlayer.h"
#include "qgsstatisticalsummary.h"
#include <QSettings>
#include <QObject>
#include <QMouseEvent>
// QWT Charting widget
#include <qwt_global.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_marker.h>
#include <qwt_plot_picker.h>
#include <qwt_picker_machine.h>
#include <qwt_plot_layout.h>
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
#include <qwt_plot_renderer.h>
#include <qwt_plot_histogram.h>
#else
#include "../raster/qwt5_histogram_item.h"
#endif
QgsGraduatedHistogramWidget::QgsGraduatedHistogramWidget( QWidget *parent )
: QgsHistogramWidget( parent )
, mRenderer( 0 )
, mHistoPicker( 0 )
, mPressedValue( 0 )
{
mFilter = new QgsGraduatedHistogramEventFilter( mPlot );
connect( mFilter, SIGNAL( mousePress( double ) ), this, SLOT( mousePress( double ) ) );
connect( mFilter, SIGNAL( mouseRelease( double ) ), this, SLOT( mouseRelease( double ) ) );
mHistoPicker = new QwtPlotPicker( mPlot->canvas() );
mHistoPicker->setTrackerMode( QwtPicker::ActiveOnly );
mHistoPicker->setRubberBand( QwtPicker::VLineRubberBand );
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
mHistoPicker->setStateMachine( new QwtPickerDragPointMachine );
#else
mHistoPicker->setSelectionFlags( QwtPicker::PointSelection | QwtPicker::DragSelection );
#endif
}
QgsGraduatedHistogramWidget::~QgsGraduatedHistogramWidget()
{
}
void QgsGraduatedHistogramWidget::setRenderer( QgsGraduatedSymbolRendererV2 *renderer )
{
mRenderer = renderer;
mRedrawRequired = true;
}
void QgsGraduatedHistogramWidget::drawHistogram()
{
if ( !mRenderer )
return;
mRanges = mRenderer->ranges();
QgsHistogramWidget::drawHistogram();
// histo picker
mHistoPicker->setEnabled( true );
}
void QgsGraduatedHistogramWidget::mousePress( double value )
{
mPressedValue = value;
int closestRangeIndex = 0;
int minPixelDistance = 9999;
findClosestRange( mPressedValue, closestRangeIndex, minPixelDistance );
if ( minPixelDistance <= 6 )
{
//moving a break, so hide the break line
mRangeMarkers.at( closestRangeIndex )->hide();
mRedrawRequired = true;
mPlot->replot();
}
}
void QgsGraduatedHistogramWidget::mouseRelease( double value )
{
int closestRangeIndex = 0;
int minPixelDistance = 9999;
findClosestRange( mPressedValue, closestRangeIndex, minPixelDistance );
if ( minPixelDistance <= 6 )
{
//do a sanity check - don't allow users to drag a break line
//into the middle of a different range. Doing so causes overlap
//of the ranges
//new value needs to be within range covered by closestRangeIndex or
//closestRangeIndex + 1
if ( value <= mRenderer->ranges().at( closestRangeIndex ).lowerValue() ||
value >= mRenderer->ranges().at( closestRangeIndex + 1 ).upperValue() )
{
refreshHistogram();
return;
}
mRenderer->updateRangeUpperValue( closestRangeIndex, value );
mRenderer->updateRangeLowerValue( closestRangeIndex + 1, value );
emit rangesModified( false );
}
else
{
//if distance from markers is too big, add a break
mRenderer->addBreak( value );
emit rangesModified( true );
}
drawHistogram();
}
void QgsGraduatedHistogramWidget::findClosestRange( double value, int &closestRangeIndex, int& pixelDistance ) const
{
const QgsRangeList& ranges = mRenderer->ranges();
double minDistance = DBL_MAX;
int pressedPixel = mPlot->canvasMap( QwtPlot::xBottom ).transform( value );
for ( int i = 0; i < ranges.count() - 1; ++i )
{
if ( qAbs( mPressedValue - ranges.at( i ).upperValue() ) < minDistance )
{
closestRangeIndex = i;
minDistance = qAbs( mPressedValue - ranges.at( i ).upperValue() );
pixelDistance = qAbs( pressedPixel - mPlot->canvasMap( QwtPlot::xBottom ).transform( ranges.at( i ).upperValue() ) );
}
}
}
QgsGraduatedHistogramEventFilter::QgsGraduatedHistogramEventFilter( QwtPlot *plot )
: QObject( plot )
, mPlot( plot )
{
mPlot->canvas()->installEventFilter( this );
}
bool QgsGraduatedHistogramEventFilter::eventFilter( QObject *object, QEvent *event )
{
switch ( event->type() )
{
case QEvent::MouseButtonPress:
{
const QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent* >( event );
if ( mouseEvent->button() == Qt::LeftButton )
{
emit mousePress( posToValue( mouseEvent->pos() ) );
}
break;
}
case QEvent::MouseButtonRelease:
{
const QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent* >( event );
if ( mouseEvent->button() == Qt::LeftButton )
{
emit mouseRelease( posToValue( mouseEvent->pos() ) );
}
break;
}
default:
break;
}
return QObject::eventFilter( object, event );
}
double QgsGraduatedHistogramEventFilter::posToValue( const QPointF &point ) const
{
if ( !mPlot )
return -99999999;
return mPlot->canvasMap( QwtPlot::xBottom ).invTransform( point.x() );
}

View File

@ -0,0 +1,114 @@
/***************************************************************************
qgsgraduatedhistogramwidget.h
-----------------------------
begin : May 2015
copyright : (C) 2015 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSGRADUATEDHISTOGRAMWIDGET_H
#define QGSGRADUATEDHISTOGRAMWIDGET_H
#include "qgshistogramwidget.h"
class QwtPlotPicker;
class QgsGraduatedHistogramEventFilter;
/** \ingroup gui
* \class QgsGraduatedHistogramWidget
* \brief Graphical histogram for displaying distribution of field values and
* editing range breaks for a QgsGraduatedSymbolRendererV2 renderer.
*
* \note Added in version 2.9
*/
class GUI_EXPORT QgsGraduatedHistogramWidget : public QgsHistogramWidget
{
Q_OBJECT
public:
/** QgsGraduatedHistogramWidget constructor
* @param parent parent widget
*/
QgsGraduatedHistogramWidget( QWidget *parent = 0 );
~QgsGraduatedHistogramWidget();
/** Sets the QgsGraduatedSymbolRendererV2 renderer associated with the histogram.
* The histogram will fetch the ranges from the renderer before every refresh.
* @param renderer associated QgsGraduatedSymbolRendererV2
*/
void setRenderer( QgsGraduatedSymbolRendererV2* renderer );
signals:
/** Emitted when the user modifies the graduated ranges using the histogram widget.
* @param rangesAdded true if the user has added ranges, false if the user has just
* modified existing range breaks
*/
void rangesModified( bool rangesAdded );
protected:
virtual void drawHistogram() override;
private slots:
void mousePress( double value );
void mouseRelease( double value );
private:
QgsGraduatedSymbolRendererV2* mRenderer;
QwtPlotPicker* mHistoPicker;
QgsGraduatedHistogramEventFilter* mFilter;
double mPressedValue;
void findClosestRange( double value, int &closestRangeIndex, int &pixelDistance ) const;
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
QwtPlotHistogram* createPlotHistogram( const QString& title, const QColor& color ) const;
#else
HistogramItem* createHistoItem( const QString& title, const QColor& color ) const;
#endif
};
//
// NOTE:
// For private use by QgsGraduatedHistogramWidget only,
// not part of stable api or exposed to Python bindings
//
class GUI_EXPORT QgsGraduatedHistogramEventFilter: public QObject
{
Q_OBJECT
public:
QgsGraduatedHistogramEventFilter( QwtPlot *plot );
virtual ~QgsGraduatedHistogramEventFilter() {}
virtual bool eventFilter( QObject* object, QEvent* event ) override;
signals:
void mousePress( double );
void mouseRelease( double );
private:
QwtPlot* mPlot;
double posToValue( const QPointF& point ) const;
};
#endif //QGSGRADUATEDHISTOGRAMWIDGET_H

View File

@ -335,9 +335,16 @@ void QgsGraduatedSymbolRendererV2Model::sort( int column, Qt::SortOrder order )
QgsDebugMsg( "Done" ); QgsDebugMsg( "Done" );
} }
void QgsGraduatedSymbolRendererV2Model::updateSymbology() void QgsGraduatedSymbolRendererV2Model::updateSymbology( bool resetModel )
{ {
emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) ); if ( resetModel )
{
reset();
}
else
{
emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) );
}
} }
void QgsGraduatedSymbolRendererV2Model::updateLabels() void QgsGraduatedSymbolRendererV2Model::updateLabels()
@ -465,6 +472,11 @@ QgsGraduatedSymbolRendererV2Widget::QgsGraduatedSymbolRendererV2Widget( QgsVecto
connect( mDataDefinedMenus, SIGNAL( sizeScaleFieldChanged( QString ) ), this, SLOT( sizeScaleFieldChanged( QString ) ) ); connect( mDataDefinedMenus, SIGNAL( sizeScaleFieldChanged( QString ) ), this, SLOT( sizeScaleFieldChanged( QString ) ) );
connect( mDataDefinedMenus, SIGNAL( scaleMethodChanged( QgsSymbolV2::ScaleMethod ) ), this, SLOT( scaleMethodChanged( QgsSymbolV2::ScaleMethod ) ) ); connect( mDataDefinedMenus, SIGNAL( scaleMethodChanged( QgsSymbolV2::ScaleMethod ) ), this, SLOT( scaleMethodChanged( QgsSymbolV2::ScaleMethod ) ) );
btnAdvanced->setMenu( advMenu ); btnAdvanced->setMenu( advMenu );
mHistogramWidget->setLayer( mLayer );
mHistogramWidget->setRenderer( mRenderer );
connect( mHistogramWidget, SIGNAL( rangesModified( bool ) ), this, SLOT( refreshRanges( bool ) ) );
connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), mHistogramWidget, SLOT( setSourceFieldExp( QString ) ) );
} }
void QgsGraduatedSymbolRendererV2Widget::on_mSizeUnitWidget_changed() void QgsGraduatedSymbolRendererV2Widget::on_mSizeUnitWidget_changed()
@ -543,6 +555,7 @@ void QgsGraduatedSymbolRendererV2Widget::updateUiFromRenderer( bool updateCount
// set column // set column
QString attrName = mRenderer->classAttribute(); QString attrName = mRenderer->classAttribute();
mExpressionWidget->setField( attrName ); mExpressionWidget->setField( attrName );
mHistogramWidget->setSourceFieldExp( attrName );
// set source symbol // set source symbol
if ( mRenderer->sourceSymbol() ) if ( mRenderer->sourceSymbol() )
@ -593,6 +606,8 @@ void QgsGraduatedSymbolRendererV2Widget::updateUiFromRenderer( bool updateCount
viewGraduated->resizeColumnToContents( 1 ); viewGraduated->resizeColumnToContents( 1 );
viewGraduated->resizeColumnToContents( 2 ); viewGraduated->resizeColumnToContents( 2 );
mHistogramWidget->refreshHistogram();
connectUpdateHandlers(); connectUpdateHandlers();
} }
@ -627,6 +642,14 @@ void QgsGraduatedSymbolRendererV2Widget::on_methodComboBox_currentIndexChanged(
} }
} }
void QgsGraduatedSymbolRendererV2Widget::refreshRanges( bool reset )
{
if ( !mModel )
return;
mModel->updateSymbology( reset );
}
void QgsGraduatedSymbolRendererV2Widget::classifyGraduated() void QgsGraduatedSymbolRendererV2Widget::classifyGraduated()
{ {
QString attrName = mExpressionWidget->currentField(); QString attrName = mExpressionWidget->currentField();
@ -1052,6 +1075,7 @@ void QgsGraduatedSymbolRendererV2Widget::rowsMoved()
void QgsGraduatedSymbolRendererV2Widget::modelDataChanged() void QgsGraduatedSymbolRendererV2Widget::modelDataChanged()
{ {
mHistogramWidget->refreshHistogram();
} }
void QgsGraduatedSymbolRendererV2Widget::keyPressEvent( QKeyEvent* event ) void QgsGraduatedSymbolRendererV2Widget::keyPressEvent( QKeyEvent* event )

View File

@ -49,7 +49,7 @@ class GUI_EXPORT QgsGraduatedSymbolRendererV2Model : public QAbstractItemModel
void deleteRows( QList<int> rows ); void deleteRows( QList<int> rows );
void removeAllRows(); void removeAllRows();
void sort( int column, Qt::SortOrder order = Qt::AscendingOrder ) override; void sort( int column, Qt::SortOrder order = Qt::AscendingOrder ) override;
void updateSymbology(); void updateSymbology( bool resetModel = false );
void updateLabels(); void updateLabels();
signals: signals:
@ -111,6 +111,7 @@ class GUI_EXPORT QgsGraduatedSymbolRendererV2Widget : public QgsRendererV2Widget
void modelDataChanged(); void modelDataChanged();
void on_mSizeUnitWidget_changed(); void on_mSizeUnitWidget_changed();
void on_methodComboBox_currentIndexChanged( int ); void on_methodComboBox_currentIndexChanged( int );
void refreshRanges( bool reset = false );
protected: protected:
void updateUiFromRenderer( bool updateCount = true ); void updateUiFromRenderer( bool updateCount = true );

View File

@ -7,53 +7,213 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>759</width> <width>759</width>
<height>638</height> <height>394</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout" rowstretch="0,1,0,0"> <layout class="QGridLayout" name="gridLayout" rowstretch="0,1,0">
<property name="margin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
<item row="3" column="0" colspan="4"> <property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Classes</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_8">
<property name="text">
<string>Mode</string>
</property>
<property name="buddy">
<cstring>cboGraduatedMode</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cboGraduatedMode">
<item>
<property name="text">
<string>Equal Interval</string>
</property>
</item>
<item>
<property name="text">
<string>Quantile (Equal Count)</string>
</property>
</item>
<item>
<property name="text">
<string>Natural Breaks (Jenks)</string>
</property>
</item>
<item>
<property name="text">
<string>Standard Deviation</string>
</property>
</item>
<item>
<property name="text">
<string>Pretty Breaks</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Classes</string>
</property>
<property name="buddy">
<cstring>spinGraduatedClasses</cstring>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinGraduatedClasses">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="value">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnGraduatedClassify">
<property name="text">
<string>Classify</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTreeView" name="viewGraduated">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="showDropIndicator" stdset="0">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="btnGraduatedAdd">
<property name="text">
<string>Add class</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnGraduatedDelete">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnDeleteAllClasses">
<property name="text">
<string>Delete all</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbxLinkBoundaries">
<property name="text">
<string>Link class boundaries</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Histogram</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0">
<item>
<widget class="QgsGraduatedHistogramWidget" name="mHistogramWidget" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="_2"> <layout class="QHBoxLayout" name="_2">
<item>
<widget class="QPushButton" name="btnGraduatedClassify">
<property name="text">
<string>Classify</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnGraduatedAdd">
<property name="text">
<string>Add class</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnGraduatedDelete">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnDeleteAllClasses">
<property name="text">
<string>Delete all</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbxLinkBoundaries">
<property name="text">
<string>Link class boundaries</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item> <item>
<spacer> <spacer>
<property name="orientation"> <property name="orientation">
@ -76,11 +236,22 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="0" column="0" colspan="4"> <item row="0" column="0" colspan="2">
<layout class="QGridLayout"> <layout class="QGridLayout" columnstretch="0,3,0,0,1,0">
<property name="rightMargin"> <property name="rightMargin">
<number>6</number> <number>6</number>
</property> </property>
<item row="2" column="1" colspan="2">
<widget class="QLineEdit" name="txtLegendFormat">
<property name="toolTip">
<string>Template for the legend text associated with each classification.
Use &quot;%1&quot; for the lower bound of the classification, and &quot;%2&quot; for the upper bound.</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="4"> <item row="2" column="4">
<layout class="QHBoxLayout" name="horizontalLayout_7"> <layout class="QHBoxLayout" name="horizontalLayout_7">
<item> <item>
@ -107,45 +278,65 @@ Negative rounds to powers of 10</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="cbxTrimTrailingZeroes">
<property name="toolTip">
<string>Check to remove trailing zeroes after the decimal point from the upper and lower values in the legend.</string>
</property>
<property name="text">
<string>Trim</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item row="0" column="4"> <item row="0" column="0">
<widget class="QComboBox" name="cboGraduatedMode"> <widget class="QLabel" name="label_4">
<item> <property name="text">
<property name="text"> <string>Column</string>
<string>Equal Interval</string> </property>
</property> </widget>
</item> </item>
<item> <item row="0" column="1" colspan="2">
<property name="text"> <widget class="QgsFieldExpressionWidget" name="mExpressionWidget" native="true">
<string>Quantile (Equal Count)</string> <property name="minimumSize">
</property> <size>
</item> <width>10</width>
<item> <height>0</height>
<property name="text"> </size>
<string>Natural Breaks (Jenks)</string> </property>
</property> <property name="maximumSize">
</item> <size>
<item> <width>500</width>
<property name="text"> <height>16777215</height>
<string>Standard Deviation</string> </size>
</property> </property>
</item> <property name="focusPolicy">
<item> <enum>Qt::StrongFocus</enum>
<property name="text"> </property>
<string>Pretty Breaks</string> </widget>
</property> </item>
</item> <item row="1" column="1" colspan="2">
<widget class="QPushButton" name="btnChangeGraduatedSymbol">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Change...</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Precision</string>
</property>
<property name="buddy">
<cstring>spinPrecision</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Symbol</string>
</property>
<property name="buddy">
<cstring>btnChangeGraduatedSymbol</cstring>
</property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
@ -155,35 +346,6 @@ Negative rounds to powers of 10</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="3">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Classes</string>
</property>
<property name="buddy">
<cstring>spinGraduatedClasses</cstring>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QSpinBox" name="spinGraduatedClasses">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="value">
<number>5</number>
</property>
</widget>
</item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
@ -197,17 +359,20 @@ Negative rounds to powers of 10</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="3"> <item row="3" column="1" colspan="2">
<widget class="QLabel" name="label_8"> <widget class="QComboBox" name="methodComboBox"/>
<property name="text"> </item>
<string>Mode</string> <item row="2" column="5">
<widget class="QCheckBox" name="cbxTrimTrailingZeroes">
<property name="toolTip">
<string>Check to remove trailing zeroes after the decimal point from the upper and lower values in the legend.</string>
</property> </property>
<property name="buddy"> <property name="text">
<cstring>cboGraduatedMode</cstring> <string>Trim</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1" colspan="4"> <item row="4" column="1" colspan="5">
<widget class="QStackedWidget" name="mMethodStackedWidget"> <widget class="QStackedWidget" name="mMethodStackedWidget">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum"> <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
@ -226,7 +391,16 @@ Negative rounds to powers of 10</string>
</property> </property>
<widget class="QWidget" name="page"> <widget class="QWidget" name="page">
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1,0,0"> <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1,0,0">
<property name="margin"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
@ -266,7 +440,16 @@ Negative rounds to powers of 10</string>
</widget> </widget>
<widget class="QWidget" name="page_2"> <widget class="QWidget" name="page_2">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1,0,1,1"> <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1,0,1,1">
<property name="margin"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
@ -293,7 +476,7 @@ Negative rounds to powers of 10</string>
<property name="value"> <property name="value">
<double>1.000000000000000</double> <double>1.000000000000000</double>
</property> </property>
<property name="showClearButton"> <property name="showClearButton" stdset="0">
<bool>false</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
@ -319,7 +502,7 @@ Negative rounds to powers of 10</string>
<property name="value"> <property name="value">
<double>10.000000000000000</double> <double>10.000000000000000</double>
</property> </property>
<property name="showClearButton"> <property name="showClearButton" stdset="0">
<bool>false</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
@ -341,106 +524,8 @@ Negative rounds to powers of 10</string>
</widget> </widget>
</widget> </widget>
</item> </item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Symbol</string>
</property>
<property name="buddy">
<cstring>btnChangeGraduatedSymbol</cstring>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Precision</string>
</property>
<property name="buddy">
<cstring>spinPrecision</cstring>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Column</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QgsFieldExpressionWidget" name="mExpressionWidget">
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>16777215</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QLineEdit" name="txtLegendFormat">
<property name="toolTip">
<string>Template for the legend text associated with each classification.
Use &quot;%1&quot; for the lower bound of the classification, and &quot;%2&quot; for the upper bound.</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QPushButton" name="btnChangeGraduatedSymbol">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Change...</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QComboBox" name="methodComboBox"/>
</item>
</layout> </layout>
</item> </item>
<item row="1" column="0" rowspan="2" colspan="4">
<widget class="QTreeView" name="viewGraduated">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="showDropIndicator" stdset="0">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
@ -449,6 +534,12 @@ Use &quot;%1&quot; for the lower bound of the classification, and &quot;%2&quot;
<extends>QDoubleSpinBox</extends> <extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header> <header>qgsdoublespinbox.h</header>
</customwidget> </customwidget>
<customwidget>
<class>QgsUnitSelectionWidget</class>
<extends>QWidget</extends>
<header>qgsunitselectionwidget.h</header>
<container>1</container>
</customwidget>
<customwidget> <customwidget>
<class>QgsFieldExpressionWidget</class> <class>QgsFieldExpressionWidget</class>
<extends>QWidget</extends> <extends>QWidget</extends>
@ -461,32 +552,23 @@ Use &quot;%1&quot; for the lower bound of the classification, and &quot;%2&quot;
<header>qgscolorrampcombobox.h</header> <header>qgscolorrampcombobox.h</header>
</customwidget> </customwidget>
<customwidget> <customwidget>
<class>QgsUnitSelectionWidget</class> <class>QgsGraduatedHistogramWidget</class>
<extends>QWidget</extends> <extends>QWidget</extends>
<header>qgsunitselectionwidget.h</header> <header>qgsgraduatedhistogramwidget.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<tabstops> <tabstops>
<tabstop>mExpressionWidget</tabstop> <tabstop>mExpressionWidget</tabstop>
<tabstop>cboGraduatedMode</tabstop>
<tabstop>btnChangeGraduatedSymbol</tabstop> <tabstop>btnChangeGraduatedSymbol</tabstop>
<tabstop>spinGraduatedClasses</tabstop>
<tabstop>txtLegendFormat</tabstop> <tabstop>txtLegendFormat</tabstop>
<tabstop>spinPrecision</tabstop> <tabstop>spinPrecision</tabstop>
<tabstop>cbxTrimTrailingZeroes</tabstop>
<tabstop>methodComboBox</tabstop> <tabstop>methodComboBox</tabstop>
<tabstop>cboGraduatedColorRamp</tabstop> <tabstop>cboGraduatedColorRamp</tabstop>
<tabstop>cbxInvertedColorRamp</tabstop> <tabstop>cbxInvertedColorRamp</tabstop>
<tabstop>minSizeSpinBox</tabstop> <tabstop>minSizeSpinBox</tabstop>
<tabstop>maxSizeSpinBox</tabstop> <tabstop>maxSizeSpinBox</tabstop>
<tabstop>mSizeUnitWidget</tabstop> <tabstop>mSizeUnitWidget</tabstop>
<tabstop>viewGraduated</tabstop>
<tabstop>btnGraduatedClassify</tabstop>
<tabstop>btnGraduatedAdd</tabstop>
<tabstop>btnGraduatedDelete</tabstop>
<tabstop>btnDeleteAllClasses</tabstop>
<tabstop>cbxLinkBoundaries</tabstop>
<tabstop>btnAdvanced</tabstop> <tabstop>btnAdvanced</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>

View File

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsHistogramWidgetBase</class>
<widget class="QWidget" name="QgsHistogramWidgetBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>694</width>
<height>128</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>60</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0">
<item>
<widget class="QwtPlot" name="mpPlot">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>Histogram bins</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="mBinsSpinBox">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="value">
<number>50</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mMeanCheckBox">
<property name="text">
<string>Show mean value</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mStdevCheckBox">
<property name="text">
<string>Show standard deviation</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QwtPlot</class>
<extends>QFrame</extends>
<header>qwt_plot.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>