QGIS API Documentation  2.15.0-Master (af20121)
qgscategorizedsymbolrendererv2widget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscategorizedsymbolrendererv2widget.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
19 
20 #include "qgssymbolv2.h"
21 #include "qgssymbollayerv2utils.h"
22 #include "qgsvectorcolorrampv2.h"
23 #include "qgsstylev2.h"
24 
27 
28 #include "qgsvectorlayer.h"
29 
30 #include "qgsproject.h"
31 #include "qgsexpression.h"
32 #include "qgsmapcanvas.h"
33 
34 #include <QKeyEvent>
35 #include <QMenu>
36 #include <QMessageBox>
37 #include <QStandardItemModel>
38 #include <QStandardItem>
39 #include <QPen>
40 #include <QPainter>
41 #include <QFileDialog>
42 
44 
45 QgsCategorizedSymbolRendererV2Model::QgsCategorizedSymbolRendererV2Model( QObject * parent ) : QAbstractItemModel( parent )
46  , mRenderer( nullptr )
47  , mMimeFormat( "application/x-qgscategorizedsymbolrendererv2model" )
48 {
49 }
50 
51 void QgsCategorizedSymbolRendererV2Model::setRenderer( QgsCategorizedSymbolRendererV2* renderer )
52 {
53  if ( mRenderer )
54  {
55  beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
56  mRenderer = nullptr;
57  endRemoveRows();
58  }
59  if ( renderer )
60  {
61  beginInsertRows( QModelIndex(), 0, renderer->categories().size() - 1 );
62  mRenderer = renderer;
63  endInsertRows();
64  }
65 }
66 
67 void QgsCategorizedSymbolRendererV2Model::addCategory( const QgsRendererCategoryV2 &cat )
68 {
69  if ( !mRenderer ) return;
70  int idx = mRenderer->categories().size();
71  beginInsertRows( QModelIndex(), idx, idx );
72  mRenderer->addCategory( cat );
73  endInsertRows();
74 }
75 
76 QgsRendererCategoryV2 QgsCategorizedSymbolRendererV2Model::category( const QModelIndex &index )
77 {
78  if ( !mRenderer )
79  {
80  return QgsRendererCategoryV2();
81  }
82  const QgsCategoryList& catList = mRenderer->categories();
83  int row = index.row();
84  if ( row >= catList.size() )
85  {
86  return QgsRendererCategoryV2();
87  }
88  return catList.at( row );
89 }
90 
91 
92 Qt::ItemFlags QgsCategorizedSymbolRendererV2Model::flags( const QModelIndex & index ) const
93 {
94  if ( !index.isValid() )
95  {
96  return Qt::ItemIsDropEnabled;
97  }
98 
99  Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
100  if ( index.column() == 1 || index.column() == 2 )
101  {
102  flags |= Qt::ItemIsEditable;
103  }
104  return flags;
105 }
106 
107 Qt::DropActions QgsCategorizedSymbolRendererV2Model::supportedDropActions() const
108 {
109  return Qt::MoveAction;
110 }
111 
112 QVariant QgsCategorizedSymbolRendererV2Model::data( const QModelIndex &index, int role ) const
113 {
114  if ( !index.isValid() || !mRenderer )
115  return QVariant();
116 
117  const QgsRendererCategoryV2 category = mRenderer->categories().value( index.row() );
118 
119  if ( role == Qt::CheckStateRole && index.column() == 0 )
120  {
121  return category.renderState() ? Qt::Checked : Qt::Unchecked;
122  }
123  else if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
124  {
125  switch ( index.column() )
126  {
127  case 1:
128  return category.value().toString();
129  case 2:
130  return category.label();
131  default:
132  return QVariant();
133  }
134  }
135  else if ( role == Qt::DecorationRole && index.column() == 0 && category.symbol() )
136  {
137  return QgsSymbolLayerV2Utils::symbolPreviewIcon( category.symbol(), QSize( 16, 16 ) );
138  }
139  else if ( role == Qt::TextAlignmentRole )
140  {
141  return ( index.column() == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft;
142  }
143  else if ( role == Qt::EditRole )
144  {
145  switch ( index.column() )
146  {
147  case 1:
148  return category.value();
149  case 2:
150  return category.label();
151  default:
152  return QVariant();
153  }
154  }
155 
156  return QVariant();
157 }
158 
159 bool QgsCategorizedSymbolRendererV2Model::setData( const QModelIndex & index, const QVariant & value, int role )
160 {
161  if ( !index.isValid() )
162  return false;
163 
164  if ( index.column() == 0 && role == Qt::CheckStateRole )
165  {
166  mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
167  emit dataChanged( index, index );
168  return true;
169  }
170 
171  if ( role != Qt::EditRole )
172  return false;
173 
174  switch ( index.column() )
175  {
176  case 1: // value
177  {
178  // try to preserve variant type for this value
179  QVariant val;
180  switch ( mRenderer->categories().value( index.row() ).value().type() )
181  {
182  case QVariant::Int:
183  val = value.toInt();
184  break;
185  case QVariant::Double:
186  val = value.toDouble();
187  break;
188  default:
189  val = value.toString();
190  break;
191  }
192  mRenderer->updateCategoryValue( index.row(), val );
193  break;
194  }
195  case 2: // label
196  mRenderer->updateCategoryLabel( index.row(), value.toString() );
197  break;
198  default:
199  return false;
200  }
201 
202  emit dataChanged( index, index );
203  return true;
204 }
205 
206 QVariant QgsCategorizedSymbolRendererV2Model::headerData( int section, Qt::Orientation orientation, int role ) const
207 {
208  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
209  {
210  QStringList lst;
211  lst << tr( "Symbol" ) << tr( "Value" ) << tr( "Legend" );
212  return lst.value( section );
213  }
214  return QVariant();
215 }
216 
217 int QgsCategorizedSymbolRendererV2Model::rowCount( const QModelIndex &parent ) const
218 {
219  if ( parent.isValid() || !mRenderer )
220  {
221  return 0;
222  }
223  return mRenderer->categories().size();
224 }
225 
226 int QgsCategorizedSymbolRendererV2Model::columnCount( const QModelIndex & index ) const
227 {
228  Q_UNUSED( index );
229  return 3;
230 }
231 
232 QModelIndex QgsCategorizedSymbolRendererV2Model::index( int row, int column, const QModelIndex &parent ) const
233 {
234  if ( hasIndex( row, column, parent ) )
235  {
236  return createIndex( row, column );
237  }
238  return QModelIndex();
239 }
240 
241 QModelIndex QgsCategorizedSymbolRendererV2Model::parent( const QModelIndex &index ) const
242 {
243  Q_UNUSED( index );
244  return QModelIndex();
245 }
246 
247 QStringList QgsCategorizedSymbolRendererV2Model::mimeTypes() const
248 {
249  QStringList types;
250  types << mMimeFormat;
251  return types;
252 }
253 
254 QMimeData *QgsCategorizedSymbolRendererV2Model::mimeData( const QModelIndexList &indexes ) const
255 {
256  QMimeData *mimeData = new QMimeData();
257  QByteArray encodedData;
258 
259  QDataStream stream( &encodedData, QIODevice::WriteOnly );
260 
261  // Create list of rows
262  Q_FOREACH ( const QModelIndex &index, indexes )
263  {
264  if ( !index.isValid() || index.column() != 0 )
265  continue;
266 
267  stream << index.row();
268  }
269  mimeData->setData( mMimeFormat, encodedData );
270  return mimeData;
271 }
272 
273 bool QgsCategorizedSymbolRendererV2Model::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
274 {
275  Q_UNUSED( row );
276  Q_UNUSED( column );
277  if ( action != Qt::MoveAction ) return true;
278 
279  if ( !data->hasFormat( mMimeFormat ) ) return false;
280 
281  QByteArray encodedData = data->data( mMimeFormat );
282  QDataStream stream( &encodedData, QIODevice::ReadOnly );
283 
284  QVector<int> rows;
285  while ( !stream.atEnd() )
286  {
287  int r;
288  stream >> r;
289  rows.append( r );
290  }
291 
292  int to = parent.row();
293  // to is -1 if dragged outside items, i.e. below any item,
294  // then move to the last position
295  if ( to == -1 ) to = mRenderer->categories().size(); // out of rang ok, will be decreased
296  for ( int i = rows.size() - 1; i >= 0; i-- )
297  {
298  QgsDebugMsg( QString( "move %1 to %2" ).arg( rows[i] ).arg( to ) );
299  int t = to;
300  // moveCategory first removes and then inserts
301  if ( rows[i] < t ) t--;
302  mRenderer->moveCategory( rows[i], t );
303  // current moved under another, shift its index up
304  for ( int j = 0; j < i; j++ )
305  {
306  if ( to < rows[j] && rows[i] > rows[j] ) rows[j] += 1;
307  }
308  // removed under 'to' so the target shifted down
309  if ( rows[i] < to ) to--;
310  }
311  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
312  emit rowsMoved();
313  return false;
314 }
315 
316 void QgsCategorizedSymbolRendererV2Model::deleteRows( QList<int> rows )
317 {
318  qSort( rows ); // list might be unsorted, depending on how the user selected the rows
319  for ( int i = rows.size() - 1; i >= 0; i-- )
320  {
321  beginRemoveRows( QModelIndex(), rows[i], rows[i] );
322  mRenderer->deleteCategory( rows[i] );
323  endRemoveRows();
324  }
325 }
326 
327 void QgsCategorizedSymbolRendererV2Model::removeAllRows()
328 {
329  beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
330  mRenderer->deleteAllCategories();
331  endRemoveRows();
332 }
333 
334 void QgsCategorizedSymbolRendererV2Model::sort( int column, Qt::SortOrder order )
335 {
336  QgsDebugMsg( "Entered" );
337  if ( column == 0 )
338  {
339  return;
340  }
341  if ( column == 1 )
342  {
343  mRenderer->sortByValue( order );
344  }
345  else if ( column == 2 )
346  {
347  mRenderer->sortByLabel( order );
348  }
349  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
350  QgsDebugMsg( "Done" );
351 }
352 
353 void QgsCategorizedSymbolRendererV2Model::updateSymbology()
354 {
355  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
356 }
357 
358 // ------------------------------ View style --------------------------------
359 QgsCategorizedSymbolRendererV2ViewStyle::QgsCategorizedSymbolRendererV2ViewStyle( QStyle* style )
360  : QProxyStyle( style )
361 {}
362 
363 void QgsCategorizedSymbolRendererV2ViewStyle::drawPrimitive( PrimitiveElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget ) const
364 {
365  if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
366  {
367  QStyleOption opt( *option );
368  opt.rect.setLeft( 0 );
369  // draw always as line above, because we move item to that index
370  opt.rect.setHeight( 0 );
371  if ( widget ) opt.rect.setRight( widget->width() );
372  QProxyStyle::drawPrimitive( element, &opt, painter, widget );
373  return;
374  }
375  QProxyStyle::drawPrimitive( element, option, painter, widget );
376 }
377 
379 
380 // ------------------------------ Widget ------------------------------------
382 {
383  return new QgsCategorizedSymbolRendererV2Widget( layer, style, renderer );
384 }
385 
386 static QgsExpressionContext _getExpressionContext( const void* context )
387 {
389 
390  QgsExpressionContext expContext;
394 
395  if ( widget->mapCanvas() )
396  {
399  }
400  else
401  {
403  }
404 
405  if ( widget->vectorLayer() )
406  expContext << QgsExpressionContextUtils::layerScope( widget->vectorLayer() );
407 
408  return expContext;
409 }
410 
412  : QgsRendererV2Widget( layer, style )
413  , mRenderer( nullptr )
414  , mModel( nullptr )
415 {
416 
417  // try to recognize the previous renderer
418  // (null renderer means "no previous renderer")
419  if ( renderer )
420  {
422  }
423  if ( !mRenderer )
424  {
426  }
427 
428  QString attrName = mRenderer->classAttribute();
429  mOldClassificationAttribute = attrName;
430 
431  // setup user interface
432  setupUi( this );
433 
434  mExpressionWidget->setLayer( mLayer );
435 
436  cboCategorizedColorRamp->populate( mStyle );
437  int randomIndex = cboCategorizedColorRamp->findText( tr( "Random colors" ) );
438  if ( randomIndex != -1 )
439  {
440  cboCategorizedColorRamp->setCurrentIndex( randomIndex );
441  }
442 
443  // set project default color ramp
444  QString defaultColorRamp = QgsProject::instance()->readEntry( "DefaultStyles", "/ColorRamp", "" );
445  if ( defaultColorRamp != "" )
446  {
447  int index = cboCategorizedColorRamp->findText( defaultColorRamp, Qt::MatchCaseSensitive );
448  if ( index >= 0 )
449  cboCategorizedColorRamp->setCurrentIndex( index );
450  }
451 
453 
454  mModel = new QgsCategorizedSymbolRendererV2Model( this );
455  mModel->setRenderer( mRenderer );
456 
457  // update GUI from renderer
459 
460  viewCategories->setModel( mModel );
461  viewCategories->resizeColumnToContents( 0 );
462  viewCategories->resizeColumnToContents( 1 );
463  viewCategories->resizeColumnToContents( 2 );
464 
465  viewCategories->setStyle( new QgsCategorizedSymbolRendererV2ViewStyle( viewCategories->style() ) );
466 
467  connect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
468  connect( mModel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SIGNAL( widgetChanged() ) );
469 
470  connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( categoryColumnChanged( QString ) ) );
471 
472  connect( viewCategories, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( categoriesDoubleClicked( const QModelIndex & ) ) );
473  connect( viewCategories, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
474 
475  connect( btnChangeCategorizedSymbol, SIGNAL( clicked() ), this, SLOT( changeCategorizedSymbol() ) );
476  connect( btnAddCategories, SIGNAL( clicked() ), this, SLOT( addCategories() ) );
477  connect( btnDeleteCategories, SIGNAL( clicked() ), this, SLOT( deleteCategories() ) );
478  connect( btnDeleteAllCategories, SIGNAL( clicked() ), this, SLOT( deleteAllCategories() ) );
479  connect( btnAddCategory, SIGNAL( clicked() ), this, SLOT( addCategory() ) );
480  connect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( applyColorRamp() ) );
481  connect( cboCategorizedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( applyColorRamp() ) );
482  connect( cboCategorizedColorRamp, SIGNAL( sourceRampEdited() ), this, SLOT( applyColorRamp() ) );
483  connect( mButtonEditRamp, SIGNAL( clicked() ), cboCategorizedColorRamp, SLOT( editSourceRamp() ) );
484 
485  // menus for data-defined rotation/size
486  QMenu* advMenu = new QMenu;
487 
488  advMenu->addAction( tr( "Match to saved symbols" ), this, SLOT( matchToSymbolsFromLibrary() ) );
489  advMenu->addAction( tr( "Match to symbols from file..." ), this, SLOT( matchToSymbolsFromXml() ) );
490  advMenu->addAction( tr( "Symbol levels..." ), this, SLOT( showSymbolLevels() ) );
491 
492  btnAdvanced->setMenu( advMenu );
493 
494  mExpressionWidget->registerGetExpressionContextCallback( &_getExpressionContext, this );
495 }
496 
498 {
499  delete mRenderer;
500  delete mModel;
501  delete mCategorizedSymbol;
502 }
503 
505 {
506  // Note: This assumes that the signals for UI element changes have not
507  // yet been connected, so that the updates to color ramp, symbol, etc
508  // don't override existing customisations.
509 
511 
512  //mModel->setRenderer ( mRenderer ); // necessary?
513 
514  // set column
515  QString attrName = mRenderer->classAttribute();
516  mExpressionWidget->setField( attrName );
517 
518  // set source symbol
519  if ( mRenderer->sourceSymbol() )
520  {
521  delete mCategorizedSymbol;
524  }
525 
526  // set source color ramp
527  if ( mRenderer->sourceColorRamp() )
528  {
529  cboCategorizedColorRamp->setSourceColorRamp( mRenderer->sourceColorRamp() );
530  cbxInvertedColorRamp->setChecked( mRenderer->invertedColorRamp() );
531  }
532 
533 }
534 
536 {
537  return mRenderer;
538 }
539 
541 {
542  QList<int> selectedCats = selectedCategories();
543 
544  if ( !selectedCats.isEmpty() )
545  {
546  QgsSymbolV2* newSymbol = mCategorizedSymbol->clone();
547  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
548  dlg.setMapCanvas( mMapCanvas );
549  if ( !dlg.exec() )
550  {
551  delete newSymbol;
552  return;
553  }
554 
555  Q_FOREACH ( int idx, selectedCats )
556  {
557  QgsRendererCategoryV2 category = mRenderer->categories().value( idx );
558 
559  QgsSymbolV2* newCatSymbol = newSymbol->clone();
560  newCatSymbol->setColor( mRenderer->categories()[idx].symbol()->color() );
561  mRenderer->updateCategorySymbol( idx, newCatSymbol );
562  }
563  }
564 }
565 
567 {
568  // When there is a slection, change the selected symbols alone
569  QItemSelectionModel* m = viewCategories->selectionModel();
570  QModelIndexList i = m->selectedRows();
571 
572  if ( m && !i.isEmpty() )
573  {
575  return;
576  }
577 
578  // When there is no selection, change the base mCategorizedSymbol
579  QgsSymbolV2* newSymbol = mCategorizedSymbol->clone();
580 
581  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
582  dlg.setMapCanvas( mMapCanvas );
583  if ( !dlg.exec() || !newSymbol )
584  {
585  delete newSymbol;
586  return;
587  }
588 
589  delete mCategorizedSymbol;
590  mCategorizedSymbol = newSymbol;
592 
594  emit widgetChanged();
595 }
596 
598 {
599  QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( mCategorizedSymbol, btnChangeCategorizedSymbol->iconSize() );
600  btnChangeCategorizedSymbol->setIcon( icon );
601 }
602 
604 {
605 }
606 
608 {
609  mRenderer->setClassAttribute( field );
610  emit widgetChanged();
611 }
612 
614 {
615  if ( idx.isValid() && idx.column() == 0 )
617 }
618 
620 {
621  int catIdx = currentCategoryRow();
623 
624  QgsSymbolV2 *symbol = category.symbol();
625  if ( symbol )
626  {
627  symbol = symbol->clone();
628  }
629  else
630  {
632  }
633 
634  QgsSymbolV2SelectorDialog dlg( symbol, mStyle, mLayer, this );
635  dlg.setMapCanvas( mMapCanvas );
636  if ( !dlg.exec() )
637  {
638  delete symbol;
639  return;
640  }
641 
642  mRenderer->updateCategorySymbol( catIdx, symbol );
643  emit widgetChanged();
644 }
645 
646 static void _createCategories( QgsCategoryList& cats, QList<QVariant>& values, QgsSymbolV2* symbol )
647 {
648  // sort the categories first
649  QgsSymbolLayerV2Utils::sortVariantList( values, Qt::AscendingOrder );
650 
651  int num = values.count();
652 
653  bool hasNull = false;
654 
655  for ( int i = 0; i < num; i++ )
656  {
657  QVariant value = values[i];
658  if ( value.toString().isNull() )
659  {
660  hasNull = true;
661  }
662  QgsSymbolV2* newSymbol = symbol->clone();
663 
664  cats.append( QgsRendererCategoryV2( value, newSymbol, value.toString(), true ) );
665  }
666 
667  // add null (default) value if not exists
668  if ( !hasNull )
669  {
670  QgsSymbolV2* newSymbol = symbol->clone();
671  cats.append( QgsRendererCategoryV2( QVariant( "" ), newSymbol, QString(), true ) );
672  }
673 }
674 
676 {
677  QgsVectorColorRampV2* ramp = cboCategorizedColorRamp->currentColorRamp();
678  if ( !ramp )
679  {
680  if ( cboCategorizedColorRamp->count() == 0 )
681  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
682  else if ( !cboCategorizedColorRamp->createNewColorRampSelected() )
683  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
684  }
685  return ramp;
686 }
687 
688 
690 {
691  QString attrName = mExpressionWidget->currentField();
692  int idx = mLayer->fieldNameIndex( attrName );
693  QList<QVariant> unique_vals;
694  if ( idx == -1 )
695  {
696  // Lets assume it's an expression
697  QgsExpression* expression = new QgsExpression( attrName );
698  QgsExpressionContext context;
703 
704  expression->prepare( &context );
706  QgsFeature feature;
707  while ( fit.nextFeature( feature ) )
708  {
709  context.setFeature( feature );
710  QVariant value = expression->evaluate( &context );
711  if ( unique_vals.contains( value ) )
712  continue;
713  unique_vals << value;
714  }
715  }
716  else
717  {
718  mLayer->uniqueValues( idx, unique_vals );
719  }
720 
721  // ask to abort if too many classes
722  if ( unique_vals.size() >= 1000 )
723  {
724  int res = QMessageBox::warning( nullptr, tr( "High number of classes!" ),
725  tr( "Classification would yield %1 entries which might not be expected. Continue?" ).arg( unique_vals.size() ),
726  QMessageBox::Ok | QMessageBox::Cancel,
727  QMessageBox::Cancel );
728  if ( res == QMessageBox::Cancel )
729  {
730  return;
731  }
732  }
733 
734 #if 0
735  DlgAddCategories dlg( mStyle, createDefaultSymbol(), unique_vals, this );
736  if ( !dlg.exec() )
737  return;
738 #endif
739 
740  QgsCategoryList cats;
741  _createCategories( cats, unique_vals, mCategorizedSymbol );
742  bool deleteExisting = false;
743 
744  if ( !mOldClassificationAttribute.isEmpty() &&
745  attrName != mOldClassificationAttribute &&
747  {
748  int res = QMessageBox::question( this,
749  tr( "Confirm Delete" ),
750  tr( "The classification field was changed from '%1' to '%2'.\n"
751  "Should the existing classes be deleted before classification?" )
752  .arg( mOldClassificationAttribute, attrName ),
753  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
754  if ( res == QMessageBox::Cancel )
755  {
756  return;
757  }
758 
759  deleteExisting = ( res == QMessageBox::Yes );
760  }
761 
762  // First element to apply coloring to
763  bool keepExistingColors = false;
764  if ( !deleteExisting )
765  {
766  QgsCategoryList prevCats = mRenderer->categories();
767  keepExistingColors = !prevCats.isEmpty();
768  for ( int i = 0; i < cats.size(); ++i )
769  {
770  bool contains = false;
771  QVariant value = cats.at( i ).value();
772  for ( int j = 0; j < prevCats.size() && !contains; ++j )
773  {
774  if ( prevCats.at( j ).value() == value )
775  {
776  contains = true;
777  break;
778  }
779  }
780 
781  if ( !contains )
782  prevCats.append( cats.at( i ) );
783  }
784  cats = prevCats;
785  }
786 
787  mOldClassificationAttribute = attrName;
788 
789  // TODO: if not all categories are desired, delete some!
790  /*
791  if (not dlg.readAllCats.isChecked())
792  {
793  cats2 = {}
794  for item in dlg.listCategories.selectedItems():
795  for k,c in cats.iteritems():
796  if item.text() == k.toString():
797  break
798  cats2[k] = c
799  cats = cats2
800  }
801  */
802 
803  // recreate renderer
808  r->setInvertedColorRamp( cbxInvertedColorRamp->isChecked() );
810  if ( ramp ) r->setSourceColorRamp( ramp->clone() );
811 
812  if ( mModel )
813  {
814  mModel->setRenderer( r );
815  }
816  delete mRenderer;
817  mRenderer = r;
818  if ( ! keepExistingColors && ramp ) applyColorRamp();
819  delete ramp;
820  emit widgetChanged();
821 }
822 
824 {
826  if ( ramp )
827  {
828  mRenderer->updateColorRamp( ramp, cbxInvertedColorRamp->isChecked() );
829  }
830  mModel->updateSymbology();
831 }
832 
834 {
835  QModelIndex idx = viewCategories->selectionModel()->currentIndex();
836  if ( !idx.isValid() )
837  return -1;
838  return idx.row();
839 }
840 
842 {
843  QList<int> rows;
844  QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
845 
846  Q_FOREACH ( const QModelIndex& r, selectedRows )
847  {
848  if ( r.isValid() )
849  {
850  rows.append( r.row() );
851  }
852  }
853  return rows;
854 }
855 
857 {
858  QList<int> categoryIndexes = selectedCategories();
859  mModel->deleteRows( categoryIndexes );
860  emit widgetChanged();
861 }
862 
864 {
865  mModel->removeAllRows();
866  emit widgetChanged();
867 }
868 
870 {
871  if ( !mModel ) return;
873  QgsRendererCategoryV2 cat( QString(), symbol, QString(), true );
874  mModel->addCategory( cat );
875  emit widgetChanged();
876 }
877 
879 {
880  mRenderer->setSizeScaleField( fldName );
881  emit widgetChanged();
882 }
883 
885 {
886  mRenderer->setScaleMethod( scaleMethod );
887  emit widgetChanged();
888 }
889 
891 {
893 
894  QItemSelectionModel* m = viewCategories->selectionModel();
895  QModelIndexList selectedIndexes = m->selectedRows( 1 );
896 
897  if ( m && !selectedIndexes.isEmpty() )
898  {
899  const QgsCategoryList& categories = mRenderer->categories();
900  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
901  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
902  {
903  int row = ( *indexIt ).row();
904  QgsSymbolV2* s = categories[row].symbol();
905  if ( s )
906  {
907  selectedSymbols.append( s );
908  }
909  }
910  }
911  return selectedSymbols;
912 }
913 
915 {
916  QgsCategoryList cl;
917 
918  QItemSelectionModel* m = viewCategories->selectionModel();
919  QModelIndexList selectedIndexes = m->selectedRows( 1 );
920 
921  if ( m && !selectedIndexes.isEmpty() )
922  {
923  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
924  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
925  {
926  cl.append( mModel->category( *indexIt ) );
927  }
928  }
929  return cl;
930 }
931 
933 {
935 }
936 
938 {
939  viewCategories->selectionModel()->clear();
940 }
941 
943 {
944  int matched = matchToSymbols( QgsStyleV2::defaultStyle() );
945  if ( matched > 0 )
946  {
947  QMessageBox::information( this, tr( "Matched symbols" ),
948  tr( "Matched %1 categories to symbols." ).arg( matched ) );
949  }
950  else
951  {
952  QMessageBox::warning( this, tr( "Matched symbols" ),
953  tr( "No categories could be matched to symbols in library." ) );
954  }
955 }
956 
958 {
959  if ( !mLayer || !style )
960  return 0;
961 
962  int matched = 0;
963  for ( int catIdx = 0; catIdx < mRenderer->categories().count(); ++catIdx )
964  {
965  QString val = mRenderer->categories().at( catIdx ).value().toString();
966  QgsSymbolV2* symbol = style->symbol( val );
967  if ( symbol &&
968  (( symbol->type() == QgsSymbolV2::Marker && mLayer->geometryType() == QGis::Point )
969  || ( symbol->type() == QgsSymbolV2::Line && mLayer->geometryType() == QGis::Line )
970  || ( symbol->type() == QgsSymbolV2::Fill && mLayer->geometryType() == QGis::Polygon ) ) )
971  {
972  matched++;
973  mRenderer->updateCategorySymbol( catIdx, symbol->clone() );
974  }
975  }
976  mModel->updateSymbology();
977  return matched;
978 }
979 
981 {
982  QSettings settings;
983  QString openFileDir = settings.value( "UI/lastMatchToSymbolsDir", QDir::homePath() ).toString();
984 
985  QString fileName = QFileDialog::getOpenFileName( this, tr( "Match to symbols from file" ), openFileDir,
986  tr( "XML files (*.xml *XML)" ) );
987  if ( fileName.isEmpty() )
988  {
989  return;
990  }
991 
992  QFileInfo openFileInfo( fileName );
993  settings.setValue( "UI/lastMatchToSymbolsDir", openFileInfo.absolutePath() );
994 
995  QgsStyleV2 importedStyle;
996  if ( !importedStyle.importXML( fileName ) )
997  {
998  QMessageBox::warning( this, tr( "Matching error" ),
999  tr( "An error occurred reading file:\n%1" ).arg( importedStyle.errorString() ) );
1000  return;
1001  }
1002 
1003  int matched = matchToSymbols( &importedStyle );
1004  if ( matched > 0 )
1005  {
1006  QMessageBox::information( this, tr( "Matched symbols" ),
1007  tr( "Matched %1 categories to symbols from file." ).arg( matched ) );
1008  }
1009  else
1010  {
1011  QMessageBox::warning( this, tr( "Matched symbols" ),
1012  tr( "No categories could be matched to symbols in file." ) );
1013  }
1014 }
1015 
1017 {
1018  if ( !event )
1019  {
1020  return;
1021  }
1022 
1023  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1024  {
1025  mCopyBuffer.clear();
1026  mCopyBuffer = selectedCategoryList();
1027  }
1028  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1029  {
1030  QgsCategoryList::const_iterator rIt = mCopyBuffer.constBegin();
1031  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1032  {
1033  mModel->addCategory( *rIt );
1034  }
1035  }
1036 }
void customContextMenuRequested(const QPoint &pos)
void matchToSymbolsFromLibrary()
Replaces category symbols with the symbols from the users&#39; symbol library that have a matching name...
static void sortVariantList(QList< QVariant > &list, Qt::SortOrder order)
Sorts the passed list in requested order.
Class for parsing and evaluation of expressions (formerly called "search strings").
void showSymbolLevelsDialog(QgsFeatureRendererV2 *r)
show a dialog with renderer&#39;s symbol level settings
void clear()
Wrapper for iterator of features from vector data provider or vector layer.
static unsigned index
void widgetChanged()
Emmited when something on the widget has changed.
void setupUi(QWidget *widget)
QByteArray data(const QString &mimeType) const
int matchToSymbols(QgsStyleV2 *style)
Replaces category symbols with the symbols from a style that have a matching name.
const QgsCategoryList & categories() const
Q_DECL_DEPRECATED QVariant evaluate(const QgsFeature *f)
Evaluate the feature and return the result.
bool importXML(const QString &filename)
Imports the symbols and colorramps into the default style database from the given XML file...
QgsVectorColorRampV2 * sourceColorRamp()
Returns the source color ramp, from which each categories&#39; color is derived.
void append(const T &value)
SymbolType type() const
Definition: qgssymbolv2.h:104
static QgsExpressionContextScope * atlasScope(const QgsAtlasComposition *atlas)
Creates a new scope which contains variables and functions relating to a QgsAtlasComposition.
Q_DECL_DEPRECATED bool prepare(const QgsFields &fields)
Get the expression ready for evaluation - find out column indexes.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
virtual bool hasFormat(const QString &mimeType) const
void uniqueValues(int index, QList< QVariant > &uniqueValues, int limit=-1)
Returns unique values for column.
void setSourceSymbol(QgsSymbolV2 *sym)
Sets the source symbol for the renderer, which is the base symbol used for the each categories&#39; symbo...
virtual QgsSymbolV2 * clone() const =0
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
QStyle * style() const
const T & at(int i) const
void addAction(QAction *action)
static QgsRendererV2Widget * create(QgsVectorLayer *layer, QgsStyleV2 *style, QgsFeatureRendererV2 *renderer)
QgsMapCanvas * mMapCanvas
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
int exec()
Line symbol.
Definition: qgssymbolv2.h:79
const QPixmap * icon() const
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
virtual QgsFeatureRendererV2 * renderer() override
return pointer to the renderer (no transfer of ownership)
QString homePath()
QString tr(const char *sourceText, const char *disambiguation, int n)
StandardButton information(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
Sets the source color ramp.
Marker symbol.
Definition: qgssymbolv2.h:78
int size() const
bool isNull() const
T value(int i) const
The QgsMapSettings class contains configuration for rendering of the map.
void setValue(const QString &key, const QVariant &value)
void updateSymbols(QgsSymbolV2 *sym)
Update all the symbols but leave categories and colors.
void setColor(const QColor &color)
bool isValid() const
QString readEntry(const QString &scope, const QString &key, const QString &def=QString::null, bool *ok=nullptr) const
QList< QgsRendererCategoryV2 > QgsCategoryList
static QIcon symbolPreviewIcon(QgsSymbolV2 *symbol, QSize size)
int count(const T &value) const
QgsVectorLayer * mLayer
void append(const T &value)
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
int toInt(bool *ok) const
StandardButton question(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
virtual QgsVectorColorRampV2 * clone() const =0
Creates a clone of the color ramp.
void changeSelectedSymbols()
change the selected symbols alone for the change button, if there is a selection
bool isEmpty() const
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static QgsStyleV2 * defaultStyle()
return default application-wide style
Definition: qgsstylev2.cpp:51
bool isEmpty() const
const QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
QModelIndexList selectedRows(int column) const
int row() const
const QgsVectorLayer * vectorLayer() const
Returns the vector layer associated with the widget.
QGis::GeometryType geometryType() const
Returns point, line or polygon.
Single scope for storing variables and functions for use within a QgsExpressionContext.
QgsCategorizedSymbolRendererV2Widget(QgsVectorLayer *layer, QgsStyleV2 *style, QgsFeatureRendererV2 *renderer)
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
static QgsCategorizedSymbolRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsCategorizedSymbolRendererV2 from an existing renderer.
void updateColorRamp(QgsVectorColorRampV2 *ramp, bool inverted=false)
Update the color ramp used and all symbols colors.
int key() const
QgsSymbolV2 * symbol(const QString &name)
return a NEW copy of symbol
Definition: qgsstylev2.cpp:164
bool contains(const T &value) const
static void _createCategories(QgsCategoryList &cats, QList< QVariant > &values, QgsSymbolV2 *symbol)
void contextMenuViewCategories(QPoint p)
QList< int > selectedCategories()
return a list of indexes for the categories unders selection
QVariant value(const QString &key, const QVariant &defaultValue) const
QgsSymbolV2 * sourceSymbol()
Returns the renderer&#39;s source symbol, which is the base symbol used for the each categories&#39; symbol b...
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object...
QgsExpressionContextScope & expressionContextScope()
Returns a reference to the expression context scope for the map canvas.
Definition: qgsmapcanvas.h:456
static QgsSymbolV2 * defaultSymbol(QGis::GeometryType geomType)
return new default symbol for specified geometry type
ScaleMethod
Scale method.
Definition: qgssymbolv2.h:87
typedef DropActions
void scaleMethodChanged(QgsSymbolV2::ScaleMethod scaleMethod)
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:388
bool updateCategorySymbol(int catIndex, QgsSymbolV2 *symbol)
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the dialog.
Fill symbol.
Definition: qgssymbolv2.h:80
StandardButton critical(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
int column() const
Base class for renderer settings widgets.
static QgsExpressionContext _getExpressionContext(const void *context)
QList< QgsSymbolV2 * > selectedSymbols() override
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
StandardButton warning(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
double toDouble(bool *ok) const
int currentCategoryRow()
return row index for the currently selected category (-1 if on no selection)
void setData(const QString &mimeType, const QByteArray &data)
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFlags< QFileDialog::Option > options)
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
QgsSymbolV2::ScaleMethod scaleMethod() const
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
const_iterator constEnd() const
bool nextFeature(QgsFeature &f)
const_iterator constBegin() const
void matchToSymbolsFromXml()
Prompts for selection of an xml file, then replaces category symbols with the symbols from the XML fi...
Type type() const
QString absolutePath() const
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const
int size() const
Represents a vector layer which manages a vector based data sets.
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
Abstract base class for color ramps.
QString toString() const
virtual bool event(QEvent *event)
void setSizeScaleField(const QString &fieldOrExpression)
QString errorString()
return last error from load/save operation
Definition: qgsstylev2.h:285
typedef ItemFlags