Skip to content

Commit 1e46196

Browse files
vmoranyalldawson
authored andcommittedMay 20, 2015
[FEATURE] add expressions at the symbollist level
Size and Rotation can be defined by an expression for all symbols composing a marker. Width can be defined by an expression for all symbols composing a line. For markers, a legend is generated for varying sizes. This allows multivariate analysis legend in the case of classified/graduated colors. The offset is now set along with size to maintain the relative position of symbols composing a marker. An asistant, with preview, is accessible through the data defined button to help the user define the size expression. Three methods are available: Frannery, Area and Radius. Added a widget for use in categorized/classified symbology gui to set the expression if needed. The assistant is also available from it.
1 parent c38ff51 commit 1e46196

25 files changed

+1474
-74
lines changed
 

‎python/gui/gui.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@
172172
%Include symbology-ng/characterwidget.sip
173173
%Include symbology-ng/qgsdashspacedialog.sip
174174
%Include symbology-ng/qgsdatadefinedsymboldialog.sip
175+
%Include symbology-ng/qgssizescalewidget.sip
175176
%Include symbology-ng/qgsstylev2exportimportdialog.sip
176177
%Include symbology-ng/qgssvgselectorwidget.sip
177178
%Include symbology-ng/qgsgraduatedhistogramwidget.sip

‎python/gui/qgsdatadefinedbutton.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class QgsDataDefinedAssistant: QDialog
1111
#include <qgsdatadefinedbutton.h>
1212
%End
1313
public:
14-
virtual QgsDataDefined* dataDefined() const = 0 /Factory/;
14+
virtual QgsDataDefined dataDefined() const = 0;
1515
};
1616

1717
/** \ingroup gui
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class QgsSizeScaleWidget : QgsDataDefinedAssistant
2+
{
3+
%TypeHeaderCode
4+
#include <qgssizescalewidget.h>
5+
%End
6+
public:
7+
QgsSizeScaleWidget( const QgsVectorLayer * layer, const QgsMarkerSymbolV2 * symbol );
8+
9+
QgsDataDefined dataDefined() const;
10+
};
11+

‎python/gui/symbology-ng/qgssymbolslistwidget.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class QgsSymbolsListWidget : QWidget
44
#include <qgssymbolslistwidget.h>
55
%End
66
public:
7-
QgsSymbolsListWidget( QgsSymbolV2* symbol, QgsStyleV2* style, QMenu* menu, QWidget* parent /TransferThis/ = 0 );
7+
QgsSymbolsListWidget( QgsSymbolV2* symbol, QgsStyleV2* style, QMenu* menu, QWidget* parent /TransferThis/ = 0, const QgsVectorLayer * layer = 0 );
88

99
public slots:
1010
void setSymbolFromStyle( const QModelIndex & index );

‎src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "qgspointdisplacementrenderer.h"
2323
#include "qgsinvertedpolygonrenderer.h"
2424
#include "qgspainteffect.h"
25+
#include "qgsscaleexpression.h"
26+
#include "qgsdatadefined.h"
2527

2628
#include "qgsfeature.h"
2729
#include "qgsvectorlayer.h"
@@ -695,6 +697,56 @@ QgsLegendSymbolList QgsCategorizedSymbolRendererV2::legendSymbolItems( double sc
695697
return lst;
696698
}
697699

700+
QgsLegendSymbolListV2 QgsCategorizedSymbolRendererV2::legendSymbolItemsV2() const
701+
{
702+
QgsLegendSymbolListV2 lst;
703+
if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
704+
{
705+
// check that all symbols that have the same size expression
706+
QgsDataDefined ddSize;
707+
foreach ( QgsRendererCategoryV2 category, mCategories )
708+
{
709+
const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( category.symbol() );
710+
if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
711+
{
712+
// no common size expression
713+
return QgsFeatureRendererV2::legendSymbolItemsV2();
714+
}
715+
else
716+
{
717+
ddSize = symbol->dataDefinedSize();
718+
}
719+
}
720+
721+
if ( !ddSize.isActive() || !ddSize.useExpression() )
722+
{
723+
return QgsFeatureRendererV2::legendSymbolItemsV2();
724+
}
725+
726+
QgsScaleExpression exp( ddSize.expressionString() );
727+
if ( exp.type() != QgsScaleExpression::Unknown )
728+
{
729+
QgsLegendSymbolItemV2 title( NULL, exp.baseExpression(), "" );
730+
lst << title;
731+
foreach ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
732+
{
733+
QgsLegendSymbolItemV2 si( mSourceSymbol.data(), QString::number( v ), "" );
734+
QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
735+
s->setColor( QColor( 0, 0, 0 ) );
736+
s->setDataDefinedSize( QgsDataDefined() );
737+
s->setSize( exp.size( v ) );
738+
lst << si;
739+
}
740+
// now list the categorized symbols
741+
const QgsLegendSymbolListV2 list2 = QgsFeatureRendererV2::legendSymbolItemsV2() ;
742+
foreach ( QgsLegendSymbolItemV2 item, list2 )
743+
lst << item;
744+
return lst;
745+
}
746+
}
747+
748+
return QgsFeatureRendererV2::legendSymbolItemsV2();
749+
}
698750

699751
QgsSymbolV2* QgsCategorizedSymbolRendererV2::sourceSymbol()
700752
{

‎src/core/symbology-ng/qgscategorizedsymbolrendererv2.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ class CORE_EXPORT QgsCategorizedSymbolRendererV2 : public QgsFeatureRendererV2
139139
//! @note not available in python bindings
140140
virtual QgsLegendSymbolList legendSymbolItems( double scaleDenominator = -1, QString rule = QString() ) override;
141141

142+
//! @note added in 2.10
143+
QgsLegendSymbolListV2 legendSymbolItemsV2() const override;
144+
142145
QgsSymbolV2* sourceSymbol();
143146
void setSourceSymbol( QgsSymbolV2* sym );
144147

‎src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include "qgspointdisplacementrenderer.h"
2222
#include "qgsinvertedpolygonrenderer.h"
2323
#include "qgspainteffect.h"
24+
#include "qgsscaleexpression.h"
25+
#include "qgsdatadefined.h"
2426

2527
#include "qgsfeature.h"
2628
#include "qgsvectorlayer.h"
@@ -1151,6 +1153,57 @@ QgsLegendSymbologyList QgsGraduatedSymbolRendererV2::legendSymbologyItems( QSize
11511153
return lst;
11521154
}
11531155

1156+
QgsLegendSymbolListV2 QgsGraduatedSymbolRendererV2::legendSymbolItemsV2() const
1157+
{
1158+
QgsLegendSymbolListV2 list;
1159+
if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
1160+
{
1161+
// check that all symbols that have the same size expression
1162+
QgsDataDefined ddSize;
1163+
foreach ( QgsRendererRangeV2 range, mRanges )
1164+
{
1165+
const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( range.symbol() );
1166+
if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
1167+
{
1168+
// no common size expression
1169+
return QgsFeatureRendererV2::legendSymbolItemsV2();
1170+
}
1171+
else
1172+
{
1173+
ddSize = symbol->dataDefinedSize();
1174+
}
1175+
}
1176+
1177+
if ( !ddSize.isActive() || !ddSize.useExpression() )
1178+
{
1179+
return QgsFeatureRendererV2::legendSymbolItemsV2();
1180+
}
1181+
1182+
QgsScaleExpression exp( ddSize.expressionString() );
1183+
if ( exp.type() != QgsScaleExpression::Unknown )
1184+
{
1185+
QgsLegendSymbolItemV2 title( NULL, exp.baseExpression(), "" );
1186+
list << title;
1187+
foreach ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
1188+
{
1189+
QgsLegendSymbolItemV2 si( mSourceSymbol.data(), QString::number( v ), "" );
1190+
QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
1191+
s->setColor( QColor( 0, 0, 0 ) );
1192+
s->setDataDefinedSize( QgsDataDefined() );
1193+
s->setSize( exp.size( v ) );
1194+
list << si;
1195+
}
1196+
// now list the graduated symbols
1197+
const QgsLegendSymbolListV2 list2 = QgsFeatureRendererV2::legendSymbolItemsV2() ;
1198+
foreach ( QgsLegendSymbolItemV2 item, list2 )
1199+
list << item;
1200+
return list;
1201+
}
1202+
}
1203+
1204+
return QgsFeatureRendererV2::legendSymbolItemsV2();
1205+
}
1206+
11541207
QgsLegendSymbolList QgsGraduatedSymbolRendererV2::legendSymbolItems( double scaleDenominator, QString rule )
11551208
{
11561209
Q_UNUSED( scaleDenominator );

‎src/core/symbology-ng/qgsgraduatedsymbolrendererv2.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
239239
//! @note not available in python bindings
240240
virtual QgsLegendSymbolList legendSymbolItems( double scaleDenominator = -1, QString rule = QString() ) override;
241241

242+
//! @note added in 2.10
243+
QgsLegendSymbolListV2 legendSymbolItemsV2() const override;
244+
245+
242246
QgsSymbolV2* sourceSymbol();
243247
void setSourceSymbol( QgsSymbolV2* sym );
244248

‎src/core/symbology-ng/qgssinglesymbolrendererv2.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include "qgspointdisplacementrenderer.h"
2727
#include "qgsinvertedpolygonrenderer.h"
2828
#include "qgspainteffect.h"
29+
#include "qgsscaleexpression.h"
30+
#include "qgsdatadefined.h"
2931

3032
#include <QDomDocument>
3133
#include <QDomElement>
@@ -384,6 +386,30 @@ QgsLegendSymbolList QgsSingleSymbolRendererV2::legendSymbolItems( double scaleDe
384386
QgsLegendSymbolListV2 QgsSingleSymbolRendererV2::legendSymbolItemsV2() const
385387
{
386388
QgsLegendSymbolListV2 lst;
389+
if ( mSymbol->type() == QgsSymbolV2::Marker )
390+
{
391+
const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( mSymbol.data() );
392+
QgsDataDefined sizeDD = symbol->dataDefinedSize();
393+
if ( sizeDD.isActive() && sizeDD.useExpression() )
394+
{
395+
QgsScaleExpression scaleExp( sizeDD.expressionString() );
396+
if ( scaleExp.type() != QgsScaleExpression::Unknown )
397+
{
398+
QgsLegendSymbolItemV2 title( NULL, scaleExp.baseExpression(), 0 );
399+
lst << title;
400+
foreach ( double v, QgsSymbolLayerV2Utils::prettyBreaks( scaleExp.minValue(), scaleExp.maxValue(), 4 ) )
401+
{
402+
QgsLegendSymbolItemV2 si( mSymbol.data(), QString::number( v ), 0 );
403+
QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
404+
s->setDataDefinedSize( 0 );
405+
s->setSize( scaleExp.size( v ) );
406+
lst << si;
407+
}
408+
return lst;
409+
}
410+
}
411+
}
412+
387413
lst << QgsLegendSymbolItemV2( mSymbol.data(), QString(), 0 );
388414
return lst;
389415
}

‎src/core/symbology-ng/qgssymbollayerv2.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#endif
2222

2323
#define DEG2RAD(x) ((x)*M_PI/180)
24-
#define DEFAULT_SCALE_METHOD QgsSymbolV2::ScaleArea
24+
#define DEFAULT_SCALE_METHOD QgsSymbolV2::ScaleDiameter
2525

2626
#include <QColor>
2727
#include <QMap>
@@ -330,7 +330,7 @@ class CORE_EXPORT QgsMarkerSymbolLayerV2 : public QgsSymbolLayerV2
330330
QgsSymbolV2::ScaleMethod scaleMethod() const { return mScaleMethod; }
331331

332332
void setOffset( QPointF offset ) { mOffset = offset; }
333-
QPointF offset() { return mOffset; }
333+
QPointF offset() const { return mOffset; }
334334

335335
virtual void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const override;
336336

‎src/core/symbology-ng/qgssymbolv2.cpp

Lines changed: 288 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,51 @@
2828
#include "qgspainteffect.h"
2929
#include "qgseffectstack.h"
3030

31+
#include "qgsdatadefined.h"
32+
3133
#include <QColor>
3234
#include <QImage>
3335
#include <QPainter>
3436
#include <QSize>
3537

3638
#include <cmath>
3739

40+
inline
41+
QgsDataDefined* rotateWholeSymbol( double additionalRotation, const QgsDataDefined& dd )
42+
{
43+
QgsDataDefined* rotatedDD = new QgsDataDefined( dd );
44+
rotatedDD->setUseExpression( true );
45+
QString exprString = dd.useExpression() ? dd.expressionString() : dd.field();
46+
rotatedDD->setExpressionString( QString::number( additionalRotation ) + " + (" + exprString + ")" );
47+
return rotatedDD;
48+
}
49+
50+
inline
51+
QgsDataDefined* scaleWholeSymbol( double scaleFactor, const QgsDataDefined& dd )
52+
{
53+
QgsDataDefined* scaledDD = new QgsDataDefined( dd );
54+
scaledDD->setUseExpression( true );
55+
QString exprString = dd.useExpression() ? dd.expressionString() : dd.field();
56+
scaledDD->setExpressionString( QString::number( scaleFactor ) + "*(" + exprString + ")" );
57+
return scaledDD;
58+
}
59+
60+
inline
61+
QgsDataDefined* scaleWholeSymbol( double scaleFactorX, double scaleFactorY, const QgsDataDefined& dd )
62+
{
63+
QgsDataDefined* scaledDD = new QgsDataDefined( dd );
64+
scaledDD->setUseExpression( true );
65+
QString exprString = dd.useExpression() ? dd.expressionString() : dd.field();
66+
scaledDD->setExpressionString(
67+
( scaleFactorX ? "tostring(" + QString::number( scaleFactorX ) + "*(" + exprString + "))" : "'0'" ) +
68+
"|| ',' || " +
69+
( scaleFactorY ? "tostring(" + QString::number( scaleFactorY ) + "*(" + exprString + "))" : "'0'" ));
70+
return scaledDD;
71+
}
72+
73+
74+
////////////////////
75+
3876
QgsSymbolV2::QgsSymbolV2( SymbolType type, QgsSymbolLayerV2List layers )
3977
: mType( type )
4078
, mLayers( layers )
@@ -500,7 +538,6 @@ QgsFillSymbolV2* QgsFillSymbolV2::createSimple( const QgsStringMap& properties )
500538

501539
///////////////////
502540

503-
504541
QgsMarkerSymbolV2::QgsMarkerSymbolV2( QgsSymbolLayerV2List layers )
505542
: QgsSymbolV2( Marker, layers )
506543
{
@@ -519,8 +556,7 @@ void QgsMarkerSymbolV2::setAngle( double ang )
519556
}
520557
}
521558

522-
523-
double QgsMarkerSymbolV2::angle()
559+
double QgsMarkerSymbolV2::angle() const
524560
{
525561
QgsSymbolLayerV2List::const_iterator it = mLayers.begin();
526562

@@ -541,6 +577,74 @@ void QgsMarkerSymbolV2::setLineAngle( double lineAng )
541577
}
542578
}
543579

580+
void QgsMarkerSymbolV2::setDataDefinedAngle( const QgsDataDefined& dd )
581+
{
582+
const double symbolRotation = angle();
583+
584+
for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
585+
{
586+
QgsMarkerSymbolLayerV2* layer = static_cast<QgsMarkerSymbolLayerV2 *>( *it );
587+
if ( dd.hasDefaultValues() )
588+
{
589+
layer->removeDataDefinedProperty( "angle" );
590+
}
591+
else
592+
{
593+
if ( qgsDoubleNear( layer->angle(), symbolRotation ) )
594+
{
595+
layer->setDataDefinedProperty( "angle", new QgsDataDefined( dd ) );
596+
}
597+
else
598+
{
599+
QgsDataDefined* rotatedDD = rotateWholeSymbol( layer->angle() - symbolRotation, dd );
600+
layer->setDataDefinedProperty( "angle", rotatedDD );
601+
}
602+
}
603+
}
604+
}
605+
606+
QgsDataDefined QgsMarkerSymbolV2::dataDefinedAngle() const
607+
{
608+
const double symbolRotation = angle();
609+
QgsDataDefined* symbolDD = 0;
610+
611+
// find the base of the "en masse" pattern
612+
for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
613+
{
614+
const QgsMarkerSymbolLayerV2* layer = static_cast<const QgsMarkerSymbolLayerV2 *>( *it );
615+
if ( layer->angle() == symbolRotation && layer->getDataDefinedProperty( "angle" ) )
616+
{
617+
symbolDD = layer->getDataDefinedProperty( "angle" );
618+
break;
619+
}
620+
}
621+
622+
if ( !symbolDD )
623+
return QgsDataDefined();
624+
625+
// check that all layer's angle expressions match the "en masse" pattern
626+
for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
627+
{
628+
const QgsMarkerSymbolLayerV2* layer = static_cast<const QgsMarkerSymbolLayerV2 *>( *it );
629+
630+
QgsDataDefined* layerAngleDD = layer->getDataDefinedProperty( "angle" );
631+
632+
if ( qgsDoubleNear( layer->angle(), symbolRotation ) )
633+
{
634+
if ( *layerAngleDD != *symbolDD )
635+
return QgsDataDefined();
636+
}
637+
else
638+
{
639+
QScopedPointer< QgsDataDefined > rotatedDD( rotateWholeSymbol( layer->angle() - symbolRotation, *symbolDD ) );
640+
if ( *layerAngleDD != *( rotatedDD.data() ) )
641+
return QgsDataDefined();
642+
}
643+
}
644+
return QgsDataDefined( *symbolDD );
645+
}
646+
647+
544648
void QgsMarkerSymbolV2::setSize( double s )
545649
{
546650
double origSize = size();
@@ -550,16 +654,19 @@ void QgsMarkerSymbolV2::setSize( double s )
550654
QgsMarkerSymbolLayerV2* layer = static_cast<QgsMarkerSymbolLayerV2*>( *it );
551655
if ( layer->size() == origSize )
552656
layer->setSize( s );
553-
else
657+
else if ( origSize != 0 )
554658
{
555659
// proportionally scale size
556-
if ( origSize != 0 )
557-
layer->setSize( layer->size() * s / origSize );
660+
layer->setSize( layer->size() * s / origSize );
558661
}
662+
// also scale offset to maintain relative position
663+
if ( origSize != 0 && ( layer->offset().x() || layer->offset().y() ) )
664+
layer->setOffset( QPointF( layer->offset().x() * s / origSize,
665+
layer->offset().y() * s / origSize ) );
559666
}
560667
}
561668

562-
double QgsMarkerSymbolV2::size()
669+
double QgsMarkerSymbolV2::size() const
563670
{
564671
// return size of the largest symbol
565672
double maxSize = 0;
@@ -573,6 +680,91 @@ double QgsMarkerSymbolV2::size()
573680
return maxSize;
574681
}
575682

683+
void QgsMarkerSymbolV2::setDataDefinedSize( const QgsDataDefined &dd )
684+
{
685+
const double symbolSize = size();
686+
687+
for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
688+
{
689+
QgsMarkerSymbolLayerV2* layer = static_cast<QgsMarkerSymbolLayerV2 *>( *it );
690+
691+
if ( dd.hasDefaultValues() )
692+
{
693+
layer->removeDataDefinedProperty( "size" );
694+
layer->removeDataDefinedProperty( "offset" );
695+
}
696+
else
697+
{
698+
if ( symbolSize == 0 || qgsDoubleNear( layer->size(), symbolSize ) )
699+
{
700+
layer->setDataDefinedProperty( "size", new QgsDataDefined( dd ) );
701+
}
702+
else
703+
{
704+
layer->setDataDefinedProperty( "size", scaleWholeSymbol( layer->size() / symbolSize, dd ) );
705+
}
706+
707+
if ( layer->offset().x() || layer->offset().y() )
708+
{
709+
layer->setDataDefinedProperty( "offset", scaleWholeSymbol(
710+
layer->offset().x() / symbolSize,
711+
layer->offset().y() / symbolSize, dd ) );
712+
}
713+
}
714+
}
715+
}
716+
717+
QgsDataDefined QgsMarkerSymbolV2::dataDefinedSize() const
718+
{
719+
const double symbolSize = size();
720+
721+
QgsDataDefined* symbolDD = 0;
722+
723+
// find the base of the "en masse" pattern
724+
for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
725+
{
726+
const QgsMarkerSymbolLayerV2* layer = static_cast<const QgsMarkerSymbolLayerV2 *>( *it );
727+
if ( layer->size() == symbolSize && layer->getDataDefinedProperty( "size" ) )
728+
{
729+
symbolDD = layer->getDataDefinedProperty( "size" );
730+
break;
731+
}
732+
}
733+
734+
if ( !symbolDD )
735+
return QgsDataDefined();
736+
737+
// check that all layers size expressions match the "en masse" pattern
738+
for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
739+
{
740+
const QgsMarkerSymbolLayerV2* layer = static_cast<const QgsMarkerSymbolLayerV2 *>( *it );
741+
742+
QgsDataDefined* layerSizeDD = layer->getDataDefinedProperty( "size" );
743+
QgsDataDefined* layerOffsetDD = layer->getDataDefinedProperty( "offset" );
744+
745+
if ( qgsDoubleNear( layer->size(), symbolSize ) )
746+
{
747+
if ( !layerSizeDD || *layerSizeDD != *symbolDD )
748+
return QgsDataDefined();
749+
}
750+
else
751+
{
752+
if ( symbolSize == 0 )
753+
return QgsDataDefined();
754+
755+
QScopedPointer< QgsDataDefined > scaledDD( scaleWholeSymbol( layer->size() / symbolSize, *symbolDD ) );
756+
if ( !layerSizeDD || *layerSizeDD != *( scaledDD.data() ) )
757+
return QgsDataDefined();
758+
}
759+
760+
761+
QScopedPointer< QgsDataDefined > scaledOffsetDD( scaleWholeSymbol( layer->offset().x() / symbolSize, layer->offset().y() / symbolSize, *symbolDD ) );
762+
if ( layerOffsetDD && *layerOffsetDD != *( scaledOffsetDD.data() ) )
763+
return QgsDataDefined();
764+
}
765+
766+
return QgsDataDefined( *symbolDD );
767+
}
576768

577769
void QgsMarkerSymbolV2::setScaleMethod( QgsSymbolV2::ScaleMethod scaleMethod )
578770
{
@@ -668,16 +860,18 @@ void QgsLineSymbolV2::setWidth( double w )
668860
{
669861
layer->setWidth( w );
670862
}
671-
else
863+
else if ( origWidth != 0 )
672864
{
673865
// proportionally scale the width
674-
if ( origWidth != 0 )
675-
layer->setWidth( layer->width() * w / origWidth );
866+
layer->setWidth( layer->width() * w / origWidth );
676867
}
868+
// also scale offset to maintain relative position
869+
if ( origWidth != 0 && layer->offset() )
870+
layer->setOffset( layer->offset() * w / origWidth );
677871
}
678872
}
679873

680-
double QgsLineSymbolV2::width()
874+
double QgsLineSymbolV2::width() const
681875
{
682876
double maxWidth = 0;
683877
for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
@@ -690,6 +884,89 @@ double QgsLineSymbolV2::width()
690884
return maxWidth;
691885
}
692886

887+
void QgsLineSymbolV2::setDataDefinedWidth( const QgsDataDefined& dd )
888+
{
889+
const double symbolWidth = width();
890+
891+
for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
892+
{
893+
QgsLineSymbolLayerV2* layer = static_cast<QgsLineSymbolLayerV2*>( *it );
894+
895+
if ( dd.hasDefaultValues() )
896+
{
897+
layer->removeDataDefinedProperty( "width" );
898+
layer->removeDataDefinedProperty( "offset" );
899+
}
900+
else
901+
{
902+
if ( symbolWidth == 0 || qgsDoubleNear( layer->width(), symbolWidth ) )
903+
{
904+
layer->setDataDefinedProperty( "width", new QgsDataDefined( dd ) );
905+
}
906+
else
907+
{
908+
layer->setDataDefinedProperty( "width", scaleWholeSymbol( layer->width() / symbolWidth, dd ) );
909+
}
910+
911+
if ( layer->offset() )
912+
{
913+
layer->setDataDefinedProperty( "offset", scaleWholeSymbol( layer->offset() / symbolWidth, dd ) );
914+
}
915+
}
916+
}
917+
}
918+
919+
QgsDataDefined QgsLineSymbolV2::dataDefinedWidth() const
920+
{
921+
const double symbolWidth = width();
922+
923+
QgsDataDefined* symbolDD = 0;
924+
925+
// find the base of the "en masse" pattern
926+
for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
927+
{
928+
const QgsLineSymbolLayerV2* layer = static_cast<const QgsLineSymbolLayerV2*>( *it );
929+
if ( layer->width() == symbolWidth && layer->getDataDefinedProperty( "width" ) )
930+
{
931+
symbolDD = layer->getDataDefinedProperty( "width" );
932+
break;
933+
}
934+
}
935+
936+
if ( !symbolDD )
937+
return QgsDataDefined();
938+
939+
// check that all layers width expressions match the "en masse" pattern
940+
for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
941+
{
942+
const QgsLineSymbolLayerV2* layer = static_cast<const QgsLineSymbolLayerV2*>( *it );
943+
944+
QgsDataDefined* layerWidthDD = layer->getDataDefinedProperty( "width" );
945+
QgsDataDefined* layerOffsetDD = layer->getDataDefinedProperty( "offset" );
946+
947+
if ( qgsDoubleNear( layer->width(), symbolWidth ) )
948+
{
949+
if ( !layerWidthDD || *layerWidthDD != *symbolDD )
950+
return QgsDataDefined();
951+
}
952+
else
953+
{
954+
if ( symbolWidth == 0 )
955+
return QgsDataDefined();
956+
957+
QScopedPointer< QgsDataDefined > scaledDD( scaleWholeSymbol( layer->width() / symbolWidth, *symbolDD ) );
958+
if ( !layerWidthDD || *layerWidthDD != *( scaledDD.data() ) )
959+
return QgsDataDefined();
960+
}
961+
962+
QScopedPointer< QgsDataDefined > scaledOffsetDD( scaleWholeSymbol( layer->offset() / symbolWidth, *symbolDD ) );
963+
if ( layerOffsetDD && *layerOffsetDD != *( scaledOffsetDD.data() ) )
964+
return QgsDataDefined();
965+
}
966+
967+
return QgsDataDefined( *symbolDD );
968+
}
969+
693970
void QgsLineSymbolV2::renderPolyline( const QPolygonF& points, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected )
694971
{
695972
//save old painter

‎src/core/symbology-ng/qgssymbolv2.h

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class QgsPaintEffect;
4141
class QgsMarkerSymbolLayerV2;
4242
class QgsLineSymbolLayerV2;
4343
class QgsFillSymbolLayerV2;
44+
class QgsDataDefined;
4445

4546
typedef QList<QgsSymbolLayerV2*> QgsSymbolLayerV2List;
4647

@@ -183,6 +184,7 @@ class CORE_EXPORT QgsSymbolV2
183184

184185
QSet<QString> usedAttributes() const;
185186

187+
//! @note the layer will be NULL after stopRender
186188
void setLayer( const QgsVectorLayer* layer ) { mLayer = layer; }
187189
const QgsVectorLayer* layer() const { return mLayer; }
188190

@@ -280,7 +282,22 @@ class CORE_EXPORT QgsMarkerSymbolV2 : public QgsSymbolV2
280282
QgsMarkerSymbolV2( QgsSymbolLayerV2List layers = QgsSymbolLayerV2List() );
281283

282284
void setAngle( double angle );
283-
double angle();
285+
double angle() const;
286+
287+
/** Set data defined angle for whole symbol (including all symbol layers).
288+
* @param dd data defined angle
289+
* @note added in QGIS 2.9
290+
* @see dataDefinedAngle
291+
*/
292+
void setDataDefinedAngle( const QgsDataDefined& dd );
293+
294+
/** Returns data defined angle for whole symbol (including all symbol layers).
295+
* @returns data defined angle, or empty data defined if angle is not set
296+
* at the marker level
297+
* @note added in QGIS 2.9
298+
* @see setDataDefinedAngle
299+
*/
300+
QgsDataDefined dataDefinedAngle() const;
284301

285302
/** Sets the line angle modification for the symbol's angle. This angle is added to
286303
* the marker's rotation and data defined rotation before rendering the symbol, and
@@ -291,7 +308,22 @@ class CORE_EXPORT QgsMarkerSymbolV2 : public QgsSymbolV2
291308
void setLineAngle( double lineAngle );
292309

293310
void setSize( double size );
294-
double size();
311+
double size() const;
312+
313+
/** Set data defined size for whole symbol (including all symbol layers).
314+
* @param dd data defined size
315+
* @note added in QGIS 2.9
316+
* @see dataDefinedSize
317+
*/
318+
void setDataDefinedSize( const QgsDataDefined& dd );
319+
320+
/** Returns data defined size for whole symbol (including all symbol layers).
321+
* @returns data defined size, or empty data defined if size is not set
322+
* at the marker level
323+
* @note added in QGIS 2.9
324+
* @see setDataDefinedSize
325+
*/
326+
QgsDataDefined dataDefinedSize() const;
295327

296328
void setScaleMethod( QgsSymbolV2::ScaleMethod scaleMethod );
297329
ScaleMethod scaleMethod();
@@ -319,7 +351,22 @@ class CORE_EXPORT QgsLineSymbolV2 : public QgsSymbolV2
319351
QgsLineSymbolV2( QgsSymbolLayerV2List layers = QgsSymbolLayerV2List() );
320352

321353
void setWidth( double width );
322-
double width();
354+
double width() const;
355+
356+
/** Set data defined width for whole symbol (including all symbol layers).
357+
* @param dd data defined width
358+
* @note added in QGIS 2.9
359+
* @see dataDefinedWidth
360+
*/
361+
void setDataDefinedWidth( const QgsDataDefined& dd );
362+
363+
/** Returns data defined size for whole symbol (including all symbol layers).
364+
* @returns data defined size, or empty data defined if size is not set
365+
* at the line level
366+
* @note added in QGIS 2.9
367+
* @see setDataDefinedWidth
368+
*/
369+
QgsDataDefined dataDefinedWidth() const;
323370

324371
void renderPolyline( const QPolygonF& points, const QgsFeature* f, QgsRenderContext& context, int layer = -1, bool selected = false );
325372

‎src/gui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ symbology-ng/qgssymbolslistwidget.cpp
4141
symbology-ng/qgssvgselectorwidget.cpp
4242
symbology-ng/qgslayerpropertieswidget.cpp
4343
symbology-ng/qgssmartgroupeditordialog.cpp
44+
symbology-ng/qgssizescalewidget.cpp
4445

4546
effects/qgseffectdrawmodecombobox.cpp
4647
effects/qgspainteffectpropertieswidget.cpp
@@ -378,6 +379,7 @@ SET(QGIS_GUI_MOC_HDRS
378379
symbology-ng/qgsvectorfieldsymbollayerwidget.h
379380
symbology-ng/qgsvectorgradientcolorrampv2dialog.h
380381
symbology-ng/qgsvectorrandomcolorrampv2dialog.h
382+
symbology-ng/qgssizescalewidget.h
381383

382384
attributetable/qgsattributetabledelegate.h
383385
attributetable/qgsattributetablefiltermodel.h

‎src/gui/qgsdatadefinedbutton.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -434,13 +434,13 @@ void QgsDataDefinedButton::showAssistant()
434434

435435
if ( mAssistant->exec() == QDialog::Accepted )
436436
{
437-
QScopedPointer<QgsDataDefined> dd( mAssistant->dataDefined() );
438-
setUseExpression( dd->useExpression() );
439-
setActive( dd->isActive() );
440-
if ( dd->isActive() && dd->useExpression() )
441-
setExpression( dd->expressionString() );
442-
else if ( dd->isActive() )
443-
setField( dd->field() );
437+
QgsDataDefined dd = mAssistant->dataDefined();
438+
setUseExpression( dd.useExpression() );
439+
setActive( dd.isActive() );
440+
if ( dd.isActive() && dd.useExpression() )
441+
setExpression( dd.expressionString() );
442+
else if ( dd.isActive() )
443+
setField( dd.field() );
444444
updateGui();
445445
}
446446
activateWindow(); // reset focus to parent window

‎src/gui/qgsdatadefinedbutton.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class QgsDataDefined;
3535
class GUI_EXPORT QgsDataDefinedAssistant: public QDialog
3636
{
3737
public:
38-
virtual QgsDataDefined* dataDefined() const = 0;
38+
virtual QgsDataDefined dataDefined() const = 0;
3939
};
4040

4141
/** \ingroup gui

‎src/gui/symbology-ng/qgsrendererv2widget.cpp

Lines changed: 123 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ QgsRendererV2Widget::QgsRendererV2Widget( QgsVectorLayer* layer, QgsStyleV2* sty
4545
else if ( mLayer && mLayer->geometryType() == QGis::Point )
4646
{
4747
contextMenu->addAction( tr( "Change size" ), this, SLOT( changeSymbolSize() ) );
48+
contextMenu->addAction( tr( "Change angle" ), this, SLOT( changeSymbolAngle() ) );
4849
}
4950
}
5051

@@ -127,17 +128,17 @@ void QgsRendererV2Widget::changeSymbolWidth()
127128
return;
128129
}
129130

130-
bool ok;
131-
QgsLineSymbolV2* line = dynamic_cast<QgsLineSymbolV2*>( symbolList.at( 0 ) ) ;
132-
double width = QInputDialog::getDouble( this, tr( "Width" ), tr( "Change symbol width" ), line ? line->width() : 0.0 , 0.0, 999999, 1, &ok );
133-
if ( ok )
131+
QgsDataDefinedWidthDialog dlg( symbolList, mLayer );
132+
133+
if ( QMessageBox::Ok == dlg.exec() )
134134
{
135-
QList<QgsSymbolV2*>::iterator symbolIt = symbolList.begin();
136-
for ( ; symbolIt != symbolList.end(); ++symbolIt )
135+
if ( !dlg.mDDBtn->isActive() )
137136
{
138-
line = dynamic_cast<QgsLineSymbolV2*>( *symbolIt );
139-
if ( line )
140-
line->setWidth( width );
137+
QList<QgsSymbolV2*>::iterator symbolIt = symbolList.begin();
138+
for ( ; symbolIt != symbolList.end(); ++symbolIt )
139+
{
140+
dynamic_cast<QgsLineSymbolV2*>( *symbolIt )->setWidth( dlg.mSpinBox->value() );
141+
}
141142
}
142143
refreshSymbolView();
143144
}
@@ -151,18 +152,41 @@ void QgsRendererV2Widget::changeSymbolSize()
151152
return;
152153
}
153154

154-
bool ok;
155-
QgsMarkerSymbolV2* marker = dynamic_cast<QgsMarkerSymbolV2*>( symbolList.at( 0 ) );
155+
QgsDataDefinedSizeDialog dlg( symbolList, mLayer );
156156

157-
double size = QInputDialog::getDouble( this, tr( "Size" ), tr( "Change symbol size" ), marker ? marker->size() : 0.0 , 0.0, 999999, 1, &ok );
158-
if ( ok )
157+
if ( QMessageBox::Ok == dlg.exec() )
159158
{
160-
QList<QgsSymbolV2*>::iterator symbolIt = symbolList.begin();
161-
for ( ; symbolIt != symbolList.end(); ++symbolIt )
159+
if ( !dlg.mDDBtn->isActive() )
160+
{
161+
QList<QgsSymbolV2*>::iterator symbolIt = symbolList.begin();
162+
for ( ; symbolIt != symbolList.end(); ++symbolIt )
163+
{
164+
dynamic_cast<QgsMarkerSymbolV2*>( *symbolIt )->setSize( dlg.mSpinBox->value() );
165+
}
166+
}
167+
refreshSymbolView();
168+
}
169+
}
170+
171+
void QgsRendererV2Widget::changeSymbolAngle()
172+
{
173+
QList<QgsSymbolV2*> symbolList = selectedSymbols();
174+
if ( symbolList.size() < 1 )
175+
{
176+
return;
177+
}
178+
179+
QgsDataDefinedRotationDialog dlg( symbolList, mLayer );
180+
181+
if ( QMessageBox::Ok == dlg.exec() )
182+
{
183+
if ( !dlg.mDDBtn->isActive() )
162184
{
163-
marker = dynamic_cast<QgsMarkerSymbolV2*>( *symbolIt );
164-
if ( marker )
165-
marker->setSize( size );
185+
QList<QgsSymbolV2*>::iterator symbolIt = symbolList.begin();
186+
for ( ; symbolIt != symbolList.end(); ++symbolIt )
187+
{
188+
dynamic_cast<QgsMarkerSymbolV2*>( *symbolIt )->setAngle( dlg.mSpinBox->value() );
189+
}
166190
}
167191
refreshSymbolView();
168192
}
@@ -355,3 +379,84 @@ void QgsRendererV2DataDefinedMenus::updateMenu( QActionGroup* actionGroup, QStri
355379
}
356380
}
357381
#endif
382+
383+
QgsDataDefinedValueDialog::QgsDataDefinedValueDialog( const QList<QgsSymbolV2*>& symbolList, QgsVectorLayer * layer, const QString & label )
384+
: mSymbolList( symbolList )
385+
, mLayer( layer )
386+
{
387+
setupUi( this );
388+
setWindowFlags( Qt::WindowStaysOnTopHint );
389+
mLabel->setText( label );
390+
connect( mDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( dataDefinedChanged() ) );
391+
connect( mDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( dataDefinedChanged() ) );
392+
393+
}
394+
395+
void QgsDataDefinedValueDialog::init( const QString & description )
396+
{
397+
QgsDataDefined dd = symbolDataDefined();
398+
mDDBtn->init( mLayer, &dd, QgsDataDefinedButton::Double, description );
399+
mSpinBox->setValue( value( mSymbolList.back() ) );
400+
mSpinBox->setEnabled( !mDDBtn->isActive() );
401+
}
402+
403+
QgsDataDefined QgsDataDefinedValueDialog::symbolDataDefined() const
404+
{
405+
// check that all symbols share the same size expression
406+
QgsDataDefined dd = symbolDataDefined( mSymbolList.back() );
407+
foreach ( QgsSymbolV2 * it, mSymbolList )
408+
{
409+
if ( symbolDataDefined( it ) != dd ) return QgsDataDefined();
410+
}
411+
return dd;
412+
}
413+
414+
void QgsDataDefinedValueDialog::dataDefinedChanged()
415+
{
416+
QgsDataDefined dd = mDDBtn->currentDataDefined();
417+
mSpinBox->setEnabled( !dd.isActive() );
418+
419+
if ( // shall we remove datadefined expressions for layers ?
420+
( symbolDataDefined().isActive() && !dd.isActive() )
421+
// shall we set the "en masse" expression for properties ?
422+
|| dd.isActive() )
423+
{
424+
foreach ( QgsSymbolV2 * it, mSymbolList )
425+
setDataDefined( it, dd );
426+
}
427+
}
428+
429+
QgsDataDefined QgsDataDefinedSizeDialog::symbolDataDefined( const QgsSymbolV2 *symbol ) const
430+
{
431+
const QgsMarkerSymbolV2* marker = static_cast<const QgsMarkerSymbolV2*>( symbol );
432+
return marker->dataDefinedSize();
433+
}
434+
435+
void QgsDataDefinedSizeDialog::setDataDefined( QgsSymbolV2* symbol, const QgsDataDefined& dd )
436+
{
437+
static_cast<QgsMarkerSymbolV2*>( symbol )->setDataDefinedSize( dd );
438+
}
439+
440+
441+
QgsDataDefined QgsDataDefinedRotationDialog::symbolDataDefined( const QgsSymbolV2 *symbol ) const
442+
{
443+
const QgsMarkerSymbolV2* marker = static_cast<const QgsMarkerSymbolV2*>( symbol );
444+
return marker->dataDefinedAngle();
445+
}
446+
447+
void QgsDataDefinedRotationDialog::setDataDefined( QgsSymbolV2 *symbol, const QgsDataDefined &dd )
448+
{
449+
static_cast<QgsMarkerSymbolV2*>( symbol )->setDataDefinedAngle( dd );
450+
}
451+
452+
453+
QgsDataDefined QgsDataDefinedWidthDialog::symbolDataDefined( const QgsSymbolV2 *symbol ) const
454+
{
455+
const QgsLineSymbolV2* line = static_cast<const QgsLineSymbolV2*>( symbol );
456+
return line->dataDefinedWidth();
457+
}
458+
459+
void QgsDataDefinedWidthDialog::setDataDefined( QgsSymbolV2 *symbol, const QgsDataDefined &dd )
460+
{
461+
static_cast<QgsLineSymbolV2*>( symbol )->setDataDefinedWidth( dd );
462+
}

‎src/gui/symbology-ng/qgsrendererv2widget.h

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <QWidget>
1919
#include <QMenu>
2020
#include "qgssymbolv2.h"
21+
#include "qgsdatadefined.h"
2122

2223
class QgsVectorLayer;
2324
class QgsStyleV2;
@@ -73,6 +74,8 @@ class GUI_EXPORT QgsRendererV2Widget : public QWidget
7374
void changeSymbolWidth();
7475
/**Change marker sizes of selected symbols*/
7576
void changeSymbolSize();
77+
/**Change marker angles of selected symbols*/
78+
void changeSymbolAngle();
7679

7780
virtual void copy() {}
7881
virtual void paste() {}
@@ -126,4 +129,98 @@ class QgsRendererV2DataDefinedMenus : public QObject
126129
QgsVectorLayer* mLayer;
127130
};
128131

132+
////////////
133+
134+
#include "ui_widget_set_dd_value.h"
135+
#include "qgssizescalewidget.h"
136+
137+
/**
138+
Utility classes for "en masse" size definition
139+
*/
140+
class GUI_EXPORT QgsDataDefinedValueDialog : public QDialog, public Ui::QgsDataDefinedValueDialog
141+
{
142+
Q_OBJECT
143+
144+
public:
145+
/** Constructor
146+
* @param symbolList must not be empty
147+
* @param layer must not be null
148+
*/
149+
QgsDataDefinedValueDialog( const QList<QgsSymbolV2*>& symbolList, QgsVectorLayer * layer, const QString & label );
150+
virtual ~QgsDataDefinedValueDialog() {}
151+
152+
public slots:
153+
void dataDefinedChanged();
154+
155+
protected:
156+
QgsDataDefined symbolDataDefined() const;
157+
void init( const QString & description ); // needed in children ctor to call virtual
158+
159+
virtual QgsDataDefined symbolDataDefined( const QgsSymbolV2 * ) const = 0;
160+
virtual double value( const QgsSymbolV2 * ) const = 0;
161+
virtual void setDataDefined( QgsSymbolV2* symbol, const QgsDataDefined& dd ) = 0;
162+
163+
QList<QgsSymbolV2*> mSymbolList;
164+
QgsVectorLayer* mLayer;
165+
};
166+
167+
class GUI_EXPORT QgsDataDefinedSizeDialog : public QgsDataDefinedValueDialog
168+
{
169+
Q_OBJECT
170+
public:
171+
QgsDataDefinedSizeDialog( const QList<QgsSymbolV2*>& symbolList, QgsVectorLayer * layer )
172+
: QgsDataDefinedValueDialog( symbolList, layer, tr( "Size" ) )
173+
{
174+
init( tr( "Symbol size" ) );
175+
if ( symbolList.length() )
176+
mDDBtn->setAssistant( new QgsSizeScaleWidget( mLayer, static_cast<const QgsMarkerSymbolV2*>( symbolList[0] ) ) );
177+
}
178+
179+
protected:
180+
QgsDataDefined symbolDataDefined( const QgsSymbolV2 * symbol ) const override;
181+
182+
double value( const QgsSymbolV2 * symbol ) const override { return static_cast<const QgsMarkerSymbolV2*>( symbol )->size(); }
183+
184+
void setDataDefined( QgsSymbolV2* symbol, const QgsDataDefined& dd ) override;
185+
};
186+
187+
class GUI_EXPORT QgsDataDefinedRotationDialog : public QgsDataDefinedValueDialog
188+
{
189+
Q_OBJECT
190+
public:
191+
QgsDataDefinedRotationDialog( const QList<QgsSymbolV2*>& symbolList, QgsVectorLayer * layer )
192+
: QgsDataDefinedValueDialog( symbolList, layer, tr( "Rotation" ) )
193+
{
194+
init( tr( "Symbol rotation" ) );
195+
}
196+
197+
protected:
198+
QgsDataDefined symbolDataDefined( const QgsSymbolV2 * symbol ) const override;
199+
200+
double value( const QgsSymbolV2 * symbol ) const override { return static_cast<const QgsMarkerSymbolV2*>( symbol )->angle(); }
201+
202+
void setDataDefined( QgsSymbolV2* symbol, const QgsDataDefined& dd ) override;
203+
};
204+
205+
206+
class GUI_EXPORT QgsDataDefinedWidthDialog : public QgsDataDefinedValueDialog
207+
{
208+
Q_OBJECT
209+
public:
210+
QgsDataDefinedWidthDialog( const QList<QgsSymbolV2*>& symbolList, QgsVectorLayer * layer )
211+
: QgsDataDefinedValueDialog( symbolList, layer, tr( "Width" ) )
212+
{
213+
init( tr( "Symbol width" ) );
214+
}
215+
216+
protected:
217+
QgsDataDefined symbolDataDefined( const QgsSymbolV2 * symbol ) const override;
218+
219+
double value( const QgsSymbolV2 * symbol ) const override { return static_cast<const QgsLineSymbolV2*>( symbol )->width(); }
220+
221+
void setDataDefined( QgsSymbolV2* symbol, const QgsDataDefined& dd ) override;
222+
};
223+
224+
225+
129226
#endif // QGSRENDERERV2WIDGET_H
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/***************************************************************************
2+
qgssizescalewidget.cpp - continuous size scale assistant
3+
4+
---------------------
5+
begin : March 2015
6+
copyright : (C) 2015 by Vincent Mora
7+
email : vincent dot mora at oslandia dot com
8+
***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
17+
#include "qgssizescalewidget.h"
18+
19+
#include "qgsvectorlayer.h"
20+
#include "qgsmaplayerregistry.h"
21+
#include "qgssymbolv2.h"
22+
#include "qgslayertreelayer.h"
23+
#include "qgslayertreemodellegendnode.h"
24+
#include "qgssymbollayerv2utils.h"
25+
#include "qgsscaleexpression.h"
26+
#include "qgsdatadefined.h"
27+
28+
#include <QMenu>
29+
#include <QAction>
30+
#include <QItemDelegate>
31+
32+
#include <limits>
33+
34+
35+
class ItemDelegate : public QItemDelegate
36+
{
37+
public:
38+
ItemDelegate( QStandardItemModel* model ): mModel( model ) {}
39+
40+
QSize sizeHint( const QStyleOptionViewItem& /*option*/, const QModelIndex & index ) const override
41+
{
42+
return mModel->item( index.row() )->icon().actualSize( QSize( 512, 512 ) );
43+
}
44+
45+
private:
46+
QStandardItemModel* mModel;
47+
48+
};
49+
50+
QgsSizeScaleWidget::QgsSizeScaleWidget( const QgsVectorLayer * layer, const QgsMarkerSymbolV2 * symbol )
51+
: mSymbol( symbol )
52+
// we just use the minimumValue and maximumValue from the layer, unfortunately they are
53+
// non const, so we get the layer from the registry instead
54+
, mLayer( dynamic_cast<QgsVectorLayer *>( QgsMapLayerRegistry::instance()->mapLayer( layer->id() ) ) )
55+
{
56+
setupUi( this );
57+
setWindowFlags( Qt::WindowStaysOnTopHint );
58+
59+
mLayerTreeLayer = new QgsLayerTreeLayer( mLayer );
60+
mRoot.addChildNode( mLayerTreeLayer ); // takes ownership
61+
62+
treeView->setModel( &mPreviewList );
63+
treeView->setItemDelegate( new ItemDelegate( &mPreviewList ) );
64+
treeView->setHeaderHidden( true );
65+
treeView->expandAll();
66+
67+
QAction* computeFromLayer = new QAction( tr( "Compute from layer" ), this );
68+
connect( computeFromLayer, SIGNAL( triggered() ), this, SLOT( computeFromLayerTriggered() ) );
69+
70+
QMenu* menu = new QMenu();
71+
menu->addAction( computeFromLayer );
72+
computeValuesButton->setMenu( menu );
73+
connect( computeValuesButton, SIGNAL( clicked() ), computeValuesButton, SLOT( showMenu() ) );
74+
75+
//mExpressionWidget->setFilters( QgsFieldProxyModel::Numeric | QgsFieldProxyModel::Date );
76+
mExpressionWidget->setLayer( mLayer );
77+
78+
scaleMethodComboBox->addItem( tr( "Flannery" ), int( QgsScaleExpression::Flannery ) );
79+
scaleMethodComboBox->addItem( tr( "Surface" ), int( QgsScaleExpression::Area ) );
80+
scaleMethodComboBox->addItem( tr( "Radius" ), int( QgsScaleExpression::Linear ) );
81+
82+
minSizeSpinBox->setShowClearButton( false );
83+
maxSizeSpinBox->setShowClearButton( false );
84+
minValueSpinBox->setShowClearButton( false );
85+
maxValueSpinBox->setShowClearButton( false );
86+
87+
// setup ui from expression if any
88+
QgsDataDefined ddSize = mSymbol->dataDefinedSize();
89+
QgsScaleExpression expr( ddSize.expressionString() );
90+
if ( expr )
91+
{
92+
for ( int i = 0; i < scaleMethodComboBox->count(); i++ )
93+
{
94+
if ( scaleMethodComboBox->itemData( i ).toInt() == int( expr.type() ) )
95+
{
96+
scaleMethodComboBox->setCurrentIndex( i );
97+
break;
98+
}
99+
}
100+
101+
mExpressionWidget->setField( expr.baseExpression() );
102+
103+
minValueSpinBox->setValue( expr.minValue() );
104+
maxValueSpinBox->setValue( expr.maxValue() );
105+
minSizeSpinBox->setValue( expr.minSize() );
106+
maxSizeSpinBox->setValue( expr.maxSize() );
107+
updatePreview();
108+
}
109+
110+
connect( minSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( updatePreview() ) );
111+
connect( maxSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( updatePreview() ) );
112+
connect( minValueSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( updatePreview() ) );
113+
connect( maxValueSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( updatePreview() ) );
114+
//potentially very expensive for large layers:
115+
connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( computeFromLayerTriggered() ) );
116+
connect( scaleMethodComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( updatePreview() ) );
117+
}
118+
119+
QgsDataDefined QgsSizeScaleWidget::dataDefined() const
120+
{
121+
QScopedPointer<QgsScaleExpression> exp( createExpression() );
122+
return QgsDataDefined( exp.data() );
123+
}
124+
125+
QgsScaleExpression *QgsSizeScaleWidget::createExpression() const
126+
{
127+
return new QgsScaleExpression( QgsScaleExpression::Type( scaleMethodComboBox->itemData( scaleMethodComboBox->currentIndex() ).toInt() ),
128+
mExpressionWidget->currentField(),
129+
minValueSpinBox->value(),
130+
maxValueSpinBox->value(),
131+
minSizeSpinBox->value(),
132+
maxSizeSpinBox->value() );
133+
}
134+
135+
void QgsSizeScaleWidget::updatePreview()
136+
{
137+
QScopedPointer<QgsScaleExpression> expr( createExpression() );
138+
QList<double> breaks = QgsSymbolLayerV2Utils::prettyBreaks( expr->minValue(), expr->maxValue(), 4 );
139+
140+
treeView->setIconSize( QSize( 512, 512 ) );
141+
mPreviewList.clear();
142+
int widthMax = 0;
143+
for ( int i = 0; i < breaks.length(); i++ )
144+
{
145+
QScopedPointer< QgsMarkerSymbolV2 > symbol( dynamic_cast<QgsMarkerSymbolV2*>( mSymbol->clone() ) );
146+
symbol->setDataDefinedSize( QgsDataDefined() );
147+
symbol->setDataDefinedAngle( "" ); // to avoid symbol not beeing drawn
148+
symbol->setSize( expr->size( breaks[i] ) );
149+
QgsSymbolV2LegendNode node( mLayerTreeLayer, QgsLegendSymbolItemV2( symbol.data(), QString::number( i ), 0 ) );
150+
const QSize sz( node.minimumIconSize() );
151+
node.setIconSize( sz );
152+
QScopedPointer< QStandardItem > item( new QStandardItem( node.data( Qt::DecorationRole ).value<QPixmap>(), QString::number( breaks[i] ) ) );
153+
widthMax = qMax( sz.width(), widthMax );
154+
mPreviewList.appendRow( item.take() );
155+
}
156+
157+
// center icon and align text left by giving icons the same width
158+
// @todo maybe add some space so that icons don't touch
159+
for ( int i = 0; i < breaks.length(); i++ )
160+
{
161+
QPixmap img( mPreviewList.item( i )->icon().pixmap( mPreviewList.item( i )->icon().actualSize( QSize( 512, 512 ) ) ) );
162+
QPixmap enlarged( widthMax, img.height() );
163+
// fill transparent and add original image
164+
enlarged.fill( Qt::transparent );
165+
QPainter p( &enlarged );
166+
p.drawPixmap( QPoint(( widthMax - img.width() ) / 2, 0 ), img );
167+
p.end();
168+
mPreviewList.item( i )->setIcon( enlarged );
169+
}
170+
}
171+
172+
void QgsSizeScaleWidget::computeFromLayerTriggered()
173+
{
174+
QgsExpression expression( mExpressionWidget->currentField() );
175+
if ( ! expression.prepare( mLayer->pendingFields() ) )
176+
return;
177+
178+
QStringList lst( expression.referencedColumns() );
179+
180+
QgsFeatureIterator fit = mLayer->getFeatures(
181+
QgsFeatureRequest().setFlags( expression.needsGeometry()
182+
? QgsFeatureRequest::NoFlags
183+
: QgsFeatureRequest::NoGeometry )
184+
.setSubsetOfAttributes( lst, mLayer->pendingFields() ) );
185+
186+
// create list of non-null attribute values
187+
double min = DBL_MAX;
188+
double max = -DBL_MAX;
189+
QgsFeature f;
190+
while ( fit.nextFeature( f ) )
191+
{
192+
bool ok;
193+
const double value = expression.evaluate( f ).toDouble( &ok );
194+
if ( ok )
195+
{
196+
max = qMax( max, value );
197+
min = qMin( min, value );
198+
}
199+
}
200+
minValueSpinBox->setValue( min );
201+
maxValueSpinBox->setValue( max );
202+
updatePreview();
203+
}
204+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/***************************************************************************
2+
qgssizescalewidget.h - continuous size scale assistant
3+
4+
---------------------
5+
begin : March 2015
6+
copyright : (C) 2015 by Vincent Mora
7+
email : vincent dot mora at oslandia dot com
8+
***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
17+
#ifndef QGSSIZESCALEWIDGET_H
18+
#define QGSSIZESCALEWIDGET_H
19+
20+
#include "qgslayertreegroup.h"
21+
#include "qgslayertreemodel.h"
22+
#include "qgsdatadefinedbutton.h"
23+
#include "ui_widget_size_scale.h"
24+
#include <QStandardItemModel>
25+
26+
class QgsVectorLayer;
27+
class QgsMarkerSymbolV2;
28+
class QgsLayerTreeLayer;
29+
class QgsScaleExpression;
30+
class QgsDataDefined;
31+
32+
class GUI_EXPORT QgsSizeScaleWidget : public QgsDataDefinedAssistant, private Ui_SizeScaleBase
33+
{
34+
Q_OBJECT
35+
36+
public:
37+
QgsSizeScaleWidget( const QgsVectorLayer * layer, const QgsMarkerSymbolV2 * symbol );
38+
39+
QgsDataDefined dataDefined() const override;
40+
41+
private slots:
42+
void computeFromLayerTriggered();
43+
void updatePreview();
44+
45+
private:
46+
47+
const QgsMarkerSymbolV2* mSymbol;
48+
QgsVectorLayer* mLayer;
49+
QgsLayerTreeLayer* mLayerTreeLayer;
50+
QgsLayerTreeGroup mRoot;
51+
QStandardItemModel mPreviewList;
52+
53+
QgsScaleExpression* createExpression() const;
54+
};
55+
56+
#endif //QGSSIZESCALEWIDGET_H

‎src/gui/symbology-ng/qgssymbolslistwidget.cpp

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@
1616

1717
#include "qgssymbolslistwidget.h"
1818

19+
#include "qgssizescalewidget.h"
20+
1921
#include "qgsstylev2managerdialog.h"
22+
#include "qgsdatadefined.h"
2023

2124
#include "qgssymbolv2.h"
2225
#include "qgsstylev2.h"
2326
#include "qgssymbollayerv2utils.h"
27+
#include "qgsmarkersymbollayerv2.h"
2428

2529
#include "qgsapplication.h"
2630

@@ -33,16 +37,17 @@
3337
#include <QInputDialog>
3438
#include <QMessageBox>
3539
#include <QMenu>
40+
#include <QScopedPointer>
3641

3742

38-
QgsSymbolsListWidget::QgsSymbolsListWidget( QgsSymbolV2* symbol, QgsStyleV2* style, QMenu* menu, QWidget* parent )
43+
QgsSymbolsListWidget::QgsSymbolsListWidget( QgsSymbolV2* symbol, QgsStyleV2* style, QMenu* menu, QWidget* parent, const QgsVectorLayer * layer )
3944
: QWidget( parent )
45+
, mSymbol( symbol )
46+
, mStyle( style )
4047
, mAdvancedMenu( 0 )
4148
, mClipFeaturesAction( 0 )
49+
, mLayer( layer )
4250
{
43-
mSymbol = symbol;
44-
mStyle = style;
45-
4651
setupUi( this );
4752

4853
mSymbolUnitWidget->setUnits( QgsSymbolV2::OutputUnitList() << QgsSymbolV2::MM << QgsSymbolV2::MapUnit );
@@ -94,6 +99,16 @@ QgsSymbolsListWidget::QgsSymbolsListWidget( QgsSymbolV2* symbol, QgsStyleV2* sty
9499
connect( spinSize, SIGNAL( valueChanged( double ) ), this, SLOT( setMarkerSize( double ) ) );
95100
connect( spinWidth, SIGNAL( valueChanged( double ) ), this, SLOT( setLineWidth( double ) ) );
96101

102+
connect( mRotationDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedMarkerAngle() ) );
103+
connect( mRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedMarkerAngle() ) );
104+
connect( mSizeDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedMarkerSize() ) );
105+
connect( mSizeDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedMarkerSize() ) );
106+
connect( mWidthDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedLineWidth() ) );
107+
connect( mWidthDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedLineWidth() ) );
108+
109+
if ( mSymbol->type() == QgsSymbolV2::Marker && mLayer )
110+
mSizeDDBtn->setAssistant( new QgsSizeScaleWidget( mLayer, static_cast<const QgsMarkerSymbolV2*>( mSymbol ) ) );
111+
97112
// Live color updates are not undoable to child symbol layers
98113
btnColor->setAcceptLiveUpdates( false );
99114
btnColor->setAllowAlpha( true );
@@ -198,6 +213,23 @@ void QgsSymbolsListWidget::setMarkerAngle( double angle )
198213
emit changed();
199214
}
200215

216+
void QgsSymbolsListWidget::updateDataDefinedMarkerAngle()
217+
{
218+
QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( mSymbol );
219+
QgsDataDefined dd = mRotationDDBtn->currentDataDefined();
220+
221+
bool isDefault = dd.hasDefaultValues();
222+
223+
if ( // shall we remove datadefined expressions for layers ?
224+
( markerSymbol->dataDefinedAngle().hasDefaultValues() && isDefault )
225+
// shall we set the "en masse" expression for properties ?
226+
|| !isDefault )
227+
{
228+
markerSymbol->setDataDefinedAngle( dd );
229+
emit changed();
230+
}
231+
}
232+
201233
void QgsSymbolsListWidget::setMarkerSize( double size )
202234
{
203235
QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( mSymbol );
@@ -207,6 +239,23 @@ void QgsSymbolsListWidget::setMarkerSize( double size )
207239
emit changed();
208240
}
209241

242+
void QgsSymbolsListWidget::updateDataDefinedMarkerSize()
243+
{
244+
QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( mSymbol );
245+
QgsDataDefined dd = mSizeDDBtn->currentDataDefined();
246+
247+
bool isDefault = dd.hasDefaultValues();
248+
249+
if ( // shall we remove datadefined expressions for layers ?
250+
( !markerSymbol->dataDefinedSize().hasDefaultValues() && isDefault )
251+
// shall we set the "en masse" expression for properties ?
252+
|| !isDefault )
253+
{
254+
markerSymbol->setDataDefinedSize( dd );
255+
emit changed();
256+
}
257+
}
258+
210259
void QgsSymbolsListWidget::setLineWidth( double width )
211260
{
212261
QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( mSymbol );
@@ -216,6 +265,23 @@ void QgsSymbolsListWidget::setLineWidth( double width )
216265
emit changed();
217266
}
218267

268+
void QgsSymbolsListWidget::updateDataDefinedLineWidth()
269+
{
270+
QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( mSymbol );
271+
QgsDataDefined dd = mWidthDDBtn->currentDataDefined();
272+
273+
bool isDefault = dd.hasDefaultValues();
274+
275+
if ( // shall we remove datadefined expressions for layers ?
276+
( !lineSymbol->dataDefinedWidth().hasDefaultValues() && isDefault )
277+
// shall we set the "en masse" expression for properties ?
278+
|| !isDefault )
279+
{
280+
lineSymbol->setDataDefinedWidth( dd );
281+
emit changed();
282+
}
283+
}
284+
219285
void QgsSymbolsListWidget::symbolAddedToStyle( QString name, QgsSymbolV2* symbol )
220286
{
221287
Q_UNUSED( name );
@@ -297,11 +363,37 @@ void QgsSymbolsListWidget::updateSymbolInfo()
297363
QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( mSymbol );
298364
spinSize->setValue( markerSymbol->size() );
299365
spinAngle->setValue( markerSymbol->angle() );
366+
367+
if ( mLayer )
368+
{
369+
QgsDataDefined ddSize = markerSymbol->dataDefinedSize();
370+
mSizeDDBtn->init( mLayer, &ddSize, QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doublePosDesc() );
371+
spinSize->setEnabled( !mSizeDDBtn->isActive() );
372+
QgsDataDefined ddAngle( markerSymbol->dataDefinedAngle() );
373+
mRotationDDBtn->init( mLayer, &ddAngle, QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
374+
spinAngle->setEnabled( !mRotationDDBtn->isActive() );
375+
}
376+
else
377+
{
378+
mSizeDDBtn->setEnabled( false );
379+
mRotationDDBtn->setEnabled( false );
380+
}
300381
}
301382
else if ( mSymbol->type() == QgsSymbolV2::Line )
302383
{
303384
QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( mSymbol );
304385
spinWidth->setValue( lineSymbol->width() );
386+
387+
if ( mLayer )
388+
{
389+
QgsDataDefined dd( lineSymbol->dataDefinedWidth() );
390+
mWidthDDBtn->init( mLayer, &dd, QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
391+
spinWidth->setEnabled( !mWidthDDBtn->isActive() );
392+
}
393+
else
394+
{
395+
mWidthDDBtn->setEnabled( false );
396+
}
305397
}
306398

307399
mSymbolUnitWidget->blockSignals( true );

‎src/gui/symbology-ng/qgssymbolslistwidget.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class GUI_EXPORT QgsSymbolsListWidget : public QWidget, private Ui::SymbolsListW
3030
Q_OBJECT
3131

3232
public:
33-
QgsSymbolsListWidget( QgsSymbolV2* symbol, QgsStyleV2* style, QMenu* menu, QWidget* parent );
33+
QgsSymbolsListWidget( QgsSymbolV2* symbol, QgsStyleV2* style, QMenu* menu, QWidget* parent, const QgsVectorLayer * layer = 0 );
3434

3535
public slots:
3636
void setSymbolFromStyle( const QModelIndex & index );
@@ -49,6 +49,10 @@ class GUI_EXPORT QgsSymbolsListWidget : public QWidget, private Ui::SymbolsListW
4949
void openStyleManager();
5050
void clipFeaturesToggled( bool checked );
5151

52+
void updateDataDefinedMarkerSize();
53+
void updateDataDefinedMarkerAngle();
54+
void updateDataDefinedLineWidth();
55+
5256
signals:
5357
void changed();
5458

@@ -57,6 +61,7 @@ class GUI_EXPORT QgsSymbolsListWidget : public QWidget, private Ui::SymbolsListW
5761
QgsStyleV2* mStyle;
5862
QMenu* mAdvancedMenu;
5963
QAction* mClipFeaturesAction;
64+
const QgsVectorLayer* mLayer;
6065

6166
void populateSymbolView();
6267
void populateSymbols( QStringList symbols );

‎src/gui/symbology-ng/qgssymbolv2selectordialog.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,10 @@ void QgsSymbolV2SelectorDialog::layerChanged()
333333
else
334334
{
335335
// then it must be a symbol
336+
currentItem->symbol()->setLayer( mVectorLayer );
336337
// Now populate symbols of that type using the symbols list widget:
337-
QWidget *symbolsList = new QgsSymbolsListWidget( currentItem->symbol(), mStyle, mAdvancedMenu, this );
338+
QWidget *symbolsList = new QgsSymbolsListWidget( currentItem->symbol(), mStyle, mAdvancedMenu, this, mVectorLayer );
339+
338340
setWidget( symbolsList );
339341
connect( symbolsList, SIGNAL( changed() ), this, SLOT( symbolChanged() ) );
340342
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>QgsDataDefinedValueDialog</class>
4+
<widget class="QDialog" name="QgsDataDefinedValueDialog">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>400</width>
10+
<height>99</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Dialog</string>
15+
</property>
16+
<layout class="QGridLayout" name="gridLayout">
17+
<item row="0" column="0">
18+
<widget class="QLabel" name="mLabel">
19+
<property name="text">
20+
<string>Label</string>
21+
</property>
22+
</widget>
23+
</item>
24+
<item row="0" column="1">
25+
<widget class="QgsDoubleSpinBox" name="mSpinBox"/>
26+
</item>
27+
<item row="0" column="2">
28+
<widget class="QgsDataDefinedButton" name="mDDBtn">
29+
<property name="text">
30+
<string>...</string>
31+
</property>
32+
</widget>
33+
</item>
34+
<item row="1" column="0" colspan="3">
35+
<widget class="QDialogButtonBox" name="buttonBox">
36+
<property name="orientation">
37+
<enum>Qt::Horizontal</enum>
38+
</property>
39+
<property name="standardButtons">
40+
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
41+
</property>
42+
</widget>
43+
</item>
44+
</layout>
45+
</widget>
46+
<customwidgets>
47+
<customwidget>
48+
<class>QgsDoubleSpinBox</class>
49+
<extends>QDoubleSpinBox</extends>
50+
<header>qgsdoublespinbox.h</header>
51+
</customwidget>
52+
<customwidget>
53+
<class>QgsDataDefinedButton</class>
54+
<extends>QToolButton</extends>
55+
<header>qgsdatadefinedbutton.h</header>
56+
</customwidget>
57+
</customwidgets>
58+
<resources/>
59+
<connections>
60+
<connection>
61+
<sender>buttonBox</sender>
62+
<signal>accepted()</signal>
63+
<receiver>QgsDataDefinedValueDialog</receiver>
64+
<slot>accept()</slot>
65+
<hints>
66+
<hint type="sourcelabel">
67+
<x>248</x>
68+
<y>254</y>
69+
</hint>
70+
<hint type="destinationlabel">
71+
<x>157</x>
72+
<y>274</y>
73+
</hint>
74+
</hints>
75+
</connection>
76+
<connection>
77+
<sender>buttonBox</sender>
78+
<signal>rejected()</signal>
79+
<receiver>QgsDataDefinedValueDialog</receiver>
80+
<slot>reject()</slot>
81+
<hints>
82+
<hint type="sourcelabel">
83+
<x>316</x>
84+
<y>260</y>
85+
</hint>
86+
<hint type="destinationlabel">
87+
<x>286</x>
88+
<y>274</y>
89+
</hint>
90+
</hints>
91+
</connection>
92+
</connections>
93+
</ui>
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>SizeScaleBase</class>
4+
<widget class="QDialog" name="SizeScaleBase">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>576</width>
10+
<height>343</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Dialog</string>
15+
</property>
16+
<layout class="QGridLayout" name="gridLayout_2">
17+
<item row="0" column="0">
18+
<layout class="QGridLayout" name="gridLayout">
19+
<item row="0" column="0">
20+
<widget class="QLabel" name="label_2">
21+
<property name="text">
22+
<string>Field</string>
23+
</property>
24+
</widget>
25+
</item>
26+
<item row="1" column="0">
27+
<widget class="QLabel" name="label">
28+
<property name="text">
29+
<string>Scale method</string>
30+
</property>
31+
</widget>
32+
</item>
33+
<item row="2" column="0">
34+
<widget class="QLabel" name="label_5">
35+
<property name="text">
36+
<string>Size from</string>
37+
</property>
38+
</widget>
39+
</item>
40+
<item row="2" column="1">
41+
<widget class="QgsDoubleSpinBox" name="minSizeSpinBox">
42+
<property name="decimals">
43+
<number>6</number>
44+
</property>
45+
<property name="minimum">
46+
<double>0.000000000000000</double>
47+
</property>
48+
<property name="maximum">
49+
<double>99999999.000000000000000</double>
50+
</property>
51+
<property name="value">
52+
<double>1.000000000000000</double>
53+
</property>
54+
</widget>
55+
</item>
56+
<item row="2" column="2">
57+
<widget class="QLabel" name="label_6">
58+
<property name="text">
59+
<string>to</string>
60+
</property>
61+
</widget>
62+
</item>
63+
<item row="2" column="3">
64+
<widget class="QgsDoubleSpinBox" name="maxSizeSpinBox">
65+
<property name="decimals">
66+
<number>6</number>
67+
</property>
68+
<property name="maximum">
69+
<double>99999999.000000000000000</double>
70+
</property>
71+
<property name="value">
72+
<double>10.000000000000000</double>
73+
</property>
74+
</widget>
75+
</item>
76+
<item row="3" column="0">
77+
<widget class="QLabel" name="label_3">
78+
<property name="text">
79+
<string>Values from</string>
80+
</property>
81+
</widget>
82+
</item>
83+
<item row="3" column="1">
84+
<widget class="QgsDoubleSpinBox" name="minValueSpinBox">
85+
<property name="decimals">
86+
<number>6</number>
87+
</property>
88+
<property name="minimum">
89+
<double>-99999999.000000000000000</double>
90+
</property>
91+
<property name="maximum">
92+
<double>99999999.000000000000000</double>
93+
</property>
94+
</widget>
95+
</item>
96+
<item row="3" column="2">
97+
<widget class="QLabel" name="label_4">
98+
<property name="text">
99+
<string>to</string>
100+
</property>
101+
</widget>
102+
</item>
103+
<item row="3" column="3">
104+
<widget class="QgsDoubleSpinBox" name="maxValueSpinBox">
105+
<property name="decimals">
106+
<number>6</number>
107+
</property>
108+
<property name="minimum">
109+
<double>-99999999.000000000000000</double>
110+
</property>
111+
<property name="maximum">
112+
<double>99999999.000000000000000</double>
113+
</property>
114+
</widget>
115+
</item>
116+
<item row="3" column="4">
117+
<widget class="QToolButton" name="computeValuesButton">
118+
<property name="maximumSize">
119+
<size>
120+
<width>20</width>
121+
<height>16777215</height>
122+
</size>
123+
</property>
124+
<property name="text">
125+
<string/>
126+
</property>
127+
</widget>
128+
</item>
129+
<item row="0" column="1" colspan="3">
130+
<widget class="QgsFieldExpressionWidget" name="mExpressionWidget" native="true">
131+
<property name="minimumSize">
132+
<size>
133+
<width>0</width>
134+
<height>0</height>
135+
</size>
136+
</property>
137+
<property name="maximumSize">
138+
<size>
139+
<width>500</width>
140+
<height>16777215</height>
141+
</size>
142+
</property>
143+
<property name="focusPolicy">
144+
<enum>Qt::StrongFocus</enum>
145+
</property>
146+
</widget>
147+
</item>
148+
<item row="1" column="1" colspan="3">
149+
<widget class="QComboBox" name="scaleMethodComboBox"/>
150+
</item>
151+
</layout>
152+
</item>
153+
<item row="0" column="1" rowspan="2">
154+
<widget class="QTreeView" name="treeView"/>
155+
</item>
156+
<item row="1" column="0">
157+
<spacer name="verticalSpacer">
158+
<property name="orientation">
159+
<enum>Qt::Vertical</enum>
160+
</property>
161+
<property name="sizeHint" stdset="0">
162+
<size>
163+
<width>20</width>
164+
<height>154</height>
165+
</size>
166+
</property>
167+
</spacer>
168+
</item>
169+
<item row="2" column="0" colspan="2">
170+
<widget class="QDialogButtonBox" name="buttonBox">
171+
<property name="orientation">
172+
<enum>Qt::Horizontal</enum>
173+
</property>
174+
<property name="standardButtons">
175+
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
176+
</property>
177+
</widget>
178+
</item>
179+
</layout>
180+
</widget>
181+
<customwidgets>
182+
<customwidget>
183+
<class>QgsFieldExpressionWidget</class>
184+
<extends>QWidget</extends>
185+
<header location="global">qgsfieldexpressionwidget.h</header>
186+
<container>1</container>
187+
</customwidget>
188+
<customwidget>
189+
<class>QgsDoubleSpinBox</class>
190+
<extends>QDoubleSpinBox</extends>
191+
<header>qgsdoublespinbox.h</header>
192+
</customwidget>
193+
</customwidgets>
194+
<tabstops>
195+
<tabstop>mExpressionWidget</tabstop>
196+
<tabstop>scaleMethodComboBox</tabstop>
197+
<tabstop>minSizeSpinBox</tabstop>
198+
<tabstop>maxSizeSpinBox</tabstop>
199+
<tabstop>minValueSpinBox</tabstop>
200+
<tabstop>maxValueSpinBox</tabstop>
201+
<tabstop>computeValuesButton</tabstop>
202+
<tabstop>buttonBox</tabstop>
203+
<tabstop>treeView</tabstop>
204+
</tabstops>
205+
<resources/>
206+
<connections>
207+
<connection>
208+
<sender>buttonBox</sender>
209+
<signal>accepted()</signal>
210+
<receiver>SizeScaleBase</receiver>
211+
<slot>accept()</slot>
212+
<hints>
213+
<hint type="sourcelabel">
214+
<x>248</x>
215+
<y>254</y>
216+
</hint>
217+
<hint type="destinationlabel">
218+
<x>157</x>
219+
<y>274</y>
220+
</hint>
221+
</hints>
222+
</connection>
223+
<connection>
224+
<sender>buttonBox</sender>
225+
<signal>rejected()</signal>
226+
<receiver>SizeScaleBase</receiver>
227+
<slot>reject()</slot>
228+
<hints>
229+
<hint type="sourcelabel">
230+
<x>316</x>
231+
<y>260</y>
232+
</hint>
233+
<hint type="destinationlabel">
234+
<x>286</x>
235+
<y>274</y>
236+
</hint>
237+
</hints>
238+
</connection>
239+
</connections>
240+
</ui>

‎src/ui/symbollayer/widget_symbolslist.ui

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
<property name="text">
8585
<string/>
8686
</property>
87+
<zorder>stackedWidget</zorder>
8788
</widget>
8889
</item>
8990
<item row="0" column="1">
@@ -104,40 +105,28 @@
104105
</property>
105106
<widget class="QWidget" name="pageMarker">
106107
<layout class="QGridLayout" name="gridLayout">
107-
<item row="0" column="1">
108-
<widget class="QgsDoubleSpinBox" name="spinSize">
109-
<property name="decimals">
110-
<number>5</number>
111-
</property>
112-
<property name="maximum">
113-
<double>99999.000000000000000</double>
114-
</property>
115-
<property name="singleStep">
116-
<double>0.200000000000000</double>
117-
</property>
118-
<property name="value">
119-
<double>1.000000000000000</double>
120-
</property>
121-
<property name="showClearButton" stdset="0">
122-
<bool>false</bool>
123-
</property>
124-
</widget>
125-
</item>
126108
<item row="0" column="0">
127109
<widget class="QLabel" name="label_2">
128110
<property name="text">
129111
<string>Size</string>
130112
</property>
131113
</widget>
132114
</item>
133-
<item row="2" column="0">
134-
<widget class="QLabel" name="label_3">
115+
<item row="0" column="3">
116+
<widget class="QgsDataDefinedButton" name="mSizeDDBtn">
135117
<property name="text">
136-
<string>Rotation</string>
118+
<string>...</string>
119+
</property>
120+
</widget>
121+
</item>
122+
<item row="1" column="3">
123+
<widget class="QgsDataDefinedButton" name="mRotationDDBtn">
124+
<property name="text">
125+
<string>...</string>
137126
</property>
138127
</widget>
139128
</item>
140-
<item row="2" column="1">
129+
<item row="1" column="2">
141130
<widget class="QgsDoubleSpinBox" name="spinAngle">
142131
<property name="suffix">
143132
<string> °</string>
@@ -153,6 +142,32 @@
153142
</property>
154143
</widget>
155144
</item>
145+
<item row="1" column="0">
146+
<widget class="QLabel" name="label_3">
147+
<property name="text">
148+
<string>Rotation</string>
149+
</property>
150+
</widget>
151+
</item>
152+
<item row="0" column="2">
153+
<widget class="QgsDoubleSpinBox" name="spinSize">
154+
<property name="decimals">
155+
<number>5</number>
156+
</property>
157+
<property name="maximum">
158+
<double>99999.000000000000000</double>
159+
</property>
160+
<property name="singleStep">
161+
<double>0.200000000000000</double>
162+
</property>
163+
<property name="value">
164+
<double>1.000000000000000</double>
165+
</property>
166+
<property name="showClearButton" stdset="0">
167+
<bool>false</bool>
168+
</property>
169+
</widget>
170+
</item>
156171
</layout>
157172
</widget>
158173
<widget class="QWidget" name="pageLine">
@@ -176,6 +191,13 @@
176191
</property>
177192
</widget>
178193
</item>
194+
<item row="0" column="2">
195+
<widget class="QgsDataDefinedButton" name="mWidthDDBtn">
196+
<property name="text">
197+
<string>...</string>
198+
</property>
199+
</widget>
200+
</item>
179201
<item row="0" column="0">
180202
<widget class="QLabel" name="label_4">
181203
<property name="text">
@@ -334,17 +356,25 @@
334356
<header>qgsunitselectionwidget.h</header>
335357
<container>1</container>
336358
</customwidget>
359+
<customwidget>
360+
<class>QgsDataDefinedButton</class>
361+
<extends>QToolButton</extends>
362+
<header>qgsdatadefinedbutton.h</header>
363+
</customwidget>
337364
</customwidgets>
338365
<tabstops>
339366
<tabstop>mTransparencySlider</tabstop>
340367
<tabstop>spinSize</tabstop>
368+
<tabstop>mSizeDDBtn</tabstop>
369+
<tabstop>spinWidth</tabstop>
370+
<tabstop>mWidthDDBtn</tabstop>
341371
<tabstop>btnColor</tabstop>
342372
<tabstop>spinAngle</tabstop>
373+
<tabstop>mRotationDDBtn</tabstop>
343374
<tabstop>groupsCombo</tabstop>
344375
<tabstop>openStyleManagerButton</tabstop>
345376
<tabstop>viewSymbols</tabstop>
346377
<tabstop>btnAdvanced</tabstop>
347-
<tabstop>spinWidth</tabstop>
348378
</tabstops>
349379
<resources/>
350380
<connections/>

0 commit comments

Comments
 (0)
Please sign in to comment.