QGIS API Documentation  2.11.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 
373 static QgsExpressionContext _getExpressionContext( const void* context )
374 {
375  QgsExpressionContext expContext;
378 
379  const QgsVectorLayer* layer = ( const QgsVectorLayer* ) context;
380  if ( layer )
381  expContext << QgsExpressionContextUtils::layerScope( layer );
382 
383  return expContext;
384 }
385 
387  : QgsRendererV2Widget( layer, style )
388  , mRenderer( 0 )
389  , mModel( 0 )
390 {
391 
392  // try to recognize the previous renderer
393  // (null renderer means "no previous renderer")
394  if ( renderer )
395  {
397  }
398  if ( !mRenderer )
399  {
401  }
402 
403  QString attrName = mRenderer->classAttribute();
404  mOldClassificationAttribute = attrName;
405 
406  // setup user interface
407  setupUi( this );
408 
409  mExpressionWidget->setLayer( mLayer );
410 
411  cboCategorizedColorRamp->populate( mStyle );
412  int randomIndex = cboCategorizedColorRamp->findText( tr( "Random colors" ) );
413  if ( randomIndex != -1 )
414  {
415  cboCategorizedColorRamp->setCurrentIndex( randomIndex );
416  }
417 
418  // set project default color ramp
419  QString defaultColorRamp = QgsProject::instance()->readEntry( "DefaultStyles", "/ColorRamp", "" );
420  if ( defaultColorRamp != "" )
421  {
422  int index = cboCategorizedColorRamp->findText( defaultColorRamp, Qt::MatchCaseSensitive );
423  if ( index >= 0 )
424  cboCategorizedColorRamp->setCurrentIndex( index );
425  }
426 
428 
430  mModel->setRenderer( mRenderer );
431 
432  // update GUI from renderer
434 
435  viewCategories->setModel( mModel );
436  viewCategories->resizeColumnToContents( 0 );
437  viewCategories->resizeColumnToContents( 1 );
438  viewCategories->resizeColumnToContents( 2 );
439 
440  viewCategories->setStyle( new QgsCategorizedSymbolRendererV2ViewStyle( viewCategories->style() ) );
441 
442  connect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
443 
444  connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( categoryColumnChanged( QString ) ) );
445 
446  connect( viewCategories, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( categoriesDoubleClicked( const QModelIndex & ) ) );
447  connect( viewCategories, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
448 
449  connect( btnChangeCategorizedSymbol, SIGNAL( clicked() ), this, SLOT( changeCategorizedSymbol() ) );
450  connect( btnAddCategories, SIGNAL( clicked() ), this, SLOT( addCategories() ) );
451  connect( btnDeleteCategories, SIGNAL( clicked() ), this, SLOT( deleteCategories() ) );
452  connect( btnDeleteAllCategories, SIGNAL( clicked() ), this, SLOT( deleteAllCategories() ) );
453  connect( btnAddCategory, SIGNAL( clicked() ), this, SLOT( addCategory() ) );
454  connect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( applyColorRamp() ) );
455  connect( cboCategorizedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( applyColorRamp() ) );
456  connect( cboCategorizedColorRamp, SIGNAL( sourceRampEdited() ), this, SLOT( applyColorRamp() ) );
457  connect( mButtonEditRamp, SIGNAL( clicked() ), cboCategorizedColorRamp, SLOT( editSourceRamp() ) );
458 
459  // menus for data-defined rotation/size
460  QMenu* advMenu = new QMenu;
461 
462  advMenu->addAction( tr( "Match to saved symbols" ), this, SLOT( matchToSymbolsFromLibrary() ) );
463  advMenu->addAction( tr( "Match to symbols from file..." ), this, SLOT( matchToSymbolsFromXml() ) );
464  advMenu->addAction( tr( "Symbol levels..." ), this, SLOT( showSymbolLevels() ) );
465 
466  btnAdvanced->setMenu( advMenu );
467 
468  mExpressionWidget->registerGetExpressionContextCallback( &_getExpressionContext, layer );
469 }
470 
472 {
473  if ( mRenderer ) delete mRenderer;
474  if ( mModel ) delete mModel;
475 }
476 
478 {
479  // Note: This assumes that the signals for UI element changes have not
480  // yet been connected, so that the updates to color ramp, symbol, etc
481  // don't override existing customisations.
482 
484 
485  //mModel->setRenderer ( mRenderer ); // necessary?
486 
487  // set column
488  QString attrName = mRenderer->classAttribute();
489  mExpressionWidget->setField( attrName );
490 
491  // set source symbol
492  if ( mRenderer->sourceSymbol() )
493  {
494  delete mCategorizedSymbol;
497  }
498 
499  // set source color ramp
500  if ( mRenderer->sourceColorRamp() )
501  {
502  cboCategorizedColorRamp->setSourceColorRamp( mRenderer->sourceColorRamp() );
503  cbxInvertedColorRamp->setChecked( mRenderer->invertedColorRamp() );
504  }
505 
506 }
507 
509 {
510  return mRenderer;
511 }
512 
514 {
515  QList<int> selectedCats = selectedCategories();
516 
517  if ( selectedCats.size() > 0 )
518  {
519  QgsSymbolV2* newSymbol = mCategorizedSymbol->clone();
520  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
521  if ( !dlg.exec() )
522  {
523  delete newSymbol;
524  return;
525  }
526 
527  foreach ( const int idx, selectedCats )
528  {
529  QgsRendererCategoryV2 category = mRenderer->categories().value( idx );
530 
531  QgsSymbolV2* newCatSymbol = newSymbol->clone();
532  newCatSymbol->setColor( mRenderer->categories()[idx].symbol()->color() );
533  mRenderer->updateCategorySymbol( idx, newCatSymbol );
534  }
535  }
536 }
537 
539 {
540  // When there is a slection, change the selected symbols alone
541  QItemSelectionModel* m = viewCategories->selectionModel();
542  QModelIndexList i = m->selectedRows();
543 
544  if ( m && i.size() > 0 )
545  {
547  return;
548  }
549 
550  // When there is no selection, change the base mCategorizedSymbol
551  QgsSymbolV2* newSymbol = mCategorizedSymbol->clone();
552 
553  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
554  if ( !dlg.exec() )
555  {
556  delete newSymbol;
557  return;
558  }
559 
560  mCategorizedSymbol = newSymbol;
562 
564 }
565 
567 {
568  QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( mCategorizedSymbol, btnChangeCategorizedSymbol->iconSize() );
569  btnChangeCategorizedSymbol->setIcon( icon );
570 }
571 
573 {
574 }
575 
577 {
578  mRenderer->setClassAttribute( field );
579 }
580 
582 {
583  if ( idx.isValid() && idx.column() == 0 )
585 }
586 
588 {
589  int catIdx = currentCategoryRow();
591 
592  QgsSymbolV2 *symbol = category.symbol();
593  if ( symbol )
594  {
595  symbol = symbol->clone();
596  }
597  else
598  {
600  }
601 
602  QgsSymbolV2SelectorDialog dlg( symbol, mStyle, mLayer, this );
603  if ( !dlg.exec() )
604  {
605  delete symbol;
606  return;
607  }
608 
609  mRenderer->updateCategorySymbol( catIdx, symbol );
610 }
611 
612 static void _createCategories( QgsCategoryList& cats, QList<QVariant>& values, QgsSymbolV2* symbol )
613 {
614  // sort the categories first
615  QgsSymbolLayerV2Utils::sortVariantList( values, Qt::AscendingOrder );
616 
617  int num = values.count();
618 
619  bool hasNull = false;
620 
621  for ( int i = 0; i < num; i++ )
622  {
623  QVariant value = values[i];
624  if ( value.toString().isNull() )
625  {
626  hasNull = true;
627  }
628  QgsSymbolV2* newSymbol = symbol->clone();
629 
630  cats.append( QgsRendererCategoryV2( value, newSymbol, value.toString(), true ) );
631  }
632 
633  // add null (default) value if not exists
634  if ( !hasNull )
635  {
636  QgsSymbolV2* newSymbol = symbol->clone();
637  cats.append( QgsRendererCategoryV2( QVariant( "" ), newSymbol, QString(), true ) );
638  }
639 }
640 
642 {
643  QgsVectorColorRampV2* ramp = cboCategorizedColorRamp->currentColorRamp();
644  if ( ramp == NULL )
645  {
646  if ( cboCategorizedColorRamp->count() == 0 )
647  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
648  else if ( !cboCategorizedColorRamp->createNewColorRampSelected() )
649  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
650  }
651  return ramp;
652 }
653 
654 
656 {
657  QString attrName = mExpressionWidget->currentField();
658  int idx = mLayer->fieldNameIndex( attrName );
659  QList<QVariant> unique_vals;
660  if ( idx == -1 )
661  {
662  // Lets assume it's an expression
663  QgsExpression* expression = new QgsExpression( attrName );
664  QgsExpressionContext context;
668 
669  expression->prepare( &context );
671  QgsFeature feature;
672  while ( fit.nextFeature( feature ) )
673  {
674  context.setFeature( feature );
675  QVariant value = expression->evaluate( &context );
676  if ( unique_vals.contains( value ) )
677  continue;
678  unique_vals << value;
679  }
680  }
681  else
682  {
683  mLayer->uniqueValues( idx, unique_vals );
684  }
685 
686  // ask to abort if too many classes
687  if ( unique_vals.size() >= 1000 )
688  {
689  int res = QMessageBox::warning( 0, tr( "High number of classes!" ),
690  tr( "Classification would yield %1 entries which might not be expected. Continue?" ).arg( unique_vals.size() ),
691  QMessageBox::Ok | QMessageBox::Cancel,
692  QMessageBox::Cancel );
693  if ( res == QMessageBox::Cancel )
694  {
695  return;
696  }
697  }
698 
699 #if 0
700  DlgAddCategories dlg( mStyle, createDefaultSymbol(), unique_vals, this );
701  if ( !dlg.exec() )
702  return;
703 #endif
704 
705  QgsCategoryList cats;
706  _createCategories( cats, unique_vals, mCategorizedSymbol );
707  bool deleteExisting = false;
708 
709  if ( !mOldClassificationAttribute.isEmpty() &&
710  attrName != mOldClassificationAttribute &&
711  mRenderer->categories().count() > 0 )
712  {
713  int res = QMessageBox::question( this,
714  tr( "Confirm Delete" ),
715  tr( "The classification field was changed from '%1' to '%2'.\n"
716  "Should the existing classes be deleted before classification?" )
717  .arg( mOldClassificationAttribute ).arg( attrName ),
718  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
719  if ( res == QMessageBox::Cancel )
720  {
721  return;
722  }
723 
724  deleteExisting = ( res == QMessageBox::Yes );
725  }
726 
727  // First element to apply coloring to
728  bool keepExistingColors = false;
729  if ( !deleteExisting )
730  {
731  QgsCategoryList prevCats = mRenderer->categories();
732  keepExistingColors = prevCats.size() > 0;
733  for ( int i = 0; i < cats.size(); ++i )
734  {
735  bool contains = false;
736  QVariant value = cats.at( i ).value();
737  for ( int j = 0; j < prevCats.size() && !contains; ++j )
738  {
739  if ( prevCats.at( j ).value() == value )
740  {
741  contains = true;
742  break;
743  }
744  }
745 
746  if ( !contains )
747  prevCats.append( cats.at( i ) );
748  }
749  cats = prevCats;
750  }
751 
752  mOldClassificationAttribute = attrName;
753 
754  // TODO: if not all categories are desired, delete some!
755  /*
756  if (not dlg.readAllCats.isChecked())
757  {
758  cats2 = {}
759  for item in dlg.listCategories.selectedItems():
760  for k,c in cats.iteritems():
761  if item.text() == k.toString():
762  break
763  cats2[k] = c
764  cats = cats2
765  }
766  */
767 
768  // recreate renderer
773  r->setInvertedColorRamp( cbxInvertedColorRamp->isChecked() );
775  if ( ramp ) r->setSourceColorRamp( ramp->clone() );
776 
777  if ( mModel )
778  {
779  mModel->setRenderer( r );
780  }
781  delete mRenderer;
782  mRenderer = r;
783  if ( ! keepExistingColors && ramp ) applyColorRamp();
784 }
785 
787 {
789  if ( ramp )
790  {
791  mRenderer->updateColorRamp( ramp->clone(), cbxInvertedColorRamp->isChecked() );
792  }
794 }
795 
797 {
798  QModelIndex idx = viewCategories->selectionModel()->currentIndex();
799  if ( !idx.isValid() )
800  return -1;
801  return idx.row();
802 }
803 
805 {
806  QList<int> rows;
807  QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
808 
809  foreach ( QModelIndex r, selectedRows )
810  {
811  if ( r.isValid() )
812  {
813  rows.append( r.row() );
814  }
815  }
816  return rows;
817 }
818 
820 {
821  QList<int> categoryIndexes = selectedCategories();
822  mModel->deleteRows( categoryIndexes );
823 }
824 
826 {
828 }
829 
831 {
832  if ( !mModel ) return;
834  QgsRendererCategoryV2 cat( QString(), symbol, QString(), true );
835  mModel->addCategory( cat );
836 }
837 
839 {
840  mRenderer->setSizeScaleField( fldName );
841 }
842 
844 {
845  mRenderer->setScaleMethod( scaleMethod );
846 }
847 
849 {
851 
852  QItemSelectionModel* m = viewCategories->selectionModel();
853  QModelIndexList selectedIndexes = m->selectedRows( 1 );
854 
855  if ( m && selectedIndexes.size() > 0 )
856  {
857  const QgsCategoryList& categories = mRenderer->categories();
858  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
859  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
860  {
861  int row = ( *indexIt ).row();
862  QgsSymbolV2* s = categories[row].symbol();
863  if ( s )
864  {
865  selectedSymbols.append( s );
866  }
867  }
868  }
869  return selectedSymbols;
870 }
871 
873 {
874  QgsCategoryList cl;
875 
876  QItemSelectionModel* m = viewCategories->selectionModel();
877  QModelIndexList selectedIndexes = m->selectedRows( 1 );
878 
879  if ( m && selectedIndexes.size() > 0 )
880  {
881  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
882  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
883  {
884  cl.append( mModel->category( *indexIt ) );
885  }
886  }
887  return cl;
888 }
889 
891 {
893 }
894 
896 {
897  viewCategories->selectionModel()->clear();
898 }
899 
901 {
902  int matched = matchToSymbols( QgsStyleV2::defaultStyle() );
903  if ( matched > 0 )
904  {
905  QMessageBox::information( this, tr( "Matched symbols" ),
906  tr( "Matched %1 categories to symbols." ).arg( matched ) );
907  }
908  else
909  {
910  QMessageBox::warning( this, tr( "Matched symbols" ),
911  tr( "No categories could be matched to symbols in library." ) );
912  }
913 }
914 
916 {
917  if ( !mLayer || !style )
918  return 0;
919 
920  int matched = 0;
921  for ( int catIdx = 0; catIdx < mRenderer->categories().count(); ++catIdx )
922  {
923  QString val = mRenderer->categories().at( catIdx ).value().toString();
924  QgsSymbolV2* symbol = style->symbol( val );
925  if ( symbol &&
926  (( symbol->type() == QgsSymbolV2::Marker && mLayer->geometryType() == QGis::Point )
927  || ( symbol->type() == QgsSymbolV2::Line && mLayer->geometryType() == QGis::Line )
928  || ( symbol->type() == QgsSymbolV2::Fill && mLayer->geometryType() == QGis::Polygon ) ) )
929  {
930  matched++;
931  mRenderer->updateCategorySymbol( catIdx, symbol->clone() );
932  }
933  }
935  return matched;
936 }
937 
939 {
940  QSettings settings;
941  QString openFileDir = settings.value( "UI/lastMatchToSymbolsDir", "" ).toString();
942 
943  QString fileName = QFileDialog::getOpenFileName( this, tr( "Match to symbols from file" ), openFileDir,
944  tr( "XML files (*.xml *XML)" ) );
945  if ( fileName.isEmpty() )
946  {
947  return;
948  }
949 
950  QFileInfo openFileInfo( fileName );
951  settings.setValue( "UI/lastMatchToSymbolsDir", openFileInfo.absolutePath() );
952 
953  QgsStyleV2 importedStyle;
954  if ( !importedStyle.importXML( fileName ) )
955  {
956  QMessageBox::warning( this, tr( "Matching error" ),
957  tr( "An error occured reading file:\n%1" ).arg( importedStyle.errorString() ) );
958  return;
959  }
960 
961  int matched = matchToSymbols( &importedStyle );
962  if ( matched > 0 )
963  {
964  QMessageBox::information( this, tr( "Matched symbols" ),
965  tr( "Matched %1 categories to symbols from file." ).arg( matched ) );
966  }
967  else
968  {
969  QMessageBox::warning( this, tr( "Matched symbols" ),
970  tr( "No categories could be matched to symbols in file." ) );
971  }
972 }
973 
975 {
976  if ( !event )
977  {
978  return;
979  }
980 
981  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
982  {
983  mCopyBuffer.clear();
984  mCopyBuffer = selectedCategoryList();
985  }
986  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
987  {
988  QgsCategoryList::const_iterator rIt = mCopyBuffer.constBegin();
989  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
990  {
991  mModel->addCategory( *rIt );
992  }
993  }
994 }
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:88
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.
void contextMenuViewCategories(const QPoint &p)
void append(const T &value)
SymbolType type() const
Definition: qgssymbolv2.h:86
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
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:162
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
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)
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
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
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:352
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.
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)
QString errorString()
return last error from load/save operation
Definition: qgsstylev2.h:274
typedef ItemFlags