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