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