QGIS API Documentation 4.1.0-Master (31622b25bb0)
Loading...
Searching...
No Matches
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
19#include "qgscolorrampbutton.h"
20#include "qgscolorrampimpl.h"
23#include "qgsexpression.h"
25#include "qgsfeatureiterator.h"
26#include "qgsguiutils.h"
27#include "qgslogger.h"
28#include "qgsmapcanvas.h"
29#include "qgsmarkersymbol.h"
30#include "qgspanelwidget.h"
31#include "qgsproject.h"
33#include "qgssettings.h"
34#include "qgsstyle.h"
35#include "qgssymbol.h"
36#include "qgssymbollayerutils.h"
39#include "qgsvectorlayer.h"
40#include "qgsvectorlayerutils.h"
41
42#include <QClipboard>
43#include <QFileDialog>
44#include <QKeyEvent>
45#include <QMenu>
46#include <QMessageBox>
47#include <QPainter>
48#include <QPen>
49#include <QPointer>
50#include <QScreen>
51#include <QStandardItem>
52#include <QStandardItemModel>
53#include <QString>
54#include <QUuid>
55
56#include "moc_qgscategorizedsymbolrendererwidget.cpp"
57
58using namespace Qt::StringLiterals;
59
61
62QgsCategorizedSymbolRendererModel::QgsCategorizedSymbolRendererModel( QObject *parent, QScreen *screen )
63 : QgsTemplatedCategorizedRendererModel<QgsCategorizedSymbolRenderer>( parent, screen )
64{}
65
66Qt::ItemFlags QgsCategorizedSymbolRendererModel::extraFlags( const QModelIndex &index ) const
67{
68 // legend column
69 return index.column() == 2 ? Qt::ItemFlags( Qt::ItemIsEditable ) : Qt::NoItemFlags;
70}
71
72QIcon QgsCategorizedSymbolRendererModel::symbolIcon( const QgsCategorizedSymbolRenderer::Category &category ) const
73{
74 const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
75 return QgsSymbolLayerUtils::symbolPreviewIcon( category.symbol(), QSize( iconSize, iconSize ), 0, nullptr, QgsScreenProperties( mScreen.data() ) );
76}
77
78bool QgsCategorizedSymbolRendererModel::setExtraData( const QModelIndex &index, const QVariant &value )
79{
80 if ( index.column() == 2 ) // legend
81 {
82 mRenderer->updateCategoryLabel( index.row(), value.toString() );
83 emit dataChanged( index, index );
84 return true;
85 }
86
87 return false;
88}
89
90QVariant QgsCategorizedSymbolRendererModel::extraData( const QModelIndex &index, int role ) const
91{
92 if ( index.column() == 2 && ( role == Qt::DisplayRole || role == Qt::ToolTipRole || role == Qt::EditRole ) ) // legend
93 {
94 const QgsRendererCategory category = mRenderer->categories().value( index.row() );
95 return category.label();
96 }
97
98 return QVariant();
99}
100
101QVariant QgsCategorizedSymbolRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
102{
103 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
104 {
105 QStringList lst;
106 lst << tr( "Symbol" ) << tr( "Value" ) << tr( "Legend" );
107 return lst.value( section );
108 }
109 return QVariant();
110}
111
112int QgsCategorizedSymbolRendererModel::columnCount( const QModelIndex &index ) const
113{
114 Q_UNUSED( index )
115 return 3;
116}
117
118void QgsCategorizedSymbolRendererModel::sort( int column, Qt::SortOrder order )
119{
120 if ( column == symbolColumn() )
121 {
122 return;
123 }
124 if ( column == valueColumn() )
125 {
126 mRenderer->sortByValue( order );
127 }
128 else if ( column == 2 ) // legend
129 {
130 mRenderer->sortByLabel( order );
131 }
132 emit dataChanged( createIndex( 0, 0 ), createIndex( static_cast<int>( mRenderer->categories().size() ), 0 ) );
133}
134
135void QgsCategorizedSymbolRendererModel::onRowsMoved()
136{
137 emit rowsMoved();
138}
139
140// ------------------------------ View style --------------------------------
141QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QWidget *parent )
142 : QgsProxyStyle( parent )
143{}
144
145void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget ) const
146{
147 if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
148 {
149 QStyleOption opt( *option );
150 opt.rect.setLeft( 0 );
151 // draw always as line above, because we move item to that index
152 opt.rect.setHeight( 0 );
153 if ( widget )
154 opt.rect.setRight( widget->width() );
155 QProxyStyle::drawPrimitive( element, &opt, painter, widget );
156 return;
157 }
158 QProxyStyle::drawPrimitive( element, option, painter, widget );
159}
160
161
162QgsCategorizedRendererViewItemDelegate::QgsCategorizedRendererViewItemDelegate( QgsFieldExpressionWidget *expressionWidget, QObject *parent )
163 : QStyledItemDelegate( parent )
164 , mFieldExpressionWidget( expressionWidget )
165{}
166
167QWidget *QgsCategorizedRendererViewItemDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
168{
169 QMetaType::Type userType { static_cast<QMetaType::Type>( index.data( static_cast<int>( QgsCategorizedSymbolRendererWidget::CustomRole::Value ) ).userType() ) };
170
171 // In case of new values the type is not known
172 if ( userType == QMetaType::Type::QString && QgsVariantUtils::isNull( index.data( static_cast<int>( QgsCategorizedSymbolRendererWidget::CustomRole::Value ) ) ) )
173 {
174 bool isExpression;
175 bool isValid;
176 const QString fieldName { mFieldExpressionWidget->currentField( &isExpression, &isValid ) };
177 if ( !fieldName.isEmpty() && mFieldExpressionWidget->layer() && mFieldExpressionWidget->layer()->fields().lookupField( fieldName ) != -1 )
178 {
179 userType = mFieldExpressionWidget->layer()->fields().field( fieldName ).type();
180 }
181 else if ( isExpression && isValid )
182 {
183 // Try to guess the type from the expression return value
184 QgsFeature feat;
185 if ( mFieldExpressionWidget->layer()->getFeatures().nextFeature( feat ) )
186 {
187 QgsExpressionContext expressionContext;
190 expressionContext.appendScope( mFieldExpressionWidget->layer()->createExpressionContextScope() );
191 expressionContext.setFeature( feat );
192 QgsExpression exp { mFieldExpressionWidget->expression() };
193 const QVariant value = exp.evaluate( &expressionContext );
194 if ( !exp.hasEvalError() )
195 {
196 userType = static_cast<QMetaType::Type>( value.userType() );
197 }
198 }
199 }
200 }
201
202 QgsDoubleSpinBox *editor = nullptr;
203 switch ( userType )
204 {
205 case QMetaType::Type::Double:
206 {
207 editor = new QgsDoubleSpinBox( parent );
208 bool ok;
209 const QVariant value = index.data( static_cast<int>( QgsCategorizedSymbolRendererWidget::CustomRole::Value ) );
210 int decimals { 2 };
211 if ( value.toDouble( &ok ); ok )
212 {
213 const QString strVal { value.toString() };
214 const int dotPosition( strVal.indexOf( '.' ) );
215 if ( dotPosition >= 0 )
216 {
217 decimals = std::max<int>( 2, strVal.length() - dotPosition - 1 );
218 }
219 }
220 editor->setDecimals( decimals );
221 editor->setClearValue( 0 );
222 editor->setMaximum( std::numeric_limits<double>::max() );
223 editor->setMinimum( std::numeric_limits<double>::lowest() );
224 break;
225 }
226 case QMetaType::Type::Int:
227 {
228 editor = new QgsDoubleSpinBox( parent );
229 editor->setDecimals( 0 );
230 editor->setClearValue( 0 );
231 editor->setMaximum( std::numeric_limits<int>::max() );
232 editor->setMinimum( std::numeric_limits<int>::min() );
233 break;
234 }
235 case QMetaType::Type::QChar:
236 {
237 editor = new QgsDoubleSpinBox( parent );
238 editor->setDecimals( 0 );
239 editor->setClearValue( 0 );
240 editor->setMaximum( std::numeric_limits<char>::max() );
241 editor->setMinimum( std::numeric_limits<char>::min() );
242 break;
243 }
244 case QMetaType::Type::UInt:
245 {
246 editor = new QgsDoubleSpinBox( parent );
247 editor->setDecimals( 0 );
248 editor->setClearValue( 0 );
249 editor->setMaximum( std::numeric_limits<unsigned int>::max() );
250 editor->setMinimum( 0 );
251 break;
252 }
253 case QMetaType::Type::LongLong:
254 {
255 editor = new QgsDoubleSpinBox( parent );
256 editor->setDecimals( 0 );
257 editor->setClearValue( 0 );
258 editor->setMaximum( static_cast<double>( std::numeric_limits<qlonglong>::max() ) );
259 editor->setMinimum( std::numeric_limits<qlonglong>::min() );
260 break;
261 }
262 case QMetaType::Type::ULongLong:
263 {
264 editor = new QgsDoubleSpinBox( parent );
265 editor->setDecimals( 0 );
266 editor->setClearValue( 0 );
267 editor->setMaximum( static_cast<double>( std::numeric_limits<unsigned long long>::max() ) );
268 editor->setMinimum( 0 );
269 break;
270 }
271 default:
272 break;
273 }
274 return editor ? editor : QStyledItemDelegate::createEditor( parent, option, index );
275}
276
278
279// ------------------------------ Widget ------------------------------------
284
286 : QgsRendererWidget( layer, style )
287 , mContextMenu( new QMenu( this ) )
288{
289 // try to recognize the previous renderer
290 // (null renderer means "no previous renderer")
291 if ( renderer )
292 {
294 }
295 if ( !mRenderer )
296 {
297 mRenderer = std::make_unique<QgsCategorizedSymbolRenderer>( QString(), QgsCategoryList() );
298 if ( renderer )
299 renderer->copyRendererData( mRenderer.get() );
300 }
301
302 const QString attrName = mRenderer->classAttribute();
303 mOldClassificationAttribute = attrName;
304
305 // setup user interface
306 setupUi( this );
307 layout()->setContentsMargins( 0, 0, 0, 0 );
308
309 mExpressionWidget->setLayer( mLayer );
310 btnChangeCategorizedSymbol->setLayer( mLayer );
311 btnChangeCategorizedSymbol->registerExpressionContextGenerator( this );
312
313 // initiate color ramp button to random
314 btnColorRamp->setShowRandomColorRamp( true );
315
316 // set project default color ramp
317 std::unique_ptr<QgsColorRamp> colorRamp( QgsProject::instance()->styleSettings()->defaultColorRamp() );
318 if ( colorRamp )
319 {
320 btnColorRamp->setColorRamp( colorRamp.get() );
321 }
322 else
323 {
324 btnColorRamp->setRandomColorRamp();
325 }
326
327 mCategorizedSymbol.reset( QgsSymbol::defaultSymbol( mLayer->geometryType() ) );
328 if ( mCategorizedSymbol )
329 {
330 btnChangeCategorizedSymbol->setSymbolType( mCategorizedSymbol->type() );
331 btnChangeCategorizedSymbol->setSymbol( mCategorizedSymbol->clone() );
332 }
333
334 mModel = new QgsCategorizedSymbolRendererModel( this, screen() );
335 mModel->setRenderer( mRenderer.get() );
336
337 // update GUI from renderer
339
340 viewCategories->setModel( mModel );
341 viewCategories->resizeColumnToContents( 0 );
342 viewCategories->resizeColumnToContents( 1 );
343 viewCategories->resizeColumnToContents( 2 );
344 viewCategories->setItemDelegateForColumn( 1, new QgsCategorizedRendererViewItemDelegate( mExpressionWidget, viewCategories ) );
345
346 viewCategories->setStyle( new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
347 connect( viewCategories->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsCategorizedSymbolRendererWidget::selectionChanged );
348
349 connect( mModel, &QgsCategorizedSymbolRendererModel::rowsMoved, this, &QgsCategorizedSymbolRendererWidget::rowsMoved );
350 connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsPanelWidget::widgetChanged );
351
352 connect( mExpressionWidget, static_cast<void ( QgsFieldExpressionWidget::* )( const QString & )>( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsCategorizedSymbolRendererWidget::categoryColumnChanged );
353
354 connect( viewCategories, &QAbstractItemView::doubleClicked, this, &QgsCategorizedSymbolRendererWidget::categoriesDoubleClicked );
355 connect( viewCategories, &QTreeView::customContextMenuRequested, this, &QgsCategorizedSymbolRendererWidget::showContextMenu );
356
357 connect( btnChangeCategorizedSymbol, &QgsSymbolButton::changed, this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton );
358
359 connect( btnAddCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::addCategories );
360 connect( btnDeleteCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::deleteCategories );
361 connect( btnDeleteAllCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::deleteAllCategories );
362 connect( btnDeleteUnusedCategories, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::deleteUnusedCategories );
363 connect( btnAddCategory, &QAbstractButton::clicked, this, &QgsCategorizedSymbolRendererWidget::addCategory );
364
366
367 // menus for data-defined rotation/size
368 QMenu *advMenu = new QMenu;
369
370 advMenu->addAction( tr( "Match to Saved Symbols" ), this, &QgsCategorizedSymbolRendererWidget::matchToSymbolsFromLibrary );
371 advMenu->addAction( tr( "Match to Symbols from File…" ), this, &QgsCategorizedSymbolRendererWidget::matchToSymbolsFromXml );
372 mActionLevels = advMenu->addAction( tr( "Symbol Levels…" ), this, &QgsCategorizedSymbolRendererWidget::showSymbolLevels );
374 {
375 QAction *actionDdsLegend = advMenu->addAction( tr( "Data-defined Size Legend…" ) );
376 // only from Qt 5.6 there is convenience addAction() with new style connection
377 connect( actionDdsLegend, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
378 }
379
380 btnAdvanced->setMenu( advMenu );
381
382 mExpressionWidget->registerExpressionContextGenerator( this );
383
384 mMergeCategoriesAction = new QAction( tr( "Merge Categories" ), this );
385 connect( mMergeCategoriesAction, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::mergeSelectedCategories );
386 mUnmergeCategoriesAction = new QAction( tr( "Unmerge Categories" ), this );
387 connect( mUnmergeCategoriesAction, &QAction::triggered, this, &QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories );
388
389 connect( mContextMenu, &QMenu::aboutToShow, this, [this] {
390 const std::unique_ptr<QgsSymbol> tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
391 mPasteSymbolAction->setEnabled( static_cast<bool>( tempSymbol ) );
392 } );
393}
394
399
401{
402 // Note: This assumes that the signals for UI element changes have not
403 // yet been connected, so that the updates to color ramp, symbol, etc
404 // don't override existing customizations.
405
406 //mModel->setRenderer ( mRenderer ); // necessary?
407
408 // set column
409 const QString attrName = mRenderer->classAttribute();
410 mExpressionWidget->setField( attrName );
411
412 // set source symbol
413 if ( mRenderer->sourceSymbol() )
414 {
415 mCategorizedSymbol.reset( mRenderer->sourceSymbol()->clone() );
416 whileBlocking( btnChangeCategorizedSymbol )->setSymbol( mCategorizedSymbol->clone() );
417 }
418
419 // if a color ramp attached to the renderer, enable the color ramp button
420 if ( mRenderer->sourceColorRamp() )
421 {
422 btnColorRamp->setColorRamp( mRenderer->sourceColorRamp() );
423 }
424}
425
430
432{
434 btnChangeCategorizedSymbol->setMapCanvas( context.mapCanvas() );
435 btnChangeCategorizedSymbol->setMessageBar( context.messageBar() );
436}
437
439{
440 delete mActionLevels;
441 mActionLevels = nullptr;
442}
443
445{
446 const QList<int> selectedCats = selectedCategories();
447
448 if ( !selectedCats.isEmpty() )
449 {
450 QgsSymbol *newSymbol = mCategorizedSymbol->clone();
451 QgsSymbolSelectorDialog dlg( newSymbol, mStyle, mLayer, this );
452 dlg.setContext( context() );
453 if ( !dlg.exec() )
454 {
455 delete newSymbol;
456 return;
457 }
458
459 const auto constSelectedCats = selectedCats;
460 for ( const int idx : constSelectedCats )
461 {
462 const QgsRendererCategory category = mRenderer->categories().value( idx );
463
464 QgsSymbol *newCatSymbol = newSymbol->clone();
465 newCatSymbol->setColor( mRenderer->categories()[idx].symbol()->color() );
466 mRenderer->updateCategorySymbol( idx, newCatSymbol );
467 }
468 }
469}
470
472{
474 std::unique_ptr<QgsSymbol> newSymbol( mCategorizedSymbol->clone() );
475 if ( panel && panel->dockMode() )
476 {
478 widget->setContext( mContext );
479 connect( widget, &QgsPanelWidget::widgetChanged, this, [this, widget] { updateSymbolsFromWidget( widget ); } );
480 openPanel( widget );
481 }
482 else
483 {
484 QgsSymbolSelectorDialog dlg( newSymbol.get(), mStyle, mLayer, panel );
485 dlg.setContext( mContext );
486 if ( !dlg.exec() || !newSymbol )
487 {
488 return;
489 }
490
491 mCategorizedSymbol = std::move( newSymbol );
493 }
494}
495
496
499
501{
502 mRenderer->setClassAttribute( field );
503 emit widgetChanged();
504}
505
507{
508 if ( idx.isValid() && idx.column() == mModel->symbolColumn() )
510}
511
513{
514 const QgsRendererCategory category = mRenderer->categories().value( currentCategoryRow() );
515
516 std::unique_ptr<QgsSymbol> symbol;
517
518 if ( auto *lSymbol = category.symbol() )
519 {
520 symbol.reset( lSymbol->clone() );
521 }
522 else
523 {
524 symbol.reset( QgsSymbol::defaultSymbol( mLayer->geometryType() ) );
525 }
526
528 if ( panel && panel->dockMode() )
529 {
531 widget->setContext( mContext );
532 widget->setPanelTitle( category.label() );
533 connect( widget, &QgsPanelWidget::widgetChanged, this, [this, widget] { updateSymbolsFromWidget( widget ); } );
534 openPanel( widget );
535 }
536 else
537 {
538 QgsSymbolSelectorDialog dlg( symbol.get(), mStyle, mLayer, panel );
539 dlg.setContext( mContext );
540 if ( !dlg.exec() || !symbol )
541 {
542 return;
543 }
544
545 mCategorizedSymbol = std::move( symbol );
547 }
548}
549
550
552{
553 const QString attrName = mExpressionWidget->currentField();
554 bool valuesRetrieved;
555 const QList<QVariant> uniqueValues = QgsVectorLayerUtils::uniqueValues( mLayer, attrName, valuesRetrieved );
556 if ( !valuesRetrieved )
557 {
558 QgsDebugMsgLevel( u"Unable to retrieve values from layer %1 with expression %2"_s.arg( mLayer->name() ).arg( attrName ), 2 );
559 return;
560 }
561
562 // ask to abort if too many classes
563 if ( uniqueValues.size() >= 1000 )
564 {
565 const int res = QMessageBox::
566 warning( nullptr, tr( "Classify Categories" ), tr( "High number of classes. Classification would yield %n entries which might not be expected. Continue?", nullptr, uniqueValues.size() ), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel );
567 if ( res == QMessageBox::Cancel )
568 {
569 return;
570 }
571 }
572
573#if 0
574 DlgAddCategories dlg( mStyle, createDefaultSymbol(), unique_vals, this );
575 if ( !dlg.exec() )
576 return;
577#endif
578
580 bool deleteExisting = false;
581
582 if ( !mOldClassificationAttribute.isEmpty() && attrName != mOldClassificationAttribute && !mRenderer->categories().isEmpty() )
583 {
584 const int res = QMessageBox::question(
585 this,
586 tr( "Delete Classification" ),
587 tr(
588 "The classification field was changed from '%1' to '%2'.\n"
589 "Should the existing classes be deleted before classification?"
590 )
591 .arg( mOldClassificationAttribute, attrName ),
592 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel
593 );
594 if ( res == QMessageBox::Cancel )
595 {
596 return;
597 }
598
599 deleteExisting = ( res == QMessageBox::Yes );
600 }
601
602 // First element to apply coloring to
603 bool keepExistingColors = false;
604 if ( !deleteExisting )
605 {
606 QgsCategoryList prevCats = mRenderer->categories();
607 keepExistingColors = !prevCats.isEmpty();
608 QgsRandomColorRamp randomColors;
609 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
610 randomColors.setTotalColorCount( cats.size() );
611 for ( int i = 0; i < cats.size(); ++i )
612 {
613 bool contains = false;
614 const QVariant value = cats.at( i ).value();
615 for ( int j = 0; j < prevCats.size() && !contains; ++j )
616 {
617 const QVariant prevCatValue = prevCats.at( j ).value();
618 if ( prevCatValue.userType() == QMetaType::Type::QVariantList )
619 {
620 const QVariantList list = prevCatValue.toList();
621 for ( const QVariant &v : list )
622 {
623 if ( v == value )
624 {
625 contains = true;
626 break;
627 }
628 }
629 }
630 else
631 {
632 if ( prevCats.at( j ).value() == value )
633 {
634 contains = true;
635 }
636 }
637 if ( contains )
638 break;
639 }
640
641 if ( !contains )
642 {
643 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
644 {
645 // insure that append symbols have random colors
646 cats.at( i ).symbol()->setColor( randomColors.color( i ) );
647 }
648 prevCats.append( cats.at( i ) );
649 }
650 }
651 cats = prevCats;
652 }
653
654 mOldClassificationAttribute = attrName;
655
656 // TODO: if not all categories are desired, delete some!
657 /*
658 if (not dlg.readAllCats.isChecked())
659 {
660 cats2 = {}
661 for item in dlg.listCategories.selectedItems():
662 for k,c in cats.iteritems():
663 if item.text() == k.toString():
664 break
665 cats2[k] = c
666 cats = cats2
667 }
668 */
669
670 // recreate renderer
671 auto r = std::make_unique<QgsCategorizedSymbolRenderer>( attrName, cats );
672 r->setSourceSymbol( mCategorizedSymbol->clone() );
673 std::unique_ptr<QgsColorRamp> ramp( btnColorRamp->colorRamp() );
674 if ( ramp )
675 r->setSourceColorRamp( ramp->clone() );
676
677 if ( mModel )
678 {
679 mModel->setRenderer( r.get() );
680 }
681 mRenderer = std::move( r );
682 if ( !keepExistingColors && ramp )
684 emit widgetChanged();
685}
686
688{
689 if ( !btnColorRamp->isNull() )
690 {
691 mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
692 }
693 mModel->updateSymbology();
694}
695
697{
698 const QModelIndex idx = viewCategories->selectionModel()->currentIndex();
699 if ( !idx.isValid() )
700 return -1;
701 return idx.row();
702}
703
705{
706 QList<int> rows;
707 const QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
708
709 const auto constSelectedRows = selectedRows;
710 for ( const QModelIndex &r : constSelectedRows )
711 {
712 if ( r.isValid() )
713 {
714 rows.append( r.row() );
715 }
716 }
717 return rows;
718}
719
721{
722 const QList<int> categoryIndexes = selectedCategories();
723 mModel->deleteRows( categoryIndexes );
724 emit widgetChanged();
725}
726
728{
729 mModel->removeAllRows();
730 emit widgetChanged();
731}
732
734{
735 if ( !mRenderer )
736 return;
737 const QString attrName = mExpressionWidget->currentField();
738 bool valuesRetrieved;
739 const QList<QVariant> uniqueValues = QgsVectorLayerUtils::uniqueValues( mLayer, attrName, valuesRetrieved );
740 if ( !valuesRetrieved )
741 {
742 QgsDebugMsgLevel( u"Unable to retrieve values from layer %1 with expression %2"_s.arg( mLayer->name() ).arg( attrName ), 2 );
743 }
744
745 const QgsCategoryList catList = mRenderer->categories();
746
747 QList<int> unusedIndexes;
748
749 for ( int i = 0; i < catList.size(); ++i )
750 {
751 const QgsRendererCategory cat = catList.at( i );
752 if ( !uniqueValues.contains( cat.value() ) )
753 {
754 unusedIndexes.append( i );
755 }
756 }
757 mModel->deleteRows( unusedIndexes );
758 emit widgetChanged();
759}
760
761QList<QVariant> QgsCategorizedSymbolRendererWidget::layerUniqueValues( const QString &attrName )
762{
763 bool valuesRetrieved;
764 const QList<QVariant> uniqueValues = QgsVectorLayerUtils::uniqueValues( mLayer, attrName, valuesRetrieved );
765 if ( !valuesRetrieved )
766 {
767 QgsDebugMsgLevel( u"Unable to retrieve values from layer %1 with expression %2"_s.arg( mLayer->name() ).arg( attrName ), 2 );
768 }
769 return uniqueValues;
770}
771
773{
774 if ( !mModel )
775 return;
776 QgsSymbol *symbol = QgsSymbol::defaultSymbol( mLayer->geometryType() );
777 const QgsRendererCategory cat( QVariant(), symbol, QString(), true );
778 mModel->addCategory( cat );
779 emit widgetChanged();
780}
781
783{
784 QList<QgsSymbol *> selectedSymbols;
785
786 QItemSelectionModel *m = viewCategories->selectionModel();
787 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
788
789 if ( !selectedIndexes.isEmpty() )
790 {
791 const QgsCategoryList &categories = mRenderer->categories();
792 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
793 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
794 {
795 const int row = ( *indexIt ).row();
796 QgsSymbol *s = categories[row].symbol();
797 if ( s )
798 {
799 selectedSymbols.append( s );
800 }
801 }
802 }
803 return selectedSymbols;
804}
805
807{
809
810 QItemSelectionModel *m = viewCategories->selectionModel();
811 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
812
813 if ( !selectedIndexes.isEmpty() )
814 {
815 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
816 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
817 {
818 cl.append( mModel->category( *indexIt ) );
819 }
820 }
821 return cl;
822}
823
829
834
836{
837 viewCategories->selectionModel()->clear();
838}
839
841{
842 const int matched = matchToSymbols( QgsStyle::defaultStyle() );
843 if ( matched > 0 )
844 {
845 QMessageBox::information( this, tr( "Matched Symbols" ), tr( "Matched %n categories to symbols.", nullptr, matched ) );
846 }
847 else
848 {
849 QMessageBox::warning( this, tr( "Matched Symbols" ), tr( "No categories could be matched to symbols in library." ) );
850 }
851}
852
854{
855 if ( !mLayer || !style )
856 return 0;
857
861
862 QVariantList unmatchedCategories;
863 QStringList unmatchedSymbols;
864 const int matched = mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
865
866 mModel->updateSymbology();
867 return matched;
868}
869
871{
872 QgsSettings settings;
873 const QString openFileDir = settings.value( u"UI/lastMatchToSymbolsDir"_s, QDir::homePath() ).toString();
874
875 const QString fileName = QFileDialog::getOpenFileName( this, tr( "Match to Symbols from File" ), openFileDir, tr( "XML files (*.xml *.XML)" ) );
876 if ( fileName.isEmpty() )
877 {
878 return;
879 }
880
881 const QFileInfo openFileInfo( fileName );
882 settings.setValue( u"UI/lastMatchToSymbolsDir"_s, openFileInfo.absolutePath() );
883
884 QgsStyle importedStyle;
885 if ( !importedStyle.importXml( fileName ) )
886 {
887 QMessageBox::warning( this, tr( "Match to Symbols from File" ), tr( "An error occurred while reading file:\n%1" ).arg( importedStyle.errorString() ) );
888 return;
889 }
890
891 const int matched = matchToSymbols( &importedStyle );
892 if ( matched > 0 )
893 {
894 QMessageBox::information( this, tr( "Match to Symbols from File" ), tr( "Matched %n categories to symbols from file.", nullptr, matched ) );
895 }
896 else
897 {
898 QMessageBox::warning( this, tr( "Match to Symbols from File" ), tr( "No categories could be matched to symbols in file." ) );
899 }
900}
901
903{
904 for ( const QgsLegendSymbolItem &legendSymbol : levels )
905 {
906 QgsSymbol *sym = legendSymbol.symbol();
907 for ( int layer = 0; layer < sym->symbolLayerCount(); layer++ )
908 {
909 mRenderer->setLegendSymbolItem( legendSymbol.ruleKey(), sym->clone() );
910 }
911 }
912 mRenderer->setUsingSymbolLevels( enabled );
913 mModel->updateSymbology();
914 emit widgetChanged();
915}
916
918{
919 std::unique_ptr<QgsSymbol> tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
920 if ( !tempSymbol )
921 return;
922
923 const QList<int> selectedCats = selectedCategories();
924 if ( !selectedCats.isEmpty() )
925 {
926 for ( const int idx : selectedCats )
927 {
928 if ( mRenderer->categories().at( idx ).symbol()->type() != tempSymbol->type() )
929 continue;
930
931 std::unique_ptr<QgsSymbol> newCatSymbol( tempSymbol->clone() );
932 if ( selectedCats.count() > 1 )
933 {
934 //if updating multiple categories, retain the existing category colors
935 newCatSymbol->setColor( mRenderer->categories().at( idx ).symbol()->color() );
936 }
937 mRenderer->updateCategorySymbol( idx, newCatSymbol.release() );
938 }
939 emit widgetChanged();
940 }
941}
942
943void QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget( QgsSymbolSelectorWidget *widget )
944{
945 mCategorizedSymbol.reset( widget->symbol()->clone() );
946
948}
949
950void QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton()
951{
952 mCategorizedSymbol.reset( btnChangeCategorizedSymbol->symbol()->clone() );
953
955}
956
958{
959 // When there is a selection, change the selected symbols only
960 QItemSelectionModel *m = viewCategories->selectionModel();
961 const QModelIndexList i = m->selectedRows();
962
963 if ( !i.isEmpty() )
964 {
965 const QList<int> selectedCats = selectedCategories();
966
967 if ( !selectedCats.isEmpty() )
968 {
969 const auto constSelectedCats = selectedCats;
970 for ( const int idx : constSelectedCats )
971 {
972 QgsSymbol *newCatSymbol = mCategorizedSymbol->clone();
973 if ( selectedCats.count() > 1 )
974 {
975 //if updating multiple categories, retain the existing category colors
976 newCatSymbol->setColor( mRenderer->categories().at( idx ).symbol()->color() );
977 }
978 mRenderer->updateCategorySymbol( idx, newCatSymbol );
979 }
980 }
981 }
982 else
983 {
984 mRenderer->updateSymbols( mCategorizedSymbol.get() );
985 }
986
987 mModel->updateSymbology();
988 emit widgetChanged();
989}
990
992{
993 if ( !event )
994 {
995 return;
996 }
997
998 if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
999 {
1000 mCopyBuffer.clear();
1001 mCopyBuffer = selectedCategoryList();
1002 }
1003 else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1004 {
1005 QgsCategoryList::iterator rIt = mCopyBuffer.begin();
1006 for ( ; rIt != mCopyBuffer.end(); ++rIt )
1007 {
1008 rIt->mUuid = QUuid::createUuid().toString();
1009 mModel->addCategory( *rIt );
1010 }
1011 }
1012}
1013
1015{
1016 QgsExpressionContext expContext;
1017 if ( auto *lMapCanvas = mContext.mapCanvas() )
1018 {
1019 expContext = lMapCanvas->createExpressionContext();
1020 }
1021 else
1022 {
1023 expContext
1028 }
1029
1030 if ( auto *lVectorLayer = vectorLayer() )
1031 expContext << QgsExpressionContextUtils::layerScope( lVectorLayer );
1032
1033 // additional scopes
1034 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
1035 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
1036 {
1037 expContext.appendScope( new QgsExpressionContextScope( scope ) );
1038 }
1039
1040 return expContext;
1041}
1042
1043void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
1044{
1045 QgsMarkerSymbol *s = static_cast<QgsMarkerSymbol *>( mCategorizedSymbol.get() ); // this should be only enabled for marker symbols
1046 QgsDataDefinedSizeLegendWidget *panel = createDataDefinedSizeLegendWidget( s, mRenderer->dataDefinedSizeLegend() );
1047 if ( panel )
1048 {
1049 connect( panel, &QgsPanelWidget::widgetChanged, this, [this, panel] {
1050 mRenderer->setDataDefinedSizeLegend( panel->dataDefinedSizeLegend() );
1051 emit widgetChanged();
1052 } );
1053 openPanel( panel ); // takes ownership of the panel
1054 }
1055}
1056
1057void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
1058{
1059 const QgsCategoryList &categories = mRenderer->categories();
1060
1061 const QList<int> selectedCategoryIndexes = selectedCategories();
1062 QList<int> categoryIndexes;
1063
1064 // filter out "" entry
1065 for ( const int i : selectedCategoryIndexes )
1066 {
1067 const QVariant v = categories.at( i ).value();
1068
1069 if ( !v.isValid() || v == "" )
1070 {
1071 continue;
1072 }
1073
1074 categoryIndexes.append( i );
1075 }
1076
1077 if ( categoryIndexes.count() < 2 )
1078 return;
1079
1080 QStringList labels;
1081 QVariantList values;
1082 values.reserve( categoryIndexes.count() );
1083 labels.reserve( categoryIndexes.count() );
1084 for ( const int i : categoryIndexes )
1085 {
1086 const QVariant v = categories.at( i ).value();
1087
1088 if ( v.userType() == QMetaType::Type::QVariantList )
1089 {
1090 values.append( v.toList() );
1091 }
1092 else
1093 values << v;
1094
1095 labels << categories.at( i ).label();
1096 }
1097
1098 // modify first category (basically we "merge up" into the first selected category)
1099 mRenderer->updateCategoryLabel( categoryIndexes.at( 0 ), labels.join( ',' ) );
1100 mRenderer->updateCategoryValue( categoryIndexes.at( 0 ), values );
1101
1102 categoryIndexes.pop_front();
1103 mModel->deleteRows( categoryIndexes );
1104
1105 emit widgetChanged();
1106}
1107
1108void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
1109{
1110 const QList<int> categoryIndexes = selectedCategories();
1111 if ( categoryIndexes.isEmpty() )
1112 return;
1113
1114 const QgsCategoryList &categories = mRenderer->categories();
1115 for ( const int i : categoryIndexes )
1116 {
1117 const QVariant v = categories.at( i ).value();
1118 if ( v.userType() != QMetaType::Type::QVariantList )
1119 continue;
1120
1121 const QVariantList list = v.toList();
1122 for ( int j = 1; j < list.count(); ++j )
1123 {
1124 mModel->addCategory( QgsRendererCategory( list.at( j ), categories.at( i ).symbol()->clone(), list.at( j ).toString(), categories.at( i ).renderState() ) );
1125 }
1126 mRenderer->updateCategoryValue( i, list.at( 0 ) );
1127 mRenderer->updateCategoryLabel( i, list.at( 0 ).toString() );
1128 }
1129
1130 emit widgetChanged();
1131}
1132
1133void QgsCategorizedSymbolRendererWidget::showContextMenu( QPoint )
1134{
1135 mContextMenu->clear();
1136 const QList<QAction *> actions = contextMenu->actions();
1137 for ( QAction *act : actions )
1138 {
1139 mContextMenu->addAction( act );
1140 }
1141
1142 mContextMenu->addSeparator();
1143
1144 if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1145 {
1146 mContextMenu->addAction( mMergeCategoriesAction );
1147 }
1148 if ( viewCategories->selectionModel()->selectedRows().count() == 1 )
1149 {
1150 const QList<int> categoryIndexes = selectedCategories();
1151 const QgsCategoryList &categories = mRenderer->categories();
1152 const QVariant v = categories.at( categoryIndexes.at( 0 ) ).value();
1153 if ( v.userType() == QMetaType::Type::QVariantList )
1154 mContextMenu->addAction( mUnmergeCategoriesAction );
1155 }
1156 else if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1157 {
1158 mContextMenu->addAction( mUnmergeCategoriesAction );
1159 }
1160
1161 mContextMenu->exec( QCursor::pos() );
1162}
1163
1164void QgsCategorizedSymbolRendererWidget::selectionChanged( const QItemSelection &, const QItemSelection & )
1165{
1166 const QList<int> selectedCats = selectedCategories();
1167 if ( !selectedCats.isEmpty() )
1168 {
1169 whileBlocking( btnChangeCategorizedSymbol )->setSymbol( mRenderer->categories().at( selectedCats.at( 0 ) ).symbol()->clone() );
1170 }
1171 else if ( mRenderer->sourceSymbol() )
1172 {
1173 whileBlocking( btnChangeCategorizedSymbol )->setSymbol( mRenderer->sourceSymbol()->clone() );
1174 }
1175 btnChangeCategorizedSymbol->setDialogTitle( selectedCats.size() == 1 ? mRenderer->categories().at( selectedCats.at( 0 ) ).label() : tr( "Symbol Settings" ) );
1176}
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
SymbolType
Symbol types.
Definition qgis.h:637
@ Marker
Marker symbol.
Definition qgis.h:638
@ Line
Line symbol.
Definition qgis.h:639
@ Fill
Fill symbol.
Definition qgis.h:640
void changeSelectedSymbols()
Changes the selected symbols alone for the change button, if there is a selection.
std::unique_ptr< QgsCategorizedSymbolRenderer > mRenderer
void applyColorRamp()
Applies the color ramp passed on by the color ramp button.
void deleteUnusedCategories()
Deletes unused categories from the widget which are not used by the layer renderer.
QgsFeatureRenderer * renderer() override
Returns pointer to the renderer (no transfer of ownership).
Q_DECL_DEPRECATED QList< QVariant > layerUniqueValues(const QString &attrName)
Returns the list of unique values in the current widget's layer for attribute name attrName.
void disableSymbolLevels() override
Disables symbol level modification on the widget.
void matchToSymbolsFromXml()
Prompts for selection of an xml file, then replaces category symbols with the symbols from the XML fi...
int currentCategoryRow()
Returns row index for the currently selected category (-1 if on no selection).
void matchToSymbolsFromLibrary()
Replaces category symbols with the symbols from the users' symbol library that have a matching name.
void setContext(const QgsSymbolWidgetContext &context) override
Sets the context in which the renderer widget is shown, e.g., the associated map canvas and expressio...
void setSymbolLevels(const QgsLegendSymbolList &levels, bool enabled) override
Sets the symbol levels for the renderer defined in the widget.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void applyChangeToSymbol()
Applies current symbol to selected categories, or to all categories if none is selected.
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
QList< int > selectedCategories()
Returns a list of indexes for the categories under selection.
QList< QgsSymbol * > selectedSymbols() override
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
QgsCategorizedSymbolRendererWidget(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
int matchToSymbols(QgsStyle *style)
Replaces category symbols with the symbols from a style that have a matching name.
A feature renderer which represents features using a list of renderer categories.
static QgsCategorizedSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer, QgsVectorLayer *layer=nullptr)
Creates a new QgsCategorizedSymbolRenderer from an existing renderer.
static QgsCategoryList createCategories(const QVariantList &values, const QgsSymbol *symbol, QgsVectorLayer *layer=nullptr, const QString &fieldName=QString())
Create categories for a list of values.
void colorRampChanged()
Emitted whenever a new color ramp is set for the button.
Widget for configuration of appearance of legend for marker symbols with data-defined size.
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration as set up in the dialog (may be nullptr). Ownership is passed to the caller.
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value.
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...
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QVariant evaluate()
Evaluate the feature and return the result.
Abstract base class for all 2D vector feature renderers.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
A widget for selection of layer fields or expression creation.
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
Stores information about one class/rule of a vector layer renderer in a unified way that can be used ...
Contains configuration for rendering maps.
A marker symbol type, for rendering Point and MultiPoint geometries.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
bool dockMode() const
Returns the dock mode state.
QgsPanelWidget(QWidget *parent=nullptr)
Base class for any widget that can be shown as an inline panel.
void widgetChanged()
Emitted when the widget state changes.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
static QgsProject * instance()
Returns the QgsProject singleton instance.
A QProxyStyle subclass which correctly sets the base style to match the QGIS application style,...
A color ramp consisting of random colors, constrained within component ranges.
virtual void setTotalColorCount(int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
QColor color(double value) const override
Returns the color corresponding to a specified value.
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
QgsSymbol * symbol() const
Returns the symbol which will be used to render this category.
QVariant value() const
Returns the value corresponding to this category.
QString label() const
Returns the label for this category, which is used to represent the category within legends and the l...
QAction * mPasteSymbolAction
Paste symbol action.
QgsRendererWidget(QgsVectorLayer *layer, QgsStyle *style)
void showSymbolLevelsDialog(QgsFeatureRenderer *r)
Show a dialog with renderer's symbol level settings.
QgsSymbolWidgetContext mContext
Context in which widget is shown.
virtual void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the renderer widget is shown, e.g., the associated map canvas and expressio...
QgsDataDefinedSizeLegendWidget * createDataDefinedSizeLegendWidget(const QgsMarkerSymbol *symbol, const QgsDataDefinedSizeLegend *ddsLegend)
Creates widget to setup data-defined size legend.
const QgsVectorLayer * vectorLayer() const
Returns the vector layer associated with the widget.
QgsSymbolWidgetContext context() const
Returns the context in which the renderer widget is shown, e.g., the associated map canvas and expres...
QgsVectorLayer * mLayer
Stores properties relating to a screen.
Stores settings for use within QGIS.
Definition qgssettings.h:68
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
A database of saved style entities, including symbols, color ramps, text formats and others.
Definition qgsstyle.h:91
QString errorString() const
Returns the last error from a load() operation.
Definition qgsstyle.h:970
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:164
bool importXml(const QString &filename)
Imports the symbols and colorramps into the default style database from the given XML file.
void changed()
Emitted when the symbol's settings are changed.
static std::unique_ptr< QgsSymbol > symbolFromMimeData(const QMimeData *data)
Attempts to parse mime data as a symbol.
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0, QgsLegendPatchShape *shape=nullptr, const QgsScreenProperties &screen=QgsScreenProperties())
Returns an icon preview for a color ramp.
A dialog that can be used to select and build a symbol.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
Symbol selector widget that can be used to select and build a symbol.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
QgsSymbol * symbol()
Returns the symbol that is currently active in the widget.
static QgsSymbolSelectorWidget * createWidgetWithSymbolOwnership(std::unique_ptr< QgsSymbol > symbol, QgsStyle *style, QgsVectorLayer *vl, QWidget *parent=nullptr)
Creates a QgsSymbolSelectorWidget which takes ownership of a symbol and maintains the ownership for t...
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
Abstract base class for all rendered symbols.
Definition qgssymbol.h:227
void setColor(const QColor &color) const
Sets the color for the symbol.
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition qgssymbol.h:357
static QgsSymbol * defaultSymbol(Qgis::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QList< QVariant > uniqueValues(const QgsVectorLayer *layer, const QString &fieldOrExpression, bool &ok, bool selectedOnly=false, int limit=-1, QgsFeedback *feedback=nullptr)
Fetches all unique values from a specified field name or expression.
Represents a vector layer which manages a vector based dataset.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:7127
QList< QgsRendererCategory > QgsCategoryList
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63