QGIS API Documentation
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 
469  connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( categoryColumnChanged( QString ) ) );
470 
471  connect( viewCategories, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( categoriesDoubleClicked( const QModelIndex & ) ) );
472  connect( viewCategories, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
473 
474  connect( btnChangeCategorizedSymbol, SIGNAL( clicked() ), this, SLOT( changeCategorizedSymbol() ) );
475  connect( btnAddCategories, SIGNAL( clicked() ), this, SLOT( addCategories() ) );
476  connect( btnDeleteCategories, SIGNAL( clicked() ), this, SLOT( deleteCategories() ) );
477  connect( btnDeleteAllCategories, SIGNAL( clicked() ), this, SLOT( deleteAllCategories() ) );
478  connect( btnAddCategory, SIGNAL( clicked() ), this, SLOT( addCategory() ) );
479  connect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( applyColorRamp() ) );
480  connect( cboCategorizedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( applyColorRamp() ) );
481  connect( cboCategorizedColorRamp, SIGNAL( sourceRampEdited() ), this, SLOT( applyColorRamp() ) );
482  connect( mButtonEditRamp, SIGNAL( clicked() ), cboCategorizedColorRamp, SLOT( editSourceRamp() ) );
483 
484  // menus for data-defined rotation/size
485  QMenu* advMenu = new QMenu;
486 
487  advMenu->addAction( tr( "Match to saved symbols" ), this, SLOT( matchToSymbolsFromLibrary() ) );
488  advMenu->addAction( tr( "Match to symbols from file..." ), this, SLOT( matchToSymbolsFromXml() ) );
489  advMenu->addAction( tr( "Symbol levels..." ), this, SLOT( showSymbolLevels() ) );
490 
491  btnAdvanced->setMenu( advMenu );
492 
493  mExpressionWidget->registerGetExpressionContextCallback( &_getExpressionContext, this );
494 }
495 
497 {
498  delete mRenderer;
499  delete mModel;
500  delete mCategorizedSymbol;
501 }
502 
504 {
505  // Note: This assumes that the signals for UI element changes have not
506  // yet been connected, so that the updates to color ramp, symbol, etc
507  // don't override existing customisations.
508 
510 
511  //mModel->setRenderer ( mRenderer ); // necessary?
512 
513  // set column
514  QString attrName = mRenderer->classAttribute();
515  mExpressionWidget->setField( attrName );
516 
517  // set source symbol
518  if ( mRenderer->sourceSymbol() )
519  {
520  delete mCategorizedSymbol;
523  }
524 
525  // set source color ramp
526  if ( mRenderer->sourceColorRamp() )
527  {
528  cboCategorizedColorRamp->setSourceColorRamp( mRenderer->sourceColorRamp() );
529  cbxInvertedColorRamp->setChecked( mRenderer->invertedColorRamp() );
530  }
531 
532 }
533 
535 {
536  return mRenderer;
537 }
538 
540 {
541  QList<int> selectedCats = selectedCategories();
542 
543  if ( !selectedCats.isEmpty() )
544  {
545  QgsSymbolV2* newSymbol = mCategorizedSymbol->clone();
546  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
547  dlg.setMapCanvas( mMapCanvas );
548  if ( !dlg.exec() )
549  {
550  delete newSymbol;
551  return;
552  }
553 
554  Q_FOREACH ( int idx, selectedCats )
555  {
556  QgsRendererCategoryV2 category = mRenderer->categories().value( idx );
557 
558  QgsSymbolV2* newCatSymbol = newSymbol->clone();
559  newCatSymbol->setColor( mRenderer->categories()[idx].symbol()->color() );
560  mRenderer->updateCategorySymbol( idx, newCatSymbol );
561  }
562  }
563 }
564 
566 {
567  // When there is a slection, change the selected symbols alone
568  QItemSelectionModel* m = viewCategories->selectionModel();
569  QModelIndexList i = m->selectedRows();
570 
571  if ( m && !i.isEmpty() )
572  {
574  return;
575  }
576 
577  // When there is no selection, change the base mCategorizedSymbol
578  QgsSymbolV2* newSymbol = mCategorizedSymbol->clone();
579 
580  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
581  dlg.setMapCanvas( mMapCanvas );
582  if ( !dlg.exec() || !newSymbol )
583  {
584  delete newSymbol;
585  return;
586  }
587 
588  delete mCategorizedSymbol;
589  mCategorizedSymbol = newSymbol;
591 
593 }
594 
596 {
597  QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( mCategorizedSymbol, btnChangeCategorizedSymbol->iconSize() );
598  btnChangeCategorizedSymbol->setIcon( icon );
599 }
600 
602 {
603 }
604 
606 {
607  mRenderer->setClassAttribute( field );
608 }
609 
611 {
612  if ( idx.isValid() && idx.column() == 0 )
614 }
615 
617 {
618  int catIdx = currentCategoryRow();
620 
621  QgsSymbolV2 *symbol = category.symbol();
622  if ( symbol )
623  {
624  symbol = symbol->clone();
625  }
626  else
627  {
629  }
630 
631  QgsSymbolV2SelectorDialog dlg( symbol, mStyle, mLayer, this );
632  dlg.setMapCanvas( mMapCanvas );
633  if ( !dlg.exec() )
634  {
635  delete symbol;
636  return;
637  }
638 
639  mRenderer->updateCategorySymbol( catIdx, symbol );
640 }
641 
642 static void _createCategories( QgsCategoryList& cats, QList<QVariant>& values, QgsSymbolV2* symbol )
643 {
644  // sort the categories first
645  QgsSymbolLayerV2Utils::sortVariantList( values, Qt::AscendingOrder );
646 
647  int num = values.count();
648 
649  bool hasNull = false;
650 
651  for ( int i = 0; i < num; i++ )
652  {
653  QVariant value = values[i];
654  if ( value.toString().isNull() )
655  {
656  hasNull = true;
657  }
658  QgsSymbolV2* newSymbol = symbol->clone();
659 
660  cats.append( QgsRendererCategoryV2( value, newSymbol, value.toString(), true ) );
661  }
662 
663  // add null (default) value if not exists
664  if ( !hasNull )
665  {
666  QgsSymbolV2* newSymbol = symbol->clone();
667  cats.append( QgsRendererCategoryV2( QVariant( "" ), newSymbol, QString(), true ) );
668  }
669 }
670 
672 {
673  QgsVectorColorRampV2* ramp = cboCategorizedColorRamp->currentColorRamp();
674  if ( !ramp )
675  {
676  if ( cboCategorizedColorRamp->count() == 0 )
677  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
678  else if ( !cboCategorizedColorRamp->createNewColorRampSelected() )
679  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
680  }
681  return ramp;
682 }
683 
684 
686 {
687  QString attrName = mExpressionWidget->currentField();
688  int idx = mLayer->fieldNameIndex( attrName );
689  QList<QVariant> unique_vals;
690  if ( idx == -1 )
691  {
692  // Lets assume it's an expression
693  QgsExpression* expression = new QgsExpression( attrName );
694  QgsExpressionContext context;
699 
700  expression->prepare( &context );
702  QgsFeature feature;
703  while ( fit.nextFeature( feature ) )
704  {
705  context.setFeature( feature );
706  QVariant value = expression->evaluate( &context );
707  if ( unique_vals.contains( value ) )
708  continue;
709  unique_vals << value;
710  }
711  }
712  else
713  {
714  mLayer->uniqueValues( idx, unique_vals );
715  }
716 
717  // ask to abort if too many classes
718  if ( unique_vals.size() >= 1000 )
719  {
720  int res = QMessageBox::warning( nullptr, tr( "High number of classes!" ),
721  tr( "Classification would yield %1 entries which might not be expected. Continue?" ).arg( unique_vals.size() ),
722  QMessageBox::Ok | QMessageBox::Cancel,
723  QMessageBox::Cancel );
724  if ( res == QMessageBox::Cancel )
725  {
726  return;
727  }
728  }
729 
730 #if 0
731  DlgAddCategories dlg( mStyle, createDefaultSymbol(), unique_vals, this );
732  if ( !dlg.exec() )
733  return;
734 #endif
735 
736  QgsCategoryList cats;
737  _createCategories( cats, unique_vals, mCategorizedSymbol );
738  bool deleteExisting = false;
739 
740  if ( !mOldClassificationAttribute.isEmpty() &&
741  attrName != mOldClassificationAttribute &&
743  {
744  int res = QMessageBox::question( this,
745  tr( "Confirm Delete" ),
746  tr( "The classification field was changed from '%1' to '%2'.\n"
747  "Should the existing classes be deleted before classification?" )
748  .arg( mOldClassificationAttribute, attrName ),
749  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
750  if ( res == QMessageBox::Cancel )
751  {
752  return;
753  }
754 
755  deleteExisting = ( res == QMessageBox::Yes );
756  }
757 
758  // First element to apply coloring to
759  bool keepExistingColors = false;
760  if ( !deleteExisting )
761  {
762  QgsCategoryList prevCats = mRenderer->categories();
763  keepExistingColors = !prevCats.isEmpty();
764  for ( int i = 0; i < cats.size(); ++i )
765  {
766  bool contains = false;
767  QVariant value = cats.at( i ).value();
768  for ( int j = 0; j < prevCats.size() && !contains; ++j )
769  {
770  if ( prevCats.at( j ).value() == value )
771  {
772  contains = true;
773  break;
774  }
775  }
776 
777  if ( !contains )
778  prevCats.append( cats.at( i ) );
779  }
780  cats = prevCats;
781  }
782 
783  mOldClassificationAttribute = attrName;
784 
785  // TODO: if not all categories are desired, delete some!
786  /*
787  if (not dlg.readAllCats.isChecked())
788  {
789  cats2 = {}
790  for item in dlg.listCategories.selectedItems():
791  for k,c in cats.iteritems():
792  if item.text() == k.toString():
793  break
794  cats2[k] = c
795  cats = cats2
796  }
797  */
798 
799  // recreate renderer
804  r->setInvertedColorRamp( cbxInvertedColorRamp->isChecked() );
806  if ( ramp ) r->setSourceColorRamp( ramp->clone() );
807 
808  if ( mModel )
809  {
810  mModel->setRenderer( r );
811  }
812  delete mRenderer;
813  mRenderer = r;
814  if ( ! keepExistingColors && ramp ) applyColorRamp();
815  delete ramp;
816 }
817 
819 {
821  if ( ramp )
822  {
823  mRenderer->updateColorRamp( ramp, cbxInvertedColorRamp->isChecked() );
824  }
825  mModel->updateSymbology();
826 }
827 
829 {
830  QModelIndex idx = viewCategories->selectionModel()->currentIndex();
831  if ( !idx.isValid() )
832  return -1;
833  return idx.row();
834 }
835 
837 {
838  QList<int> rows;
839  QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
840 
841  Q_FOREACH ( const QModelIndex& r, selectedRows )
842  {
843  if ( r.isValid() )
844  {
845  rows.append( r.row() );
846  }
847  }
848  return rows;
849 }
850 
852 {
853  QList<int> categoryIndexes = selectedCategories();
854  mModel->deleteRows( categoryIndexes );
855 }
856 
858 {
859  mModel->removeAllRows();
860 }
861 
863 {
864  if ( !mModel ) return;
866  QgsRendererCategoryV2 cat( QString(), symbol, QString(), true );
867  mModel->addCategory( cat );
868 }
869 
871 {
872  mRenderer->setSizeScaleField( fldName );
873 }
874 
876 {
877  mRenderer->setScaleMethod( scaleMethod );
878 }
879 
881 {
883 
884  QItemSelectionModel* m = viewCategories->selectionModel();
885  QModelIndexList selectedIndexes = m->selectedRows( 1 );
886 
887  if ( m && !selectedIndexes.isEmpty() )
888  {
889  const QgsCategoryList& categories = mRenderer->categories();
890  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
891  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
892  {
893  int row = ( *indexIt ).row();
894  QgsSymbolV2* s = categories[row].symbol();
895  if ( s )
896  {
897  selectedSymbols.append( s );
898  }
899  }
900  }
901  return selectedSymbols;
902 }
903 
905 {
906  QgsCategoryList cl;
907 
908  QItemSelectionModel* m = viewCategories->selectionModel();
909  QModelIndexList selectedIndexes = m->selectedRows( 1 );
910 
911  if ( m && !selectedIndexes.isEmpty() )
912  {
913  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
914  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
915  {
916  cl.append( mModel->category( *indexIt ) );
917  }
918  }
919  return cl;
920 }
921 
923 {
925 }
926 
928 {
929  viewCategories->selectionModel()->clear();
930 }
931 
933 {
934  int matched = matchToSymbols( QgsStyleV2::defaultStyle() );
935  if ( matched > 0 )
936  {
937  QMessageBox::information( this, tr( "Matched symbols" ),
938  tr( "Matched %1 categories to symbols." ).arg( matched ) );
939  }
940  else
941  {
942  QMessageBox::warning( this, tr( "Matched symbols" ),
943  tr( "No categories could be matched to symbols in library." ) );
944  }
945 }
946 
948 {
949  if ( !mLayer || !style )
950  return 0;
951 
952  int matched = 0;
953  for ( int catIdx = 0; catIdx < mRenderer->categories().count(); ++catIdx )
954  {
955  QString val = mRenderer->categories().at( catIdx ).value().toString();
956  QgsSymbolV2* symbol = style->symbol( val );
957  if ( symbol &&
958  (( symbol->type() == QgsSymbolV2::Marker && mLayer->geometryType() == QGis::Point )
959  || ( symbol->type() == QgsSymbolV2::Line && mLayer->geometryType() == QGis::Line )
960  || ( symbol->type() == QgsSymbolV2::Fill && mLayer->geometryType() == QGis::Polygon ) ) )
961  {
962  matched++;
963  mRenderer->updateCategorySymbol( catIdx, symbol->clone() );
964  }
965  }
966  mModel->updateSymbology();
967  return matched;
968 }
969 
971 {
972  QSettings settings;
973  QString openFileDir = settings.value( "UI/lastMatchToSymbolsDir", QDir::homePath() ).toString();
974 
975  QString fileName = QFileDialog::getOpenFileName( this, tr( "Match to symbols from file" ), openFileDir,
976  tr( "XML files (*.xml *XML)" ) );
977  if ( fileName.isEmpty() )
978  {
979  return;
980  }
981 
982  QFileInfo openFileInfo( fileName );
983  settings.setValue( "UI/lastMatchToSymbolsDir", openFileInfo.absolutePath() );
984 
985  QgsStyleV2 importedStyle;
986  if ( !importedStyle.importXML( fileName ) )
987  {
988  QMessageBox::warning( this, tr( "Matching error" ),
989  tr( "An error occurred reading file:\n%1" ).arg( importedStyle.errorString() ) );
990  return;
991  }
992 
993  int matched = matchToSymbols( &importedStyle );
994  if ( matched > 0 )
995  {
996  QMessageBox::information( this, tr( "Matched symbols" ),
997  tr( "Matched %1 categories to symbols from file." ).arg( matched ) );
998  }
999  else
1000  {
1001  QMessageBox::warning( this, tr( "Matched symbols" ),
1002  tr( "No categories could be matched to symbols in file." ) );
1003  }
1004 }
1005 
1007 {
1008  if ( !event )
1009  {
1010  return;
1011  }
1012 
1013  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1014  {
1015  mCopyBuffer.clear();
1016  mCopyBuffer = selectedCategoryList();
1017  }
1018  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1019  {
1020  QgsCategoryList::const_iterator rIt = mCopyBuffer.constBegin();
1021  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1022  {
1023  mModel->addCategory( *rIt );
1024  }
1025  }
1026 }
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 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:446
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