Skip to content

Commit 9516c3a

Browse files
author
wonder
committedApr 20, 2010
[FEATURE] gradient color ramps now support multiple stops - for adding intermediate colors
git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@13333 c8812cc2-4d05-0410-92ff-de0c093fc19c

File tree

7 files changed

+411
-21
lines changed

7 files changed

+411
-21
lines changed
 

‎python/core/conversions.sip

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ which are not wrapped by PyQt:
99
- QMap<int, QMap<int, TYPE> >
1010
- QMap<QString, QVariant::Type>
1111
- QMap<TYPE1, TYPE2*>
12+
- QMap<double, TYPE>
1213
- QMultiMap<double, TYPE2>
1314
- QMap<int, QgsOverlayObject*>*
1415
*/
@@ -730,6 +731,107 @@ template<TYPE1, TYPE2>
730731
%End
731732
};
732733

734+
735+
736+
template<double, TYPE>
737+
%MappedType QMap<double, TYPE>
738+
{
739+
%TypeHeaderCode
740+
#include <QMap>
741+
%End
742+
743+
%ConvertFromTypeCode
744+
// Create the dictionary.
745+
PyObject *d = PyDict_New();
746+
747+
if (!d)
748+
return NULL;
749+
750+
// Set the dictionary elements.
751+
QMap<double, TYPE>::iterator i;
752+
753+
for (i = sipCpp->begin(); i != sipCpp->end(); ++i)
754+
{
755+
PyObject *t1obj = PyFloat_FromDouble(i.key());
756+
TYPE* t2 = &i.value();
757+
PyObject *t2obj = sipConvertFromInstance(t2, sipClass_TYPE, sipTransferObj);
758+
759+
if (t1obj == NULL || t2obj == NULL || PyDict_SetItem(d, t1obj, t2obj) < 0)
760+
{
761+
Py_DECREF(d);
762+
763+
if (t1obj)
764+
Py_DECREF(t1obj);
765+
766+
if (t2obj)
767+
Py_DECREF(t2obj);
768+
769+
return NULL;
770+
}
771+
772+
Py_DECREF(t1obj);
773+
Py_DECREF(t2obj);
774+
}
775+
776+
return d;
777+
%End
778+
779+
%ConvertToTypeCode
780+
PyObject *t1obj, *t2obj;
781+
#if PY_VERSION_HEX >= 0x02050000
782+
Py_ssize_t i = 0;
783+
#else
784+
int i = 0;
785+
#endif
786+
787+
// Check the type if that is all that is required.
788+
if (sipIsErr == NULL)
789+
{
790+
if (!PyDict_Check(sipPy))
791+
return 0;
792+
793+
while (PyDict_Next(sipPy, &i, &t1obj, &t2obj))
794+
{
795+
if (!PyFloat_Check(t1obj))
796+
return 0;
797+
798+
if (!sipCanConvertToInstance(t2obj, sipClass_TYPE, SIP_NOT_NONE))
799+
return 0;
800+
}
801+
802+
return 1;
803+
}
804+
805+
QMap<double, TYPE> *qm = new QMap<double, TYPE>;
806+
807+
while (PyDict_Next(sipPy, &i, &t1obj, &t2obj))
808+
{
809+
int state;
810+
811+
double k = PyFloat_AsDouble(t1obj);
812+
TYPE *t2 = reinterpret_cast<TYPE *>(sipConvertToInstance(t2obj, sipClass_TYPE, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr));
813+
814+
if (*sipIsErr)
815+
{
816+
sipReleaseInstance(t2, sipClass_TYPE, state);
817+
delete qm;
818+
return 0;
819+
}
820+
821+
qm->insert(k, *t2);
822+
823+
sipReleaseInstance(t2, sipClass_TYPE, state);
824+
}
825+
826+
*sipCppPtr = qm;
827+
828+
return sipGetState(sipTransferObj);
829+
%End
830+
};
831+
832+
833+
834+
733835
template<double, TYPE2>
734836
%MappedType QMultiMap<double, TYPE2>
735837
{

‎python/core/symbology-ng-core.sip

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,19 +762,59 @@ class QgsVectorColorRampV2
762762
#include <qgsvectorcolorrampv2.h>
763763
%End
764764

765+
%ConvertToSubClassCode
766+
if (sipCpp->type() == "gradient")
767+
{
768+
sipClass = sipClass_QgsVectorGradientColorRampV2;
769+
}
770+
else
771+
sipClass = 0;
772+
%End
773+
765774
public:
766775
virtual ~QgsVectorColorRampV2();
767776

768777
virtual QColor color(double value) const = 0;
769778

770779
virtual QString type() const = 0;
771780

772-
virtual QgsVectorColorRampV2* clone() const = 0;
781+
virtual QgsVectorColorRampV2* clone() const = 0 /Factory/;
773782

774783
virtual QgsStringMap properties() const = 0;
775784

776785
};
777786

787+
class QgsVectorGradientColorRampV2 : QgsVectorColorRampV2
788+
{
789+
public:
790+
QgsVectorGradientColorRampV2( QColor color1 = QColor(0,0,255),
791+
QColor color2 = QColor(0,255,0) );
792+
793+
static QgsVectorColorRampV2* create( const QgsStringMap& properties = QgsStringMap() ) /Factory/;
794+
795+
virtual QColor color( double value ) const;
796+
797+
virtual QString type() const;
798+
799+
virtual QgsVectorColorRampV2* clone() const /Factory/;
800+
801+
virtual QgsStringMap properties() const;
802+
803+
QColor color1() const;
804+
QColor color2() const;
805+
806+
void setColor1( QColor color );
807+
void setColor2( QColor color );
808+
809+
typedef QMultiMap<double, QColor> StopsMap;
810+
811+
void setStops(const StopsMap& stops);
812+
const StopsMap& stops() const;
813+
814+
};
815+
816+
817+
778818
//////////
779819

780820
class QgsSymbologyV2Conversion
@@ -858,3 +898,5 @@ class QgsRendererV2Registry
858898
QgsRendererV2Registry();
859899
~QgsRendererV2Registry();
860900
};
901+
902+
///////////////

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

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,84 @@ QgsVectorColorRampV2* QgsVectorGradientColorRampV2::create( const QgsStringMap&
1818
color1 = QgsSymbolLayerV2Utils::decodeColor( props["color1"] );
1919
if ( props.contains( "color2" ) )
2020
color2 = QgsSymbolLayerV2Utils::decodeColor( props["color2"] );
21-
return new QgsVectorGradientColorRampV2( color1, color2 );
21+
22+
StopsMap stops;
23+
if ( props.contains( "stops" ) )
24+
{
25+
foreach( QString stop, props["stops"].split( ':' ) )
26+
{
27+
int i = stop.indexOf( ';' );
28+
if ( i == -1 ) continue;
29+
30+
QColor c = QgsSymbolLayerV2Utils::decodeColor( stop.mid( i + 1 ) );
31+
stops.insert( stop.left( i ).toDouble(), c );
32+
}
33+
}
34+
35+
QgsVectorGradientColorRampV2* r = new QgsVectorGradientColorRampV2( color1, color2 );
36+
r->setStops( stops );
37+
return r;
2238
}
2339

24-
QColor QgsVectorGradientColorRampV2::color( double value ) const
40+
static QColor _interpolate( QColor c1, QColor c2, double value )
2541
{
26-
int r = ( int )( mColor1.red() + value * ( mColor2.red() - mColor1.red() ) );
27-
int g = ( int )( mColor1.green() + value * ( mColor2.green() - mColor1.green() ) );
28-
int b = ( int )( mColor1.blue() + value * ( mColor2.blue() - mColor1.blue() ) );
42+
int r = ( int )( c1.red() + value * ( c2.red() - c1.red() ) );
43+
int g = ( int )( c1.green() + value * ( c2.green() - c1.green() ) );
44+
int b = ( int )( c1.blue() + value * ( c2.blue() - c1.blue() ) );
2945

3046
return QColor::fromRgb( r, g, b );
3147
}
3248

49+
QColor QgsVectorGradientColorRampV2::color( double value ) const
50+
{
51+
if ( mStops.isEmpty() )
52+
{
53+
return _interpolate( mColor1, mColor2, value );
54+
}
55+
else
56+
{
57+
double lower = 0, upper;
58+
QColor c1 = mColor1, c2;
59+
for ( StopsMap::const_iterator it = mStops.begin(); it != mStops.end(); ++it )
60+
{
61+
if ( it.key() >= value )
62+
{
63+
upper = it.key();
64+
c2 = it.value();
65+
66+
return upper == lower ? c1 : _interpolate( c1, c2, ( value - lower ) / ( upper - lower ) );
67+
}
68+
lower = it.key();
69+
c1 = it.value();
70+
}
71+
72+
upper = 1;
73+
c2 = mColor2;
74+
return upper == lower ? c1 : _interpolate( c1, c2, ( value - lower ) / ( upper - lower ) );
75+
}
76+
}
77+
3378
QgsVectorColorRampV2* QgsVectorGradientColorRampV2::clone() const
3479
{
35-
return new QgsVectorGradientColorRampV2( mColor1, mColor2 );
80+
QgsVectorGradientColorRampV2* r = new QgsVectorGradientColorRampV2( mColor1, mColor2 );
81+
r->setStops( mStops );
82+
return r;
3683
}
3784

3885
QgsStringMap QgsVectorGradientColorRampV2::properties() const
3986
{
4087
QgsStringMap map;
4188
map["color1"] = QgsSymbolLayerV2Utils::encodeColor( mColor1 );
4289
map["color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 );
90+
if ( !mStops.isEmpty() )
91+
{
92+
QStringList lst;
93+
for ( StopsMap::const_iterator it = mStops.begin(); it != mStops.end(); ++it )
94+
{
95+
lst.append( QString( "%1;%2" ).arg( it.key() ).arg( QgsSymbolLayerV2Utils::encodeColor( it.value() ) ) );
96+
}
97+
map["stops"] = lst.join( ":" );
98+
}
4399
return map;
44100
}
45101

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,14 @@ class CORE_EXPORT QgsVectorGradientColorRampV2 : public QgsVectorColorRampV2
4646
void setColor1( QColor color ) { mColor1 = color; }
4747
void setColor2( QColor color ) { mColor2 = color; }
4848

49+
typedef QMap<double, QColor> StopsMap;
50+
51+
void setStops( const StopsMap& stops ) { mStops = stops; }
52+
const StopsMap& stops() const { return mStops; }
53+
4954
protected:
5055
QColor mColor1, mColor2;
56+
StopsMap mStops;
5157
};
5258

5359
#define DEFAULT_RANDOM_COUNT 10

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

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
#include "qgsvectorcolorrampv2.h"
55

66
#include <QColorDialog>
7-
7+
#include <QInputDialog>
8+
#include <QPainter>
89

910
QgsVectorGradientColorRampV2Dialog::QgsVectorGradientColorRampV2Dialog( QgsVectorGradientColorRampV2* ramp, QWidget* parent )
1011
: QDialog( parent ), mRamp( ramp )
@@ -15,11 +16,54 @@ QgsVectorGradientColorRampV2Dialog::QgsVectorGradientColorRampV2Dialog( QgsVecto
1516
connect( btnColor1, SIGNAL( clicked() ), this, SLOT( setColor1() ) );
1617
connect( btnColor2, SIGNAL( clicked() ), this, SLOT( setColor2() ) );
1718

19+
// handle stops
20+
QgsVectorGradientColorRampV2::StopsMap stops = ramp->stops();
21+
groupStops->setChecked( !stops.isEmpty() );
22+
23+
QgsVectorGradientColorRampV2::StopsMap::iterator i;
24+
QList<QTreeWidgetItem *> items;
25+
for ( i = stops.begin(); i != stops.end(); ++i )
26+
{
27+
QStringList lst;
28+
lst << "." << QString::number( i.key()*100, 'f', 0 );
29+
QTreeWidgetItem* item = new QTreeWidgetItem( lst );
30+
31+
setStopColor( item, i.value() );
32+
item->setData( 0, StopOffsetRole, i.key() );
33+
34+
items.append( item );
35+
}
36+
treeStops->insertTopLevelItems( 0, items );
37+
treeStops->resizeColumnToContents( 0 );
38+
treeStops->sortByColumn( 1, Qt::AscendingOrder );
39+
treeStops->setSortingEnabled( true );
40+
41+
connect( groupStops, SIGNAL( toggled( bool ) ), this, SLOT( toggledStops( bool ) ) );
42+
connect( btnAddStop, SIGNAL( clicked() ), this, SLOT( addStop() ) );
43+
connect( btnRemoveStop, SIGNAL( clicked() ), this, SLOT( removeStop() ) );
44+
connect( treeStops, SIGNAL( itemDoubleClicked( QTreeWidgetItem*, int ) ), this, SLOT( stopDoubleClicked( QTreeWidgetItem*, int ) ) );
45+
1846
updatePreview();
1947
}
2048

2149
void QgsVectorGradientColorRampV2Dialog::updatePreview()
2250
{
51+
// update ramp stops from the tree widget
52+
QgsVectorGradientColorRampV2::StopsMap map;
53+
if ( groupStops->isChecked() )
54+
{
55+
int count = treeStops->topLevelItemCount();
56+
for ( int i = 0; i < count; i++ )
57+
{
58+
QTreeWidgetItem* item = treeStops->topLevelItem( i );
59+
double key = item->data( 0, StopOffsetRole ).toDouble();
60+
QColor c = item->data( 0, StopColorRole ).value<QColor>();
61+
map.insert( key, c );
62+
}
63+
}
64+
mRamp->setStops( map );
65+
66+
// generate the preview
2367
QSize size( 300, 40 );
2468
lblPreview->setPixmap( QgsSymbolLayerV2Utils::colorRampPreviewPixmap( mRamp, size ) );
2569

@@ -44,3 +88,100 @@ void QgsVectorGradientColorRampV2Dialog::setColor2()
4488
mRamp->setColor2( color );
4589
updatePreview();
4690
}
91+
92+
void QgsVectorGradientColorRampV2Dialog::setStopColor( QTreeWidgetItem* item, QColor color )
93+
{
94+
QSize iconSize( 16, 16 );
95+
QPixmap pixmap( iconSize );
96+
pixmap.fill( QColor( 0, 0, 0, 0 ) );
97+
QRect rect( 1, 1, iconSize.width() - 2, iconSize.height() - 2 );
98+
99+
// draw a slightly rounded rectangle
100+
QPainter p;
101+
p.begin( &pixmap );
102+
p.setPen( Qt::NoPen );
103+
p.setRenderHint( QPainter::Antialiasing );
104+
p.setBrush( color );
105+
p.drawRoundedRect( rect, 4, 4 );
106+
p.end();
107+
108+
item->setIcon( 0, QIcon( pixmap ) );
109+
item->setData( 0, StopColorRole, color );
110+
item->setText( 0, color.name() );
111+
}
112+
113+
void QgsVectorGradientColorRampV2Dialog::stopDoubleClicked( QTreeWidgetItem* item, int column )
114+
{
115+
if ( column == 0 )
116+
{
117+
QColor color = QColorDialog::getColor( item->data( 0, StopColorRole ).value<QColor>(), this );
118+
if ( !color.isValid() )
119+
return;
120+
setStopColor( item, color );
121+
122+
updatePreview();
123+
}
124+
else
125+
{
126+
bool ok;
127+
double key = item->data( 0, StopOffsetRole ).toDouble();
128+
int val = ( int )( key * 100 );
129+
val = QInputDialog::getInt( this, tr( "Offset of the stop" ),
130+
tr( "Please enter offset in percents (%) of the new stop" ),
131+
val, 0, 100, 1, &ok );
132+
if ( !ok )
133+
return;
134+
135+
double newkey = val / 100.0;
136+
item->setText( 1, QString::number( val ) );
137+
138+
item->setData( 0, StopOffsetRole, newkey );
139+
140+
updatePreview();
141+
}
142+
}
143+
144+
void QgsVectorGradientColorRampV2Dialog::addStop()
145+
{
146+
QColor color = QColorDialog::getColor( QColor(), this );
147+
if ( !color.isValid() )
148+
return;
149+
150+
bool ok;
151+
int val = 50;
152+
val = QInputDialog::getInt( this, tr( "Offset of the stop" ),
153+
tr( "Please enter offset in percents (%) of the new stop" ),
154+
val, 0, 100, 1, &ok );
155+
if ( !ok )
156+
return;
157+
158+
double key = val / 100.0;
159+
QStringList lst;
160+
lst << "." << QString::number( val, 'f', 0 );
161+
QTreeWidgetItem* item = new QTreeWidgetItem( lst );
162+
163+
setStopColor( item, color );
164+
item->setData( 0, StopOffsetRole, key );
165+
166+
treeStops->addTopLevelItem( item );
167+
168+
treeStops->resizeColumnToContents( 0 );
169+
170+
updatePreview();
171+
}
172+
173+
void QgsVectorGradientColorRampV2Dialog::removeStop()
174+
{
175+
QTreeWidgetItem* item = treeStops->currentItem();
176+
if ( !item )
177+
return;
178+
int index = treeStops->indexOfTopLevelItem( item );
179+
treeStops->takeTopLevelItem( index );
180+
181+
updatePreview();
182+
}
183+
184+
void QgsVectorGradientColorRampV2Dialog::toggledStops( bool on )
185+
{
186+
updatePreview();
187+
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,21 @@ class GUI_EXPORT QgsVectorGradientColorRampV2Dialog : public QDialog, private Ui
1919
void setColor1();
2020
void setColor2();
2121

22+
void toggledStops( bool on );
23+
void addStop();
24+
void removeStop();
25+
26+
void stopDoubleClicked( QTreeWidgetItem* item, int column );
27+
2228
protected:
2329

2430
void updatePreview();
31+
void setStopColor( QTreeWidgetItem* item, QColor color );
2532

2633
QgsVectorGradientColorRampV2* mRamp;
34+
35+
static const int StopColorRole = Qt::UserRole + 1;
36+
static const int StopOffsetRole = Qt::UserRole + 2;
2737
};
2838

2939
#endif

‎src/ui/qgsvectorgradientcolorrampv2dialogbase.ui

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
<x>0</x>
88
<y>0</y>
99
<width>400</width>
10-
<height>300</height>
10+
<height>364</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
1414
<string>Gradient color ramp</string>
1515
</property>
16-
<layout class="QVBoxLayout">
16+
<layout class="QVBoxLayout" name="verticalLayout">
1717
<item>
1818
<layout class="QGridLayout">
1919
<item row="0" column="0">
@@ -63,20 +63,53 @@
6363
</layout>
6464
</item>
6565
<item>
66-
<spacer>
67-
<property name="orientation">
68-
<enum>Qt::Vertical</enum>
66+
<widget class="QGroupBox" name="groupStops">
67+
<property name="title">
68+
<string>Multiple stops</string>
6969
</property>
70-
<property name="sizeType">
71-
<enum>QSizePolicy::Preferred</enum>
70+
<property name="checkable">
71+
<bool>true</bool>
7272
</property>
73-
<property name="sizeHint" stdset="0">
74-
<size>
75-
<width>20</width>
76-
<height>40</height>
77-
</size>
73+
<property name="checked">
74+
<bool>false</bool>
7875
</property>
79-
</spacer>
76+
<layout class="QGridLayout" name="gridLayout">
77+
<item row="0" column="1">
78+
<widget class="QPushButton" name="btnAddStop">
79+
<property name="text">
80+
<string>Add stop</string>
81+
</property>
82+
</widget>
83+
</item>
84+
<item row="1" column="1">
85+
<widget class="QPushButton" name="btnRemoveStop">
86+
<property name="text">
87+
<string>Remove stop</string>
88+
</property>
89+
</widget>
90+
</item>
91+
<item row="0" column="0" rowspan="2">
92+
<widget class="QTreeWidget" name="treeStops">
93+
<property name="rootIsDecorated">
94+
<bool>false</bool>
95+
</property>
96+
<property name="columnCount">
97+
<number>2</number>
98+
</property>
99+
<column>
100+
<property name="text">
101+
<string>Color</string>
102+
</property>
103+
</column>
104+
<column>
105+
<property name="text">
106+
<string>Offset</string>
107+
</property>
108+
</column>
109+
</widget>
110+
</item>
111+
</layout>
112+
</widget>
80113
</item>
81114
<item>
82115
<widget class="QGroupBox" name="groupBox">

0 commit comments

Comments
 (0)
Please sign in to comment.