[QGIS Commit] r13333 - in trunk/qgis: python/core src/core/symbology-ng src/gui/symbology-ng src/ui

svn_qgis at osgeo.org svn_qgis at osgeo.org
Tue Apr 20 08:25:46 EDT 2010


Author: wonder
Date: 2010-04-20 08:25:42 -0400 (Tue, 20 Apr 2010)
New Revision: 13333

Modified:
   trunk/qgis/python/core/conversions.sip
   trunk/qgis/python/core/symbology-ng-core.sip
   trunk/qgis/src/core/symbology-ng/qgsvectorcolorrampv2.cpp
   trunk/qgis/src/core/symbology-ng/qgsvectorcolorrampv2.h
   trunk/qgis/src/gui/symbology-ng/qgsvectorgradientcolorrampv2dialog.cpp
   trunk/qgis/src/gui/symbology-ng/qgsvectorgradientcolorrampv2dialog.h
   trunk/qgis/src/ui/qgsvectorgradientcolorrampv2dialogbase.ui
Log:
[FEATURE] gradient color ramps now support multiple stops - for adding intermediate colors


Modified: trunk/qgis/python/core/conversions.sip
===================================================================
--- trunk/qgis/python/core/conversions.sip	2010-04-20 11:51:20 UTC (rev 13332)
+++ trunk/qgis/python/core/conversions.sip	2010-04-20 12:25:42 UTC (rev 13333)
@@ -9,6 +9,7 @@
 - QMap<int, QMap<int, TYPE> >
 - QMap<QString, QVariant::Type>
 - QMap<TYPE1, TYPE2*>
+- QMap<double, TYPE>
 - QMultiMap<double, TYPE2>
 - QMap<int, QgsOverlayObject*>*
 */
@@ -730,6 +731,107 @@
 %End
 };
 
+
+
+template<double, TYPE>
+%MappedType QMap<double, TYPE>
+{
+%TypeHeaderCode
+#include <QMap>
+%End
+
+%ConvertFromTypeCode
+    // Create the dictionary.
+    PyObject *d = PyDict_New();
+
+    if (!d)
+        return NULL;
+
+    // Set the dictionary elements.
+    QMap<double, TYPE>::iterator i;
+
+    for (i = sipCpp->begin(); i != sipCpp->end(); ++i)
+    {
+        PyObject *t1obj = PyFloat_FromDouble(i.key());
+        TYPE* t2 = &i.value();
+        PyObject *t2obj = sipConvertFromInstance(t2, sipClass_TYPE, sipTransferObj);
+
+        if (t1obj == NULL || t2obj == NULL || PyDict_SetItem(d, t1obj, t2obj) < 0)
+        {
+            Py_DECREF(d);
+
+            if (t1obj)
+                Py_DECREF(t1obj);
+
+            if (t2obj)
+                Py_DECREF(t2obj);
+
+            return NULL;
+        }
+
+        Py_DECREF(t1obj);
+        Py_DECREF(t2obj);
+   }
+
+    return d;
+%End
+
+%ConvertToTypeCode
+    PyObject *t1obj, *t2obj;
+#if PY_VERSION_HEX >= 0x02050000
+    Py_ssize_t i = 0;
+#else
+    int i = 0;
+#endif
+
+    // Check the type if that is all that is required.
+    if (sipIsErr == NULL)
+    {
+        if (!PyDict_Check(sipPy))
+            return 0;
+
+        while (PyDict_Next(sipPy, &i, &t1obj, &t2obj))
+        {
+            if (!PyFloat_Check(t1obj))
+                return 0;
+
+            if (!sipCanConvertToInstance(t2obj, sipClass_TYPE, SIP_NOT_NONE))
+                return 0;
+        }
+
+        return 1;
+    }
+
+    QMap<double, TYPE> *qm = new QMap<double, TYPE>;
+
+    while (PyDict_Next(sipPy, &i, &t1obj, &t2obj))
+    {
+        int state;
+
+        double k = PyFloat_AsDouble(t1obj);
+        TYPE *t2 = reinterpret_cast<TYPE *>(sipConvertToInstance(t2obj, sipClass_TYPE, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr));
+
+        if (*sipIsErr)
+        {
+            sipReleaseInstance(t2, sipClass_TYPE, state);
+            delete qm;
+            return 0;
+        }
+
+        qm->insert(k, *t2);
+
+        sipReleaseInstance(t2, sipClass_TYPE, state);
+    }
+
+    *sipCppPtr = qm;
+
+    return sipGetState(sipTransferObj);
+%End
+};
+
+
+
+
 template<double, TYPE2>
 %MappedType QMultiMap<double, TYPE2>
 {

Modified: trunk/qgis/python/core/symbology-ng-core.sip
===================================================================
--- trunk/qgis/python/core/symbology-ng-core.sip	2010-04-20 11:51:20 UTC (rev 13332)
+++ trunk/qgis/python/core/symbology-ng-core.sip	2010-04-20 12:25:42 UTC (rev 13333)
@@ -762,6 +762,15 @@
 #include <qgsvectorcolorrampv2.h>
 %End
 
+%ConvertToSubClassCode
+  if (sipCpp->type() == "gradient")
+  {
+    sipClass = sipClass_QgsVectorGradientColorRampV2;
+  }
+  else
+    sipClass = 0;
+%End
+
 public:
   virtual ~QgsVectorColorRampV2();
 
@@ -769,12 +778,43 @@
 
   virtual QString type() const = 0;
 
-  virtual QgsVectorColorRampV2* clone() const = 0;
+  virtual QgsVectorColorRampV2* clone() const = 0 /Factory/;
 
   virtual QgsStringMap properties() const = 0;
 
 };
 
+class QgsVectorGradientColorRampV2 : QgsVectorColorRampV2
+{
+  public:
+    QgsVectorGradientColorRampV2( QColor color1 = QColor(0,0,255),
+                                  QColor color2 = QColor(0,255,0) );
+
+    static QgsVectorColorRampV2* create( const QgsStringMap& properties = QgsStringMap() ) /Factory/;
+
+    virtual QColor color( double value ) const;
+
+    virtual QString type() const;
+
+    virtual QgsVectorColorRampV2* clone() const /Factory/;
+
+    virtual QgsStringMap properties() const;
+
+    QColor color1() const;
+    QColor color2() const;
+
+    void setColor1( QColor color );
+    void setColor2( QColor color );
+
+    typedef QMultiMap<double, QColor> StopsMap;
+
+    void setStops(const StopsMap& stops);
+    const StopsMap& stops() const;
+
+};
+
+
+
 //////////
 
 class QgsSymbologyV2Conversion
@@ -858,3 +898,5 @@
     QgsRendererV2Registry();
     ~QgsRendererV2Registry();
 };
+
+///////////////

Modified: trunk/qgis/src/core/symbology-ng/qgsvectorcolorrampv2.cpp
===================================================================
--- trunk/qgis/src/core/symbology-ng/qgsvectorcolorrampv2.cpp	2010-04-20 11:51:20 UTC (rev 13332)
+++ trunk/qgis/src/core/symbology-ng/qgsvectorcolorrampv2.cpp	2010-04-20 12:25:42 UTC (rev 13333)
@@ -18,21 +18,68 @@
     color1 = QgsSymbolLayerV2Utils::decodeColor( props["color1"] );
   if ( props.contains( "color2" ) )
     color2 = QgsSymbolLayerV2Utils::decodeColor( props["color2"] );
-  return new QgsVectorGradientColorRampV2( color1, color2 );
+
+  StopsMap stops;
+  if ( props.contains( "stops" ) )
+  {
+    foreach( QString stop, props["stops"].split( ':' ) )
+    {
+      int i = stop.indexOf( ';' );
+      if ( i == -1 ) continue;
+
+      QColor c = QgsSymbolLayerV2Utils::decodeColor( stop.mid( i + 1 ) );
+      stops.insert( stop.left( i ).toDouble(), c );
+    }
+  }
+
+  QgsVectorGradientColorRampV2* r = new QgsVectorGradientColorRampV2( color1, color2 );
+  r->setStops( stops );
+  return r;
 }
 
-QColor QgsVectorGradientColorRampV2::color( double value ) const
+static QColor _interpolate( QColor c1, QColor c2, double value )
 {
-  int r = ( int )( mColor1.red() + value * ( mColor2.red() - mColor1.red() ) );
-  int g = ( int )( mColor1.green() + value * ( mColor2.green() - mColor1.green() ) );
-  int b = ( int )( mColor1.blue() + value * ( mColor2.blue() - mColor1.blue() ) );
+  int r = ( int )( c1.red() + value * ( c2.red() - c1.red() ) );
+  int g = ( int )( c1.green() + value * ( c2.green() - c1.green() ) );
+  int b = ( int )( c1.blue() + value * ( c2.blue() - c1.blue() ) );
 
   return QColor::fromRgb( r, g, b );
 }
 
+QColor QgsVectorGradientColorRampV2::color( double value ) const
+{
+  if ( mStops.isEmpty() )
+  {
+    return _interpolate( mColor1, mColor2, value );
+  }
+  else
+  {
+    double lower = 0, upper;
+    QColor c1 = mColor1, c2;
+    for ( StopsMap::const_iterator it = mStops.begin(); it != mStops.end(); ++it )
+    {
+      if ( it.key() >= value )
+      {
+        upper = it.key();
+        c2 = it.value();
+
+        return upper == lower ? c1 : _interpolate( c1, c2, ( value - lower ) / ( upper - lower ) );
+      }
+      lower = it.key();
+      c1 = it.value();
+    }
+
+    upper = 1;
+    c2 = mColor2;
+    return upper == lower ? c1 : _interpolate( c1, c2, ( value - lower ) / ( upper - lower ) );
+  }
+}
+
 QgsVectorColorRampV2* QgsVectorGradientColorRampV2::clone() const
 {
-  return new QgsVectorGradientColorRampV2( mColor1, mColor2 );
+  QgsVectorGradientColorRampV2* r = new QgsVectorGradientColorRampV2( mColor1, mColor2 );
+  r->setStops( mStops );
+  return r;
 }
 
 QgsStringMap QgsVectorGradientColorRampV2::properties() const
@@ -40,6 +87,15 @@
   QgsStringMap map;
   map["color1"] = QgsSymbolLayerV2Utils::encodeColor( mColor1 );
   map["color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 );
+  if ( !mStops.isEmpty() )
+  {
+    QStringList lst;
+    for ( StopsMap::const_iterator it = mStops.begin(); it != mStops.end(); ++it )
+    {
+      lst.append( QString( "%1;%2" ).arg( it.key() ).arg( QgsSymbolLayerV2Utils::encodeColor( it.value() ) ) );
+    }
+    map["stops"] = lst.join( ":" );
+  }
   return map;
 }
 

Modified: trunk/qgis/src/core/symbology-ng/qgsvectorcolorrampv2.h
===================================================================
--- trunk/qgis/src/core/symbology-ng/qgsvectorcolorrampv2.h	2010-04-20 11:51:20 UTC (rev 13332)
+++ trunk/qgis/src/core/symbology-ng/qgsvectorcolorrampv2.h	2010-04-20 12:25:42 UTC (rev 13333)
@@ -46,8 +46,14 @@
     void setColor1( QColor color ) { mColor1 = color; }
     void setColor2( QColor color ) { mColor2 = color; }
 
+    typedef QMap<double, QColor> StopsMap;
+
+    void setStops( const StopsMap& stops ) { mStops = stops; }
+    const StopsMap& stops() const { return mStops; }
+
   protected:
     QColor mColor1, mColor2;
+    StopsMap mStops;
 };
 
 #define DEFAULT_RANDOM_COUNT   10

Modified: trunk/qgis/src/gui/symbology-ng/qgsvectorgradientcolorrampv2dialog.cpp
===================================================================
--- trunk/qgis/src/gui/symbology-ng/qgsvectorgradientcolorrampv2dialog.cpp	2010-04-20 11:51:20 UTC (rev 13332)
+++ trunk/qgis/src/gui/symbology-ng/qgsvectorgradientcolorrampv2dialog.cpp	2010-04-20 12:25:42 UTC (rev 13333)
@@ -4,8 +4,9 @@
 #include "qgsvectorcolorrampv2.h"
 
 #include <QColorDialog>
+#include <QInputDialog>
+#include <QPainter>
 
-
 QgsVectorGradientColorRampV2Dialog::QgsVectorGradientColorRampV2Dialog( QgsVectorGradientColorRampV2* ramp, QWidget* parent )
     : QDialog( parent ), mRamp( ramp )
 {
@@ -15,11 +16,54 @@
   connect( btnColor1, SIGNAL( clicked() ), this, SLOT( setColor1() ) );
   connect( btnColor2, SIGNAL( clicked() ), this, SLOT( setColor2() ) );
 
+  // handle stops
+  QgsVectorGradientColorRampV2::StopsMap stops = ramp->stops();
+  groupStops->setChecked( !stops.isEmpty() );
+
+  QgsVectorGradientColorRampV2::StopsMap::iterator i;
+  QList<QTreeWidgetItem *> items;
+  for ( i = stops.begin(); i != stops.end(); ++i )
+  {
+    QStringList lst;
+    lst << "." << QString::number( i.key()*100, 'f', 0 );
+    QTreeWidgetItem* item = new QTreeWidgetItem( lst );
+
+    setStopColor( item, i.value() );
+    item->setData( 0, StopOffsetRole, i.key() );
+
+    items.append( item );
+  }
+  treeStops->insertTopLevelItems( 0, items );
+  treeStops->resizeColumnToContents( 0 );
+  treeStops->sortByColumn( 1, Qt::AscendingOrder );
+  treeStops->setSortingEnabled( true );
+
+  connect( groupStops, SIGNAL( toggled( bool ) ), this, SLOT( toggledStops( bool ) ) );
+  connect( btnAddStop, SIGNAL( clicked() ), this, SLOT( addStop() ) );
+  connect( btnRemoveStop, SIGNAL( clicked() ), this, SLOT( removeStop() ) );
+  connect( treeStops, SIGNAL( itemDoubleClicked( QTreeWidgetItem*, int ) ), this, SLOT( stopDoubleClicked( QTreeWidgetItem*, int ) ) );
+
   updatePreview();
 }
 
 void QgsVectorGradientColorRampV2Dialog::updatePreview()
 {
+  // update ramp stops from the tree widget
+  QgsVectorGradientColorRampV2::StopsMap map;
+  if ( groupStops->isChecked() )
+  {
+    int count = treeStops->topLevelItemCount();
+    for ( int i = 0; i < count; i++ )
+    {
+      QTreeWidgetItem* item = treeStops->topLevelItem( i );
+      double key = item->data( 0, StopOffsetRole ).toDouble();
+      QColor c = item->data( 0, StopColorRole ).value<QColor>();
+      map.insert( key, c );
+    }
+  }
+  mRamp->setStops( map );
+
+  // generate the preview
   QSize size( 300, 40 );
   lblPreview->setPixmap( QgsSymbolLayerV2Utils::colorRampPreviewPixmap( mRamp, size ) );
 
@@ -44,3 +88,100 @@
   mRamp->setColor2( color );
   updatePreview();
 }
+
+void QgsVectorGradientColorRampV2Dialog::setStopColor( QTreeWidgetItem* item, QColor color )
+{
+  QSize iconSize( 16, 16 );
+  QPixmap pixmap( iconSize );
+  pixmap.fill( QColor( 0, 0, 0, 0 ) );
+  QRect rect( 1, 1, iconSize.width() - 2, iconSize.height() - 2 );
+
+  // draw a slightly rounded rectangle
+  QPainter p;
+  p.begin( &pixmap );
+  p.setPen( Qt::NoPen );
+  p.setRenderHint( QPainter::Antialiasing );
+  p.setBrush( color );
+  p.drawRoundedRect( rect, 4, 4 );
+  p.end();
+
+  item->setIcon( 0, QIcon( pixmap ) );
+  item->setData( 0, StopColorRole, color );
+  item->setText( 0, color.name() );
+}
+
+void QgsVectorGradientColorRampV2Dialog::stopDoubleClicked( QTreeWidgetItem* item, int column )
+{
+  if ( column == 0 )
+  {
+    QColor color = QColorDialog::getColor( item->data( 0, StopColorRole ).value<QColor>(), this );
+    if ( !color.isValid() )
+      return;
+    setStopColor( item, color );
+
+    updatePreview();
+  }
+  else
+  {
+    bool ok;
+    double key = item->data( 0, StopOffsetRole ).toDouble();
+    int val = ( int )( key * 100 );
+    val = QInputDialog::getInt( this, tr( "Offset of the stop" ),
+                                tr( "Please enter offset in percents (%) of the new stop" ),
+                                val, 0, 100, 1, &ok );
+    if ( !ok )
+      return;
+
+    double newkey = val / 100.0;
+    item->setText( 1, QString::number( val ) );
+
+    item->setData( 0, StopOffsetRole, newkey );
+
+    updatePreview();
+  }
+}
+
+void QgsVectorGradientColorRampV2Dialog::addStop()
+{
+  QColor color = QColorDialog::getColor( QColor(), this );
+  if ( !color.isValid() )
+    return;
+
+  bool ok;
+  int val = 50;
+  val = QInputDialog::getInt( this, tr( "Offset of the stop" ),
+                              tr( "Please enter offset in percents (%) of the new stop" ),
+                              val, 0, 100, 1, &ok );
+  if ( !ok )
+    return;
+
+  double key = val / 100.0;
+  QStringList lst;
+  lst << "." << QString::number( val, 'f', 0 );
+  QTreeWidgetItem* item = new QTreeWidgetItem( lst );
+
+  setStopColor( item, color );
+  item->setData( 0, StopOffsetRole, key );
+
+  treeStops->addTopLevelItem( item );
+
+  treeStops->resizeColumnToContents( 0 );
+
+  updatePreview();
+}
+
+void QgsVectorGradientColorRampV2Dialog::removeStop()
+{
+  QTreeWidgetItem* item = treeStops->currentItem();
+  if ( !item )
+    return;
+  int index = treeStops->indexOfTopLevelItem( item );
+  treeStops->takeTopLevelItem( index );
+
+  updatePreview();
+}
+
+void QgsVectorGradientColorRampV2Dialog::toggledStops( bool on )
+{
+  updatePreview();
+}

Modified: trunk/qgis/src/gui/symbology-ng/qgsvectorgradientcolorrampv2dialog.h
===================================================================
--- trunk/qgis/src/gui/symbology-ng/qgsvectorgradientcolorrampv2dialog.h	2010-04-20 11:51:20 UTC (rev 13332)
+++ trunk/qgis/src/gui/symbology-ng/qgsvectorgradientcolorrampv2dialog.h	2010-04-20 12:25:42 UTC (rev 13333)
@@ -19,11 +19,21 @@
     void setColor1();
     void setColor2();
 
+    void toggledStops( bool on );
+    void addStop();
+    void removeStop();
+
+    void stopDoubleClicked( QTreeWidgetItem* item, int column );
+
   protected:
 
     void updatePreview();
+    void setStopColor( QTreeWidgetItem* item, QColor color );
 
     QgsVectorGradientColorRampV2* mRamp;
+
+    static const int StopColorRole = Qt::UserRole + 1;
+    static const int StopOffsetRole = Qt::UserRole + 2;
 };
 
 #endif

Modified: trunk/qgis/src/ui/qgsvectorgradientcolorrampv2dialogbase.ui
===================================================================
--- trunk/qgis/src/ui/qgsvectorgradientcolorrampv2dialogbase.ui	2010-04-20 11:51:20 UTC (rev 13332)
+++ trunk/qgis/src/ui/qgsvectorgradientcolorrampv2dialogbase.ui	2010-04-20 12:25:42 UTC (rev 13333)
@@ -7,13 +7,13 @@
     <x>0</x>
     <y>0</y>
     <width>400</width>
-    <height>300</height>
+    <height>364</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Gradient color ramp</string>
   </property>
-  <layout class="QVBoxLayout">
+  <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <layout class="QGridLayout">
      <item row="0" column="0">
@@ -63,20 +63,53 @@
     </layout>
    </item>
    <item>
-    <spacer>
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
+    <widget class="QGroupBox" name="groupStops">
+     <property name="title">
+      <string>Multiple stops</string>
      </property>
-     <property name="sizeType">
-      <enum>QSizePolicy::Preferred</enum>
+     <property name="checkable">
+      <bool>true</bool>
      </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>20</width>
-       <height>40</height>
-      </size>
+     <property name="checked">
+      <bool>false</bool>
      </property>
-    </spacer>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="1">
+       <widget class="QPushButton" name="btnAddStop">
+        <property name="text">
+         <string>Add stop</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QPushButton" name="btnRemoveStop">
+        <property name="text">
+         <string>Remove stop</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="0" rowspan="2">
+       <widget class="QTreeWidget" name="treeStops">
+        <property name="rootIsDecorated">
+         <bool>false</bool>
+        </property>
+        <property name="columnCount">
+         <number>2</number>
+        </property>
+        <column>
+         <property name="text">
+          <string>Color</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>Offset</string>
+         </property>
+        </column>
+       </widget>
+      </item>
+     </layout>
+    </widget>
    </item>
    <item>
     <widget class="QGroupBox" name="groupBox">



More information about the QGIS-commit mailing list