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