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