QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
qgscategorizedsymbolrendererwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscategorizedsymbolrendererwidget.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 #include "qgspanelwidget.h"
18 
20 
23 #include "qgssymbol.h"
24 #include "qgssymbollayerutils.h"
25 #include "qgscolorramp.h"
26 #include "qgscolorrampbutton.h"
27 #include "qgsstyle.h"
28 #include "qgslogger.h"
30 
33 
34 #include "qgsvectorlayer.h"
35 #include "qgsfeatureiterator.h"
36 
37 #include "qgsproject.h"
38 #include "qgsexpression.h"
39 #include "qgsmapcanvas.h"
40 #include "qgssettings.h"
41 #include "qgsguiutils.h"
42 
43 #include <QKeyEvent>
44 #include <QMenu>
45 #include <QMessageBox>
46 #include <QStandardItemModel>
47 #include <QStandardItem>
48 #include <QPen>
49 #include <QPainter>
50 #include <QFileDialog>
51 #include <QClipboard>
52 
54 
55 QgsCategorizedSymbolRendererModel::QgsCategorizedSymbolRendererModel( QObject *parent ) : QAbstractItemModel( parent )
56  , mMimeFormat( QStringLiteral( "application/x-qgscategorizedsymbolrendererv2model" ) )
57 {
58 }
59 
60 void QgsCategorizedSymbolRendererModel::setRenderer( QgsCategorizedSymbolRenderer *renderer )
61 {
62  if ( mRenderer )
63  {
64  beginRemoveRows( QModelIndex(), 0, std::max( mRenderer->categories().size() - 1, 0 ) );
65  mRenderer = nullptr;
66  endRemoveRows();
67  }
68  if ( renderer )
69  {
70  mRenderer = renderer;
71  if ( renderer->categories().size() > 0 )
72  {
73  beginInsertRows( QModelIndex(), 0, renderer->categories().size() - 1 );
74  endInsertRows();
75  }
76  }
77 }
78 
79 void QgsCategorizedSymbolRendererModel::addCategory( const QgsRendererCategory &cat )
80 {
81  if ( !mRenderer ) return;
82  int idx = mRenderer->categories().size();
83  beginInsertRows( QModelIndex(), idx, idx );
84  mRenderer->addCategory( cat );
85  endInsertRows();
86 }
87 
88 QgsRendererCategory QgsCategorizedSymbolRendererModel::category( const QModelIndex &index )
89 {
90  if ( !mRenderer )
91  {
92  return QgsRendererCategory();
93  }
94  const QgsCategoryList &catList = mRenderer->categories();
95  int row = index.row();
96  if ( row >= catList.size() )
97  {
98  return QgsRendererCategory();
99  }
100  return catList.at( row );
101 }
102 
103 
104 Qt::ItemFlags QgsCategorizedSymbolRendererModel::flags( const QModelIndex &index ) const
105 {
106  if ( !index.isValid() || !mRenderer )
107  {
108  return Qt::ItemIsDropEnabled;
109  }
110 
111  Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
112  if ( index.column() == 1 )
113  {
114  const QgsRendererCategory category = mRenderer->categories().value( index.row() );
115  if ( category.value().type() != QVariant::List )
116  {
117  flags |= Qt::ItemIsEditable;
118  }
119  }
120  else if ( index.column() == 2 )
121  {
122  flags |= Qt::ItemIsEditable;
123  }
124  return flags;
125 }
126 
127 Qt::DropActions QgsCategorizedSymbolRendererModel::supportedDropActions() const
128 {
129  return Qt::MoveAction;
130 }
131 
132 QVariant QgsCategorizedSymbolRendererModel::data( const QModelIndex &index, int role ) const
133 {
134  if ( !index.isValid() || !mRenderer )
135  return QVariant();
136 
137  const QgsRendererCategory category = mRenderer->categories().value( index.row() );
138 
139  switch ( role )
140  {
141  case Qt::CheckStateRole:
142  {
143  if ( index.column() == 0 )
144  {
145  return category.renderState() ? Qt::Checked : Qt::Unchecked;
146  }
147  break;
148  }
149 
150  case Qt::DisplayRole:
151  case Qt::ToolTipRole:
152  {
153  switch ( index.column() )
154  {
155  case 1:
156  {
157  if ( category.value().type() == QVariant::List )
158  {
159  QStringList res;
160  const QVariantList list = category.value().toList();
161  res.reserve( list.size() );
162  for ( const QVariant &v : list )
163  res << v.toString();
164 
165  if ( role == Qt::DisplayRole )
166  return res.join( ';' );
167  else // tooltip
168  return res.join( '\n' );
169  }
170  else if ( !category.value().isValid() || category.value().isNull() || category.value().toString().isEmpty() )
171  {
172  return tr( "all other values" );
173  }
174  else
175  {
176  return category.value().toString();
177  }
178  }
179  case 2:
180  return category.label();
181  }
182  break;
183  }
184 
185  case Qt::FontRole:
186  {
187  if ( index.column() == 1 && category.value().type() != QVariant::List && ( !category.value().isValid() || category.value().isNull() || category.value().toString().isEmpty() ) )
188  {
189  QFont italicFont;
190  italicFont.setItalic( true );
191  return italicFont;
192  }
193  return QVariant();
194  }
195 
196  case Qt::DecorationRole:
197  {
198  if ( index.column() == 0 && category.symbol() )
199  {
200  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
201  return QgsSymbolLayerUtils::symbolPreviewIcon( category.symbol(), QSize( iconSize, iconSize ) );
202  }
203  break;
204  }
205 
206  case Qt::ForegroundRole:
207  {
208  QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
209  if ( index.column() == 1 && ( category.value().type() == QVariant::List
210  || !category.value().isValid() || category.value().isNull() || category.value().toString().isEmpty() ) )
211  {
212  QColor fadedTextColor = brush.color();
213  fadedTextColor.setAlpha( 128 );
214  brush.setColor( fadedTextColor );
215  }
216  return brush;
217  }
218 
219  case Qt::TextAlignmentRole:
220  {
221  return ( index.column() == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft;
222  }
223 
224  case Qt::EditRole:
225  {
226  switch ( index.column() )
227  {
228  case 1:
229  {
230  if ( category.value().type() == QVariant::List )
231  {
232  QStringList res;
233  const QVariantList list = category.value().toList();
234  res.reserve( list.size() );
235  for ( const QVariant &v : list )
236  res << v.toString();
237 
238  return res.join( ';' );
239  }
240  else
241  {
242  return category.value();
243  }
244  }
245 
246  case 2:
247  return category.label();
248  }
249  break;
250  }
251  }
252 
253  return QVariant();
254 }
255 
256 bool QgsCategorizedSymbolRendererModel::setData( const QModelIndex &index, const QVariant &value, int role )
257 {
258  if ( !index.isValid() )
259  return false;
260 
261  if ( index.column() == 0 && role == Qt::CheckStateRole )
262  {
263  mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
264  emit dataChanged( index, index );
265  return true;
266  }
267 
268  if ( role != Qt::EditRole )
269  return false;
270 
271  switch ( index.column() )
272  {
273  case 1: // value
274  {
275  // try to preserve variant type for this value
276  QVariant val;
277  switch ( mRenderer->categories().value( index.row() ).value().type() )
278  {
279  case QVariant::Int:
280  val = value.toInt();
281  break;
282  case QVariant::Double:
283  val = value.toDouble();
284  break;
285  case QVariant::List:
286  {
287  const QStringList parts = value.toString().split( ';' );
288  QVariantList list;
289  list.reserve( parts.count() );
290  for ( const QString &p : parts )
291  list << p;
292 
293  if ( list.count() == 1 )
294  val = list.at( 0 );
295  else
296  val = list;
297  break;
298  }
299  default:
300  val = value.toString();
301  break;
302  }
303  mRenderer->updateCategoryValue( index.row(), val );
304  break;
305  }
306  case 2: // label
307  mRenderer->updateCategoryLabel( index.row(), value.toString() );
308  break;
309  default:
310  return false;
311  }
312 
313  emit dataChanged( index, index );
314  return true;
315 }
316 
317 QVariant QgsCategorizedSymbolRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
318 {
319  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
320  {
321  QStringList lst;
322  lst << tr( "Symbol" ) << tr( "Value" ) << tr( "Legend" );
323  return lst.value( section );
324  }
325  return QVariant();
326 }
327 
328 int QgsCategorizedSymbolRendererModel::rowCount( const QModelIndex &parent ) const
329 {
330  if ( parent.isValid() || !mRenderer )
331  {
332  return 0;
333  }
334  return mRenderer->categories().size();
335 }
336 
337 int QgsCategorizedSymbolRendererModel::columnCount( const QModelIndex &index ) const
338 {
339  Q_UNUSED( index )
340  return 3;
341 }
342 
343 QModelIndex QgsCategorizedSymbolRendererModel::index( int row, int column, const QModelIndex &parent ) const
344 {
345  if ( hasIndex( row, column, parent ) )
346  {
347  return createIndex( row, column );
348  }
349  return QModelIndex();
350 }
351 
352 QModelIndex QgsCategorizedSymbolRendererModel::parent( const QModelIndex &index ) const
353 {
354  Q_UNUSED( index )
355  return QModelIndex();
356 }
357 
358 QStringList QgsCategorizedSymbolRendererModel::mimeTypes() const
359 {
360  QStringList types;
361  types << mMimeFormat;
362  return types;
363 }
364 
365 QMimeData *QgsCategorizedSymbolRendererModel::mimeData( const QModelIndexList &indexes ) const
366 {
367  QMimeData *mimeData = new QMimeData();
368  QByteArray encodedData;
369 
370  QDataStream stream( &encodedData, QIODevice::WriteOnly );
371 
372  // Create list of rows
373  const auto constIndexes = indexes;
374  for ( const QModelIndex &index : constIndexes )
375  {
376  if ( !index.isValid() || index.column() != 0 )
377  continue;
378 
379  stream << index.row();
380  }
381  mimeData->setData( mMimeFormat, encodedData );
382  return mimeData;
383 }
384 
385 bool QgsCategorizedSymbolRendererModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
386 {
387  Q_UNUSED( row )
388  Q_UNUSED( column )
389  if ( action != Qt::MoveAction ) return true;
390 
391  if ( !data->hasFormat( mMimeFormat ) ) return false;
392 
393  QByteArray encodedData = data->data( mMimeFormat );
394  QDataStream stream( &encodedData, QIODevice::ReadOnly );
395 
396  QVector<int> rows;
397  while ( !stream.atEnd() )
398  {
399  int r;
400  stream >> r;
401  rows.append( r );
402  }
403 
404  int to = parent.row();
405  // to is -1 if dragged outside items, i.e. below any item,
406  // then move to the last position
407  if ( to == -1 ) to = mRenderer->categories().size(); // out of rang ok, will be decreased
408  for ( int i = rows.size() - 1; i >= 0; i-- )
409  {
410  QgsDebugMsg( QStringLiteral( "move %1 to %2" ).arg( rows[i] ).arg( to ) );
411  int t = to;
412  // moveCategory first removes and then inserts
413  if ( rows[i] < t ) t--;
414  mRenderer->moveCategory( rows[i], t );
415  // current moved under another, shift its index up
416  for ( int j = 0; j < i; j++ )
417  {
418  if ( to < rows[j] && rows[i] > rows[j] ) rows[j] += 1;
419  }
420  // removed under 'to' so the target shifted down
421  if ( rows[i] < to ) to--;
422  }
423  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
424  emit rowsMoved();
425  return false;
426 }
427 
428 void QgsCategorizedSymbolRendererModel::deleteRows( QList<int> rows )
429 {
430  std::sort( rows.begin(), rows.end() ); // list might be unsorted, depending on how the user selected the rows
431  for ( int i = rows.size() - 1; i >= 0; i-- )
432  {
433  beginRemoveRows( QModelIndex(), rows[i], rows[i] );
434  mRenderer->deleteCategory( rows[i] );
435  endRemoveRows();
436  }
437 }
438 
439 void QgsCategorizedSymbolRendererModel::removeAllRows()
440 {
441  beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
442  mRenderer->deleteAllCategories();
443  endRemoveRows();
444 }
445 
446 void QgsCategorizedSymbolRendererModel::sort( int column, Qt::SortOrder order )
447 {
448  if ( column == 0 )
449  {
450  return;
451  }
452  if ( column == 1 )
453  {
454  mRenderer->sortByValue( order );
455  }
456  else if ( column == 2 )
457  {
458  mRenderer->sortByLabel( order );
459  }
460  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
461 }
462 
463 void QgsCategorizedSymbolRendererModel::updateSymbology()
464 {
465  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
466 }
467 
468 // ------------------------------ View style --------------------------------
469 QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QWidget *parent )
470  : QgsProxyStyle( parent )
471 {}
472 
473 void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget ) const
474 {
475  if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
476  {
477  QStyleOption opt( *option );
478  opt.rect.setLeft( 0 );
479  // draw always as line above, because we move item to that index
480  opt.rect.setHeight( 0 );
481  if ( widget ) opt.rect.setRight( widget->width() );
482  QProxyStyle::drawPrimitive( element, &opt, painter, widget );
483  return;
484  }
485  QProxyStyle::drawPrimitive( element, option, painter, widget );
486 }
487 
489 
490 // ------------------------------ Widget ------------------------------------
492 {
493  return new QgsCategorizedSymbolRendererWidget( layer, style, renderer );
494 }
495 
497  : QgsRendererWidget( layer, style )
498  , mContextMenu( new QMenu( this ) )
499 {
500 
501  // try to recognize the previous renderer
502  // (null renderer means "no previous renderer")
503  if ( renderer )
504  {
506  }
507  if ( !mRenderer )
508  {
509  mRenderer = qgis::make_unique< QgsCategorizedSymbolRenderer >( QString(), QgsCategoryList() );
510  }
511 
512  QString attrName = mRenderer->classAttribute();
513  mOldClassificationAttribute = attrName;
514 
515  // setup user interface
516  setupUi( this );
517  layout()->setContentsMargins( 0, 0, 0, 0 );
518 
519  mExpressionWidget->setLayer( mLayer );
520  btnChangeCategorizedSymbol->setLayer( mLayer );
521  btnChangeCategorizedSymbol->registerExpressionContextGenerator( this );
522 
523  // initiate color ramp button to random
524  btnColorRamp->setShowRandomColorRamp( true );
525 
526  // set project default color ramp
527  QString defaultColorRamp = QgsProject::instance()->readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ), QString() );
528  if ( !defaultColorRamp.isEmpty() )
529  {
530  btnColorRamp->setColorRampFromName( defaultColorRamp );
531  }
532  else
533  {
534  btnColorRamp->setRandomColorRamp();
535  }
536 
538  if ( mCategorizedSymbol )
539  {
540  btnChangeCategorizedSymbol->setSymbolType( mCategorizedSymbol->type() );
541  btnChangeCategorizedSymbol->setSymbol( mCategorizedSymbol->clone() );
542  }
543 
544  mModel = new QgsCategorizedSymbolRendererModel( this );
545  mModel->setRenderer( mRenderer.get() );
546 
547  // update GUI from renderer
549 
550  viewCategories->setModel( mModel );
551  viewCategories->resizeColumnToContents( 0 );
552  viewCategories->resizeColumnToContents( 1 );
553  viewCategories->resizeColumnToContents( 2 );
554 
555  viewCategories->setStyle( new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
556  connect( viewCategories->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsCategorizedSymbolRendererWidget::selectionChanged );
557 
558  connect( mModel, &QgsCategorizedSymbolRendererModel::rowsMoved, this, &QgsCategorizedSymbolRendererWidget::rowsMoved );
559  connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsPanelWidget::widgetChanged );
560 
561  connect( mExpressionWidget, static_cast < void ( QgsFieldExpressionWidget::* )( const QString & ) >( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsCategorizedSymbolRendererWidget::categoryColumnChanged );
562 
563  connect( viewCategories, &QAbstractItemView::doubleClicked, this, &QgsCategorizedSymbolRendererWidget::categoriesDoubleClicked );
564  connect( viewCategories, &QTreeView::customContextMenuRequested, this, &QgsCategorizedSymbolRendererWidget::showContextMenu );
565 
566  connect( btnChangeCategorizedSymbol, &QgsSymbolButton::changed, this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton );
567 
568  connect( btnAddCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::addCategories );
569  connect( btnDeleteCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::deleteCategories );
570  connect( btnDeleteAllCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::deleteAllCategories );
571  connect( btnAddCategory, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::addCategory );
572 
574 
575  // menus for data-defined rotation/size
576  QMenu *advMenu = new QMenu;
577 
578  advMenu->addAction( tr( "Match to Saved Symbols" ), this, SLOT( matchToSymbolsFromLibrary() ) );
579  advMenu->addAction( tr( "Match to Symbols from File…" ), this, SLOT( matchToSymbolsFromXml() ) );
580  advMenu->addAction( tr( "Symbol Levels…" ), this, SLOT( showSymbolLevels() ) );
582  {
583  QAction *actionDdsLegend = advMenu->addAction( tr( "Data-defined Size Legend…" ) );
584  // only from Qt 5.6 there is convenience addAction() with new style connection
585  connect( actionDdsLegend, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
586  }
587 
588  btnAdvanced->setMenu( advMenu );
589 
590  mExpressionWidget->registerExpressionContextGenerator( this );
591 
592  mMergeCategoriesAction = new QAction( tr( "Merge Categories" ), this );
593  connect( mMergeCategoriesAction, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::mergeSelectedCategories );
594  mUnmergeCategoriesAction = new QAction( tr( "Unmerge Categories" ), this );
595  connect( mUnmergeCategoriesAction, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories );
596 
597  connect( mContextMenu, &QMenu::aboutToShow, this, [ = ]
598  {
599  std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
600  mPasteSymbolAction->setEnabled( static_cast< bool >( tempSymbol ) );
601  } );
602 }
603 
605 {
606  delete mModel;
607 }
608 
610 {
611  // Note: This assumes that the signals for UI element changes have not
612  // yet been connected, so that the updates to color ramp, symbol, etc
613  // don't override existing customizations.
614 
615  //mModel->setRenderer ( mRenderer ); // necessary?
616 
617  // set column
618  QString attrName = mRenderer->classAttribute();
619  mExpressionWidget->setField( attrName );
620 
621  // set source symbol
622  if ( mRenderer->sourceSymbol() )
623  {
624  mCategorizedSymbol.reset( mRenderer->sourceSymbol()->clone() );
625  whileBlocking( btnChangeCategorizedSymbol )->setSymbol( mCategorizedSymbol->clone() );
626  }
627 
628  // if a color ramp attached to the renderer, enable the color ramp button
629  if ( mRenderer->sourceColorRamp() )
630  {
631  btnColorRamp->setColorRamp( mRenderer->sourceColorRamp() );
632  }
633 }
634 
636 {
637  return mRenderer.get();
638 }
639 
641 {
643  btnChangeCategorizedSymbol->setMapCanvas( context.mapCanvas() );
644  btnChangeCategorizedSymbol->setMessageBar( context.messageBar() );
645 }
646 
648 {
649  QList<int> selectedCats = selectedCategories();
650 
651  if ( !selectedCats.isEmpty() )
652  {
653  QgsSymbol *newSymbol = mCategorizedSymbol->clone();
654  QgsSymbolSelectorDialog dlg( newSymbol, mStyle, mLayer, this );
655  dlg.setContext( context() );
656  if ( !dlg.exec() )
657  {
658  delete newSymbol;
659  return;
660  }
661 
662  const auto constSelectedCats = selectedCats;
663  for ( int idx : constSelectedCats )
664  {
665  QgsRendererCategory category = mRenderer->categories().value( idx );
666 
667  QgsSymbol *newCatSymbol = newSymbol->clone();
668  newCatSymbol->setColor( mRenderer->categories()[idx].symbol()->color() );
669  mRenderer->updateCategorySymbol( idx, newCatSymbol );
670  }
671  }
672 }
673 
675 {
677  std::unique_ptr<QgsSymbol> newSymbol( mCategorizedSymbol->clone() );
678  if ( panel && panel->dockMode() )
679  {
680  // bit tricky here - the widget doesn't take ownership of the symbol. So we need it to last for the duration of the
681  // panel's existence. Accordingly, just kinda give it ownership here, and clean up in cleanUpSymbolSelector
682  QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( newSymbol.release(), mStyle, mLayer, panel );
683  dlg->setContext( mContext );
684  connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget );
685  connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector );
686  openPanel( dlg );
687  }
688  else
689  {
690  QgsSymbolSelectorDialog dlg( newSymbol.get(), mStyle, mLayer, panel );
691  dlg.setContext( mContext );
692  if ( !dlg.exec() || !newSymbol )
693  {
694  return;
695  }
696 
697  mCategorizedSymbol = std::move( newSymbol );
699  }
700 }
701 
702 
704 {
705 }
706 
708 {
709  mRenderer->setClassAttribute( field );
710  emit widgetChanged();
711 }
712 
714 {
715  if ( idx.isValid() && idx.column() == 0 )
717 }
718 
720 {
721  QgsRendererCategory category = mRenderer->categories().value( currentCategoryRow() );
722 
723  std::unique_ptr< QgsSymbol > symbol;
724 
725  if ( category.symbol() )
726  {
727  symbol.reset( category.symbol()->clone() );
728  }
729  else
730  {
731  symbol.reset( QgsSymbol::defaultSymbol( mLayer->geometryType() ) );
732  }
733 
735  if ( panel && panel->dockMode() )
736  {
737  QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( symbol.release(), mStyle, mLayer, panel );
738  dlg->setContext( mContext );
739  dlg->setPanelTitle( category.label() );
740  connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget );
741  connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector );
742  openPanel( dlg );
743  }
744  else
745  {
746  QgsSymbolSelectorDialog dlg( symbol.get(), mStyle, mLayer, panel );
747  dlg.setContext( mContext );
748  if ( !dlg.exec() || !symbol )
749  {
750  return;
751  }
752 
753  mCategorizedSymbol = std::move( symbol );
755  }
756 }
757 
758 
760 {
761  QString attrName = mExpressionWidget->currentField();
762  int idx = mLayer->fields().lookupField( attrName );
763  QList<QVariant> uniqueValues;
764  if ( idx == -1 )
765  {
766  // Lets assume it's an expression
767  QgsExpression *expression = new QgsExpression( attrName );
773 
774  expression->prepare( &context );
776  QgsFeature feature;
777  while ( fit.nextFeature( feature ) )
778  {
779  context.setFeature( feature );
780  QVariant value = expression->evaluate( &context );
781  if ( uniqueValues.contains( value ) )
782  continue;
783  uniqueValues << value;
784  }
785  }
786  else
787  {
788  uniqueValues = mLayer->uniqueValues( idx ).toList();
789  }
790 
791  // ask to abort if too many classes
792  if ( uniqueValues.size() >= 1000 )
793  {
794  int res = QMessageBox::warning( nullptr, tr( "Classify Categories" ),
795  tr( "High number of classes. Classification would yield %1 entries which might not be expected. Continue?" ).arg( uniqueValues.size() ),
796  QMessageBox::Ok | QMessageBox::Cancel,
797  QMessageBox::Cancel );
798  if ( res == QMessageBox::Cancel )
799  {
800  return;
801  }
802  }
803 
804 #if 0
805  DlgAddCategories dlg( mStyle, createDefaultSymbol(), unique_vals, this );
806  if ( !dlg.exec() )
807  return;
808 #endif
809 
811  bool deleteExisting = false;
812 
813  if ( !mOldClassificationAttribute.isEmpty() &&
814  attrName != mOldClassificationAttribute &&
815  !mRenderer->categories().isEmpty() )
816  {
817  int res = QMessageBox::question( this,
818  tr( "Delete Classification" ),
819  tr( "The classification field was changed from '%1' to '%2'.\n"
820  "Should the existing classes be deleted before classification?" )
821  .arg( mOldClassificationAttribute, attrName ),
822  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
823  if ( res == QMessageBox::Cancel )
824  {
825  return;
826  }
827 
828  deleteExisting = ( res == QMessageBox::Yes );
829  }
830 
831  // First element to apply coloring to
832  bool keepExistingColors = false;
833  if ( !deleteExisting )
834  {
835  QgsCategoryList prevCats = mRenderer->categories();
836  keepExistingColors = !prevCats.isEmpty();
837  QgsRandomColorRamp randomColors;
838  if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
839  randomColors.setTotalColorCount( cats.size() );
840  for ( int i = 0; i < cats.size(); ++i )
841  {
842  bool contains = false;
843  QVariant value = cats.at( i ).value();
844  for ( int j = 0; j < prevCats.size() && !contains; ++j )
845  {
846  const QVariant prevCatValue = prevCats.at( j ).value();
847  if ( prevCatValue.type() == QVariant::List )
848  {
849  const QVariantList list = prevCatValue.toList();
850  for ( const QVariant &v : list )
851  {
852  if ( v == value )
853  {
854  contains = true;
855  break;
856  }
857  }
858  }
859  else
860  {
861  if ( prevCats.at( j ).value() == value )
862  {
863  contains = true;
864  }
865  }
866  if ( contains )
867  break;
868  }
869 
870  if ( !contains )
871  {
872  if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
873  {
874  // insure that append symbols have random colors
875  cats.at( i ).symbol()->setColor( randomColors.color( i ) );
876  }
877  prevCats.append( cats.at( i ) );
878  }
879  }
880  cats = prevCats;
881  }
882 
883  mOldClassificationAttribute = attrName;
884 
885  // TODO: if not all categories are desired, delete some!
886  /*
887  if (not dlg.readAllCats.isChecked())
888  {
889  cats2 = {}
890  for item in dlg.listCategories.selectedItems():
891  for k,c in cats.iteritems():
892  if item.text() == k.toString():
893  break
894  cats2[k] = c
895  cats = cats2
896  }
897  */
898 
899  // recreate renderer
900  std::unique_ptr< QgsCategorizedSymbolRenderer > r = qgis::make_unique< QgsCategorizedSymbolRenderer >( attrName, cats );
901  r->setSourceSymbol( mCategorizedSymbol->clone() );
902  std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
903  if ( ramp )
904  r->setSourceColorRamp( ramp->clone() );
905 
906  if ( mModel )
907  {
908  mModel->setRenderer( r.get() );
909  }
910  mRenderer = std::move( r );
911  if ( ! keepExistingColors && ramp )
912  applyColorRamp();
913  emit widgetChanged();
914 }
915 
917 {
918  if ( !btnColorRamp->isNull() )
919  {
920  mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
921  }
922  mModel->updateSymbology();
923 }
924 
926 {
927  QModelIndex idx = viewCategories->selectionModel()->currentIndex();
928  if ( !idx.isValid() )
929  return -1;
930  return idx.row();
931 }
932 
934 {
935  QList<int> rows;
936  QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
937 
938  const auto constSelectedRows = selectedRows;
939  for ( const QModelIndex &r : constSelectedRows )
940  {
941  if ( r.isValid() )
942  {
943  rows.append( r.row() );
944  }
945  }
946  return rows;
947 }
948 
950 {
951  QList<int> categoryIndexes = selectedCategories();
952  mModel->deleteRows( categoryIndexes );
953  emit widgetChanged();
954 }
955 
957 {
958  mModel->removeAllRows();
959  emit widgetChanged();
960 }
961 
963 {
964  if ( !mModel ) return;
966  QgsRendererCategory cat( QString(), symbol, QString(), true );
967  mModel->addCategory( cat );
968  emit widgetChanged();
969 }
970 
972 {
973  QList<QgsSymbol *> selectedSymbols;
974 
975  QItemSelectionModel *m = viewCategories->selectionModel();
976  QModelIndexList selectedIndexes = m->selectedRows( 1 );
977 
978  if ( m && !selectedIndexes.isEmpty() )
979  {
980  const QgsCategoryList &categories = mRenderer->categories();
981  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
982  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
983  {
984  int row = ( *indexIt ).row();
985  QgsSymbol *s = categories[row].symbol();
986  if ( s )
987  {
988  selectedSymbols.append( s );
989  }
990  }
991  }
992  return selectedSymbols;
993 }
994 
996 {
997  QgsCategoryList cl;
998 
999  QItemSelectionModel *m = viewCategories->selectionModel();
1000  QModelIndexList selectedIndexes = m->selectedRows( 1 );
1001 
1002  if ( m && !selectedIndexes.isEmpty() )
1003  {
1004  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1005  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1006  {
1007  cl.append( mModel->category( *indexIt ) );
1008  }
1009  }
1010  return cl;
1011 }
1012 
1014 {
1016  emit widgetChanged();
1017 }
1018 
1020 {
1022 }
1023 
1025 {
1026  viewCategories->selectionModel()->clear();
1027 }
1028 
1030 {
1031  int matched = matchToSymbols( QgsStyle::defaultStyle() );
1032  if ( matched > 0 )
1033  {
1034  QMessageBox::information( this, tr( "Matched Symbols" ),
1035  tr( "Matched %1 categories to symbols." ).arg( matched ) );
1036  }
1037  else
1038  {
1039  QMessageBox::warning( this, tr( "Matched Symbols" ),
1040  tr( "No categories could be matched to symbols in library." ) );
1041  }
1042 }
1043 
1045 {
1046  if ( !mLayer || !style )
1047  return 0;
1048 
1051  : QgsSymbol::Fill;
1052 
1053  QVariantList unmatchedCategories;
1054  QStringList unmatchedSymbols;
1055  const int matched = mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
1056 
1057  mModel->updateSymbology();
1058  return matched;
1059 }
1060 
1062 {
1063  QgsSettings settings;
1064  QString openFileDir = settings.value( QStringLiteral( "UI/lastMatchToSymbolsDir" ), QDir::homePath() ).toString();
1065 
1066  QString fileName = QFileDialog::getOpenFileName( this, tr( "Match to Symbols from File" ), openFileDir,
1067  tr( "XML files (*.xml *.XML)" ) );
1068  if ( fileName.isEmpty() )
1069  {
1070  return;
1071  }
1072 
1073  QFileInfo openFileInfo( fileName );
1074  settings.setValue( QStringLiteral( "UI/lastMatchToSymbolsDir" ), openFileInfo.absolutePath() );
1075 
1076  QgsStyle importedStyle;
1077  if ( !importedStyle.importXml( fileName ) )
1078  {
1079  QMessageBox::warning( this, tr( "Match to Symbols from File" ),
1080  tr( "An error occurred while reading file:\n%1" ).arg( importedStyle.errorString() ) );
1081  return;
1082  }
1083 
1084  int matched = matchToSymbols( &importedStyle );
1085  if ( matched > 0 )
1086  {
1087  QMessageBox::information( this, tr( "Match to Symbols from File" ),
1088  tr( "Matched %1 categories to symbols from file." ).arg( matched ) );
1089  }
1090  else
1091  {
1092  QMessageBox::warning( this, tr( "Match to Symbols from File" ),
1093  tr( "No categories could be matched to symbols in file." ) );
1094  }
1095 }
1096 
1098 {
1099  std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
1100  if ( !tempSymbol )
1101  return;
1102 
1103  const QList<int> selectedCats = selectedCategories();
1104  if ( !selectedCats.isEmpty() )
1105  {
1106  for ( int idx : selectedCats )
1107  {
1108  if ( mRenderer->categories().at( idx ).symbol()->type() != tempSymbol->type() )
1109  continue;
1110 
1111  std::unique_ptr< QgsSymbol > newCatSymbol( tempSymbol->clone() );
1112  if ( selectedCats.count() > 1 )
1113  {
1114  //if updating multiple categories, retain the existing category colors
1115  newCatSymbol->setColor( mRenderer->categories().at( idx ).symbol()->color() );
1116  }
1117  mRenderer->updateCategorySymbol( idx, newCatSymbol.release() );
1118  }
1119  emit widgetChanged();
1120  }
1121 }
1122 
1123 void QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector( QgsPanelWidget *container )
1124 {
1125  QgsSymbolSelectorWidget *dlg = qobject_cast<QgsSymbolSelectorWidget *>( container );
1126  if ( !dlg )
1127  return;
1128 
1129  delete dlg->symbol();
1130 }
1131 
1132 void QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget()
1133 {
1134  QgsSymbolSelectorWidget *dlg = qobject_cast<QgsSymbolSelectorWidget *>( sender() );
1135  mCategorizedSymbol.reset( dlg->symbol()->clone() );
1136 
1138 }
1139 
1140 void QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton()
1141 {
1142  mCategorizedSymbol.reset( btnChangeCategorizedSymbol->symbol()->clone() );
1143 
1145 }
1146 
1148 {
1149  // When there is a selection, change the selected symbols only
1150  QItemSelectionModel *m = viewCategories->selectionModel();
1151  QModelIndexList i = m->selectedRows();
1152 
1153  if ( m && !i.isEmpty() )
1154  {
1155  QList<int> selectedCats = selectedCategories();
1156 
1157  if ( !selectedCats.isEmpty() )
1158  {
1159  const auto constSelectedCats = selectedCats;
1160  for ( int idx : constSelectedCats )
1161  {
1162  QgsSymbol *newCatSymbol = mCategorizedSymbol->clone();
1163  if ( selectedCats.count() > 1 )
1164  {
1165  //if updating multiple categories, retain the existing category colors
1166  newCatSymbol->setColor( mRenderer->categories().at( idx ).symbol()->color() );
1167  }
1168  mRenderer->updateCategorySymbol( idx, newCatSymbol );
1169  }
1170  }
1171  }
1172  else
1173  {
1174  mRenderer->updateSymbols( mCategorizedSymbol.get() );
1175  }
1176 
1177  mModel->updateSymbology();
1178  emit widgetChanged();
1179 }
1180 
1182 {
1183  if ( !event )
1184  {
1185  return;
1186  }
1187 
1188  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1189  {
1190  mCopyBuffer.clear();
1191  mCopyBuffer = selectedCategoryList();
1192  }
1193  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1194  {
1195  QgsCategoryList::const_iterator rIt = mCopyBuffer.constBegin();
1196  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1197  {
1198  mModel->addCategory( *rIt );
1199  }
1200  }
1201 }
1202 
1203 QgsExpressionContext QgsCategorizedSymbolRendererWidget::createExpressionContext() const
1204 {
1205  QgsExpressionContext expContext;
1209 
1210  if ( mContext.mapCanvas() )
1211  {
1214  }
1215  else
1216  {
1218  }
1219 
1220  if ( vectorLayer() )
1222 
1223  // additional scopes
1224  const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
1225  for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
1226  {
1227  expContext.appendScope( new QgsExpressionContextScope( scope ) );
1228  }
1229 
1230  return expContext;
1231 }
1232 
1233 void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
1234 {
1235  QgsMarkerSymbol *s = static_cast<QgsMarkerSymbol *>( mCategorizedSymbol.get() ); // this should be only enabled for marker symbols
1236  QgsDataDefinedSizeLegendWidget *panel = createDataDefinedSizeLegendWidget( s, mRenderer->dataDefinedSizeLegend() );
1237  if ( panel )
1238  {
1239  connect( panel, &QgsPanelWidget::widgetChanged, this, [ = ]
1240  {
1241  mRenderer->setDataDefinedSizeLegend( panel->dataDefinedSizeLegend() );
1242  emit widgetChanged();
1243  } );
1244  openPanel( panel ); // takes ownership of the panel
1245  }
1246 }
1247 
1248 void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
1249 {
1250  const QgsCategoryList &categories = mRenderer->categories();
1251 
1252  QList<int> selectedCategoryIndexes = selectedCategories();
1253  QList< int > categoryIndexes;
1254 
1255  // filter out "" entry
1256  for ( int i : selectedCategoryIndexes )
1257  {
1258  QVariant v = categories.at( i ).value();
1259 
1260  if ( !v.isValid() || v == "" )
1261  {
1262  continue;
1263  }
1264 
1265  categoryIndexes.append( i );
1266  }
1267 
1268  if ( categoryIndexes.count() < 2 )
1269  return;
1270 
1271  QStringList labels;
1272  QVariantList values;
1273  values.reserve( categoryIndexes.count() );
1274  labels.reserve( categoryIndexes.count() );
1275  for ( int i : categoryIndexes )
1276  {
1277  QVariant v = categories.at( i ).value();
1278 
1279  if ( v.type() == QVariant::List )
1280  {
1281  values.append( v.toList() );
1282  }
1283  else
1284  values << v;
1285 
1286  labels << categories.at( i ).label();
1287  }
1288 
1289  // modify first category (basically we "merge up" into the first selected category)
1290  mRenderer->updateCategoryLabel( categoryIndexes.at( 0 ), labels.join( ',' ) );
1291  mRenderer->updateCategoryValue( categoryIndexes.at( 0 ), values );
1292 
1293  categoryIndexes.pop_front();
1294  mModel->deleteRows( categoryIndexes );
1295 
1296  emit widgetChanged();
1297 }
1298 
1299 void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
1300 {
1301  const QList<int> categoryIndexes = selectedCategories();
1302  if ( categoryIndexes.isEmpty() )
1303  return;
1304 
1305  const QgsCategoryList &categories = mRenderer->categories();
1306  for ( int i : categoryIndexes )
1307  {
1308  const QVariant v = categories.at( i ).value();
1309  if ( v.type() != QVariant::List )
1310  continue;
1311 
1312  const QVariantList list = v.toList();
1313  for ( int j = 1; j < list.count(); ++j )
1314  {
1315  mModel->addCategory( QgsRendererCategory( list.at( j ), categories.at( i ).symbol()->clone(), list.at( j ).toString(), categories.at( i ).renderState() ) );
1316  }
1317  mRenderer->updateCategoryValue( i, list.at( 0 ) );
1318  mRenderer->updateCategoryLabel( i, list.at( 0 ).toString() );
1319  }
1320 
1321  emit widgetChanged();
1322 }
1323 
1324 void QgsCategorizedSymbolRendererWidget::showContextMenu( QPoint )
1325 {
1326  mContextMenu->clear();
1327  const QList< QAction * > actions = contextMenu->actions();
1328  for ( QAction *act : actions )
1329  {
1330  mContextMenu->addAction( act );
1331  }
1332 
1333  mContextMenu->addSeparator();
1334 
1335  if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1336  {
1337  mContextMenu->addAction( mMergeCategoriesAction );
1338  }
1339  if ( viewCategories->selectionModel()->selectedRows().count() == 1 )
1340  {
1341  const QList<int> categoryIndexes = selectedCategories();
1342  const QgsCategoryList &categories = mRenderer->categories();
1343  const QVariant v = categories.at( categoryIndexes.at( 0 ) ).value();
1344  if ( v.type() == QVariant::List )
1345  mContextMenu->addAction( mUnmergeCategoriesAction );
1346  }
1347  else if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1348  {
1349  mContextMenu->addAction( mUnmergeCategoriesAction );
1350  }
1351 
1352  mContextMenu->exec( QCursor::pos() );
1353 }
1354 
1355 void QgsCategorizedSymbolRendererWidget::selectionChanged( const QItemSelection &, const QItemSelection & )
1356 {
1357  QList<int> selectedCats = selectedCategories();
1358  if ( !selectedCats.isEmpty() )
1359  {
1360  whileBlocking( btnChangeCategorizedSymbol )->setSymbol( mRenderer->categories().at( selectedCats.at( 0 ) ).symbol()->clone() );
1361  }
1362  else if ( mRenderer->sourceSymbol() )
1363  {
1364  whileBlocking( btnChangeCategorizedSymbol )->setSymbol( mRenderer->sourceSymbol()->clone() );
1365  }
1366  btnChangeCategorizedSymbol->setDialogTitle( selectedCats.size() == 1 ? mRenderer->categories().at( selectedCats.at( 0 ) ).label() : tr( "Symbol Settings" ) );
1367 }
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
QString label() const
Returns the label for this category, which is used to represent the category within legends and the l...
QgsFeatureRenderer * renderer() override
Returns pointer to the renderer (no transfer of ownership)
Class for parsing and evaluation of expressions (formerly called "search strings").
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
Wrapper for iterator of features from vector data provider or vector layer.
static QgsSymbol * symbolFromMimeData(const QMimeData *data)
Attempts to parse mime data as a symbol.
The QgsFieldExpressionWidget class reates a widget to choose fields and edit expressions It contains ...
void applyColorRamp()
Applies the color ramp passed on by the color ramp button.
virtual void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the renderer widget is shown, e.g., the associated map canvas and expressio...
bool dockMode()
Returns the dock mode state.
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:62
QgsSymbol * symbol()
Returns the symbol that is currently active in the widget.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
void applyChangeToSymbol()
Applies current symbol to selected categories, or to all categories if none is selected.
QgsSymbolWidgetContext context() const
Returns the context in which the renderer widget is shown, e.g., the associated map canvas and expres...
void showSymbolLevelsDialog(QgsFeatureRenderer *r)
show a dialog with renderer&#39;s symbol level settings
void colorRampChanged()
Emitted whenever a new color ramp is set for the button.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Base class for renderer settings widgets.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void changeSelectedSymbols()
Changes the selected symbols alone for the change button, if there is a selection.
QgsSymbol * symbol() const
Returns the symbol which will be used to render this category.
QgsVectorLayer * mLayer
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0)
Returns an icon preview for a color ramp.
QVariant evaluate()
Evaluate the feature and return the result.
void matchToSymbolsFromLibrary()
Replaces category symbols with the symbols from the users&#39; symbol library that have a matching name...
void changed()
Emitted when the symbol&#39;s settings are changed.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QColor color(double value) const override
Returns the color corresponding to a specified value.
void matchToSymbolsFromXml()
Prompts for selection of an xml file, then replaces category symbols with the symbols from the XML fi...
Base class for any widget that can be shown as a inline panel.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
int currentCategoryRow()
Returns row index for the currently selected category (-1 if on no selection)
Line symbol.
Definition: qgssymbol.h:87
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
A QProxyStyle subclass which correctly sets the base style to match the QGIS application style...
Definition: qgsproxystyle.h:30
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:895
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:74
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
The QgsMapSettings class contains configuration for rendering of the map.
QList< QgsRendererCategory > QgsCategoryList
SymbolType
Type of the symbol.
Definition: qgssymbol.h:84
static QgsCategoryList createCategories(const QVariantList &values, const QgsSymbol *symbol, QgsVectorLayer *layer=nullptr, const QString &fieldName=QString())
Create categories for a list of values.
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
Definition: qgssymbol.cpp:297
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
bool importXml(const QString &filename)
Imports the symbols and colorramps into the default style database from the given XML file...
Definition: qgsstyle.cpp:2489
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget...
Contains settings which reflect the context in which a symbol (or renderer) widget is shown...
QList< int > selectedCategories()
Returns a list of indexes for the categories under selection.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
const QgsVectorLayer * vectorLayer() const
Returns the vector layer associated with the widget.
void setContext(const QgsSymbolWidgetContext &context) override
Sets the context in which the renderer widget is shown, e.g., the associated map canvas and expressio...
std::unique_ptr< QgsCategorizedSymbolRenderer > mRenderer
QgsDataDefinedSizeLegendWidget * createDataDefinedSizeLegendWidget(const QgsMarkerSymbol *symbol, const QgsDataDefinedSizeLegend *ddsLegend)
Creates widget to setup data-defined size legend.
Symbol selector widget that can be used to select and build a symbol.
Single scope for storing variables and functions for use within a QgsExpressionContext.
virtual void setTotalColorCount(int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window&#39;s toolbar icons.
bool renderState() const
Returns true if the category is currently enabled and should be rendered.
void widgetChanged()
Emitted when the widget state changes.
QList< QgsSymbol * > selectedSymbols() override
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
Totally random color ramp.
Definition: qgscolorramp.h:427
QVariant value() const
Returns the value corresponding to this category.
int matchToSymbols(QgsStyle *style)
Replaces category symbols with the symbols from a style that have a matching name.
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
Marker symbol.
Definition: qgssymbol.h:86
Fill symbol.
Definition: qgssymbol.h:88
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:262
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object...
QgsSymbolWidgetContext mContext
Context in which widget is shown.
QgsCategorizedSymbolRendererWidget(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
QgsExpressionContextScope & expressionContextScope()
Returns a reference to the expression context scope for the map canvas.
Definition: qgsmapcanvas.h:583
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:450
QgsMessageBar * messageBar() const
Returns the message bar associated with the widget.
static QgsCategorizedSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
creates a QgsCategorizedSymbolRenderer from an existing renderer.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QString errorString()
Returns last error from load/save operation.
Definition: qgsstyle.h:622
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
Represents a vector layer which manages a vector based data sets.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
QList< QgsExpressionContextScope > additionalExpressionContextScopes() const
Returns the list of additional expression context scopes to show as available within the layer...
QAction * mPasteSymbolAction
Paste symbol action.
void setColor(const QColor &color)
Sets the color for the symbol.
Definition: qgssymbol.cpp:481