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