17#include "moc_qgscategorizedsymbolrendererwidget.cpp"
46#include <QStandardItemModel>
47#include <QStandardItem>
58QgsCategorizedSymbolRendererModel::QgsCategorizedSymbolRendererModel( QObject *parent, QScreen *screen )
59 : QAbstractItemModel( parent )
60 , mMimeFormat( QStringLiteral(
"application/x-qgscategorizedsymbolrendererv2model" ) )
69 beginRemoveRows( QModelIndex(), 0, std::max<int>( mRenderer->categories().size() - 1, 0 ) );
78 beginInsertRows( QModelIndex(), 0, renderer->
categories().size() - 1 );
88 const int idx = mRenderer->categories().size();
89 beginInsertRows( QModelIndex(), idx, idx );
90 mRenderer->addCategory( cat );
101 const int row = index.row();
102 if ( row >= catList.size() )
106 return catList.at( row );
110Qt::ItemFlags QgsCategorizedSymbolRendererModel::flags(
const QModelIndex &index )
const
113 if ( !index.isValid() || !mRenderer )
115 return Qt::ItemIsDropEnabled;
118 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable;
119 if ( index.column() == 1 )
122 if ( category.
value().userType() != QMetaType::Type::QVariantList )
124 flags |= Qt::ItemIsEditable;
127 else if ( index.column() == 2 )
129 flags |= Qt::ItemIsEditable;
134Qt::DropActions QgsCategorizedSymbolRendererModel::supportedDropActions()
const
136 return Qt::MoveAction;
139QVariant QgsCategorizedSymbolRendererModel::data(
const QModelIndex &index,
int role )
const
141 if ( !index.isValid() || !mRenderer )
148 case Qt::CheckStateRole:
150 if ( index.column() == 0 )
152 return category.
renderState() ? Qt::Checked : Qt::Unchecked;
157 case Qt::DisplayRole:
158 case Qt::ToolTipRole:
160 switch ( index.column() )
164 if ( category.
value().userType() == QMetaType::Type::QVariantList )
167 const QVariantList list = category.
value().toList();
168 res.reserve( list.size() );
169 for (
const QVariant &v : list )
172 if ( role == Qt::DisplayRole )
173 return res.join(
';' );
175 return res.join(
'\n' );
179 return tr(
"all other values" );
187 return category.
label();
197 italicFont.setItalic(
true );
203 case Qt::DecorationRole:
205 if ( index.column() == 0 && category.
symbol() )
213 case Qt::ForegroundRole:
215 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
218 QColor fadedTextColor = brush.color();
219 fadedTextColor.setAlpha( 128 );
220 brush.setColor( fadedTextColor );
225 case Qt::TextAlignmentRole:
227 return ( index.column() == 0 ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignHCenter ) : static_cast<Qt::Alignment::Int>( Qt::AlignLeft );
232 switch ( index.column() )
236 if ( category.
value().userType() == QMetaType::Type::QVariantList )
239 const QVariantList list = category.
value().toList();
240 res.reserve( list.size() );
241 for (
const QVariant &v : list )
244 return res.join(
';' );
248 return category.
value();
253 return category.
label();
259 if ( index.column() == 1 )
260 return category.
value();
268bool QgsCategorizedSymbolRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
270 if ( !index.isValid() )
273 if ( index.column() == 0 && role == Qt::CheckStateRole )
275 mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
276 emit dataChanged( index, index );
280 if ( role != Qt::EditRole )
283 switch ( index.column() )
288 QVariant val = value;
289 const QVariant previousValue = mRenderer->categories().value( index.row() ).value();
290 if ( previousValue.userType() != QMetaType::Type::QString && !previousValue.toString().isEmpty() )
292 switch ( previousValue.userType() )
294 case QMetaType::Type::Int:
297 case QMetaType::Type::Double:
298 val = value.toDouble();
300 case QMetaType::Type::QVariantList:
302 const QStringList parts = value.toString().split(
';' );
304 list.reserve( parts.count() );
305 for (
const QString &p : parts )
308 if ( list.count() == 1 )
315 val = value.toString();
319 mRenderer->updateCategoryValue( index.row(), val );
323 mRenderer->updateCategoryLabel( index.row(), value.toString() );
329 emit dataChanged( index, index );
333QVariant QgsCategorizedSymbolRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
335 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
338 lst << tr(
"Symbol" ) << tr(
"Value" ) << tr(
"Legend" );
339 return lst.value( section );
344int QgsCategorizedSymbolRendererModel::rowCount(
const QModelIndex &parent )
const
346 if ( parent.isValid() || !mRenderer )
350 return mRenderer->categories().size();
353int QgsCategorizedSymbolRendererModel::columnCount(
const QModelIndex &index )
const
359QModelIndex QgsCategorizedSymbolRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const
361 if ( hasIndex( row, column, parent ) )
363 return createIndex( row, column );
365 return QModelIndex();
368QModelIndex QgsCategorizedSymbolRendererModel::parent(
const QModelIndex &index )
const
371 return QModelIndex();
374QStringList QgsCategorizedSymbolRendererModel::mimeTypes()
const
377 types << mMimeFormat;
381QMimeData *QgsCategorizedSymbolRendererModel::mimeData(
const QModelIndexList &indexes )
const
383 QMimeData *mimeData =
new QMimeData();
384 QByteArray encodedData;
386 QDataStream stream( &encodedData, QIODevice::WriteOnly );
389 const auto constIndexes = indexes;
390 for (
const QModelIndex &index : constIndexes )
392 if ( !index.isValid() || index.column() != 0 )
395 stream << index.row();
397 mimeData->setData( mMimeFormat, encodedData );
401bool QgsCategorizedSymbolRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent )
405 if ( action != Qt::MoveAction )
408 if ( !data->hasFormat( mMimeFormat ) )
411 QByteArray encodedData = data->data( mMimeFormat );
412 QDataStream stream( &encodedData, QIODevice::ReadOnly );
415 while ( !stream.atEnd() )
423 std::sort( rows.begin(), rows.end() );
430 to = mRenderer->categories().size();
431 for (
int i = rows.size() - 1; i >= 0; i-- )
433 QgsDebugMsgLevel( QStringLiteral(
"move %1 to %2" ).arg( rows[i] ).arg( to ), 2 );
438 mRenderer->moveCategory( rows[i], t );
440 for (
int j = 0; j < i; j++ )
442 if ( to < rows[j] && rows[i] > rows[j] )
449 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
454void QgsCategorizedSymbolRendererModel::deleteRows( QList<int> rows )
456 std::sort( rows.begin(), rows.end() );
457 for (
int i = rows.size() - 1; i >= 0; i-- )
459 beginRemoveRows( QModelIndex(), rows[i], rows[i] );
460 mRenderer->deleteCategory( rows[i] );
465void QgsCategorizedSymbolRendererModel::removeAllRows()
467 beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
468 mRenderer->deleteAllCategories();
472void QgsCategorizedSymbolRendererModel::sort(
int column, Qt::SortOrder order )
480 mRenderer->sortByValue( order );
482 else if ( column == 2 )
484 mRenderer->sortByLabel( order );
486 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
489void QgsCategorizedSymbolRendererModel::updateSymbology()
491 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
495QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QWidget *parent )
499void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element,
const QStyleOption *option, QPainter *painter,
const QWidget *widget )
const
501 if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
503 QStyleOption opt( *option );
504 opt.rect.setLeft( 0 );
506 opt.rect.setHeight( 0 );
508 opt.rect.setRight( widget->width() );
509 QProxyStyle::drawPrimitive( element, &opt, painter, widget );
512 QProxyStyle::drawPrimitive( element, option, painter, widget );
516QgsCategorizedRendererViewItemDelegate::QgsCategorizedRendererViewItemDelegate(
QgsFieldExpressionWidget *expressionWidget, QObject *parent )
517 : QStyledItemDelegate( parent )
518 , mFieldExpressionWidget( expressionWidget )
522QWidget *QgsCategorizedRendererViewItemDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
531 const QString fieldName { mFieldExpressionWidget->currentField( &isExpression, &isValid ) };
532 if ( !fieldName.isEmpty() && mFieldExpressionWidget->layer() && mFieldExpressionWidget->layer()->fields().lookupField( fieldName ) != -1 )
534 userType = mFieldExpressionWidget->layer()->fields().field( fieldName ).type();
536 else if ( isExpression && isValid )
540 if ( mFieldExpressionWidget->layer()->getFeatures().nextFeature( feat ) )
545 expressionContext.
appendScope( mFieldExpressionWidget->layer()->createExpressionContextScope() );
548 const QVariant value = exp.evaluate( &expressionContext );
549 if ( !exp.hasEvalError() )
551 userType =
static_cast<QMetaType::Type
>( value.userType() );
560 case QMetaType::Type::Double:
566 if ( value.toDouble( &ok ); ok )
568 const QString strVal { value.toString() };
569 const int dotPosition( strVal.indexOf(
'.' ) );
570 if ( dotPosition >= 0 )
572 decimals = std::max<int>( 2, strVal.length() - dotPosition - 1 );
575 editor->setDecimals( decimals );
577 editor->setMaximum( std::numeric_limits<double>::max() );
578 editor->setMinimum( std::numeric_limits<double>::lowest() );
581 case QMetaType::Type::Int:
584 editor->setDecimals( 0 );
586 editor->setMaximum( std::numeric_limits<int>::max() );
587 editor->setMinimum( std::numeric_limits<int>::min() );
590 case QMetaType::Type::QChar:
593 editor->setDecimals( 0 );
595 editor->setMaximum( std::numeric_limits<char>::max() );
596 editor->setMinimum( std::numeric_limits<char>::min() );
599 case QMetaType::Type::UInt:
602 editor->setDecimals( 0 );
604 editor->setMaximum( std::numeric_limits<unsigned int>::max() );
605 editor->setMinimum( 0 );
608 case QMetaType::Type::LongLong:
611 editor->setDecimals( 0 );
613 editor->setMaximum(
static_cast<double>( std::numeric_limits<qlonglong>::max() ) );
614 editor->setMinimum( std::numeric_limits<qlonglong>::min() );
617 case QMetaType::Type::ULongLong:
620 editor->setDecimals( 0 );
622 editor->setMaximum(
static_cast<double>( std::numeric_limits<unsigned long long>::max() ) );
623 editor->setMinimum( 0 );
629 return editor ? editor : QStyledItemDelegate::createEditor( parent, option, index );
642 , mContextMenu( new QMenu( this ) )
657 const QString attrName =
mRenderer->classAttribute();
658 mOldClassificationAttribute = attrName;
662 layout()->setContentsMargins( 0, 0, 0, 0 );
664 mExpressionWidget->setLayer(
mLayer );
665 btnChangeCategorizedSymbol->setLayer(
mLayer );
666 btnChangeCategorizedSymbol->registerExpressionContextGenerator(
this );
669 btnColorRamp->setShowRandomColorRamp(
true );
672 std::unique_ptr<QgsColorRamp> colorRamp(
QgsProject::instance()->styleSettings()->defaultColorRamp() );
675 btnColorRamp->setColorRamp( colorRamp.get() );
679 btnColorRamp->setRandomColorRamp();
689 mModel =
new QgsCategorizedSymbolRendererModel(
this, screen() );
695 viewCategories->setModel(
mModel );
696 viewCategories->resizeColumnToContents( 0 );
697 viewCategories->resizeColumnToContents( 1 );
698 viewCategories->resizeColumnToContents( 2 );
699 viewCategories->setItemDelegateForColumn( 1,
new QgsCategorizedRendererViewItemDelegate( mExpressionWidget, viewCategories ) );
701 viewCategories->setStyle(
new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
702 connect( viewCategories->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QgsCategorizedSymbolRendererWidget::selectionChanged );
710 connect( viewCategories, &QTreeView::customContextMenuRequested,
this, &QgsCategorizedSymbolRendererWidget::showContextMenu );
712 connect( btnChangeCategorizedSymbol, &
QgsSymbolButton::changed,
this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton );
723 QMenu *advMenu =
new QMenu;
730 QAction *actionDdsLegend = advMenu->addAction( tr(
"Data-defined Size Legend…" ) );
732 connect( actionDdsLegend, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
735 btnAdvanced->setMenu( advMenu );
737 mExpressionWidget->registerExpressionContextGenerator(
this );
739 mMergeCategoriesAction =
new QAction( tr(
"Merge Categories" ),
this );
740 connect( mMergeCategoriesAction, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::mergeSelectedCategories );
741 mUnmergeCategoriesAction =
new QAction( tr(
"Unmerge Categories" ),
this );
742 connect( mUnmergeCategoriesAction, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories );
744 connect( mContextMenu, &QMenu::aboutToShow,
this, [=] {
764 const QString attrName =
mRenderer->classAttribute();
765 mExpressionWidget->setField( attrName );
777 btnColorRamp->setColorRamp(
mRenderer->sourceColorRamp() );
795 delete mActionLevels;
796 mActionLevels =
nullptr;
803 if ( !selectedCats.isEmpty() )
814 const auto constSelectedCats = selectedCats;
815 for (
const int idx : constSelectedCats )
821 mRenderer->updateCategorySymbol( idx, newCatSymbol );
841 if ( !dlg.exec() || !newSymbol )
864 if ( idx.isValid() && idx.column() == 0 )
872 std::unique_ptr<QgsSymbol> symbol;
874 if (
auto *lSymbol = category.
symbol() )
876 symbol.reset( lSymbol->clone() );
896 if ( !dlg.exec() || !symbol )
909 const QString attrName = mExpressionWidget->currentField();
913 if ( uniqueValues.size() >= 1000 )
915 const int res = QMessageBox::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 );
916 if ( res == QMessageBox::Cancel )
923 DlgAddCategories dlg(
mStyle, createDefaultSymbol(), unique_vals,
this );
929 bool deleteExisting =
false;
931 if ( !mOldClassificationAttribute.isEmpty() && attrName != mOldClassificationAttribute && !
mRenderer->categories().isEmpty() )
933 const int res = QMessageBox::question(
this, tr(
"Delete Classification" ), tr(
"The classification field was changed from '%1' to '%2'.\n"
934 "Should the existing classes be deleted before classification?" )
935 .arg( mOldClassificationAttribute, attrName ),
936 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
937 if ( res == QMessageBox::Cancel )
942 deleteExisting = ( res == QMessageBox::Yes );
946 bool keepExistingColors =
false;
947 if ( !deleteExisting )
950 keepExistingColors = !prevCats.isEmpty();
952 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
954 for (
int i = 0; i < cats.size(); ++i )
956 bool contains =
false;
957 const QVariant value = cats.at( i ).value();
958 for (
int j = 0; j < prevCats.size() && !contains; ++j )
960 const QVariant prevCatValue = prevCats.at( j ).value();
961 if ( prevCatValue.userType() == QMetaType::Type::QVariantList )
963 const QVariantList list = prevCatValue.toList();
964 for (
const QVariant &v : list )
975 if ( prevCats.at( j ).value() == value )
986 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
989 cats.at( i ).symbol()->setColor( randomColors.
color( i ) );
991 prevCats.append( cats.at( i ) );
997 mOldClassificationAttribute = attrName;
1014 auto r = std::make_unique<QgsCategorizedSymbolRenderer>( attrName, cats );
1016 std::unique_ptr<QgsColorRamp> ramp( btnColorRamp->colorRamp() );
1018 r->setSourceColorRamp( ramp->clone() );
1022 mModel->setRenderer( r.get() );
1025 if ( !keepExistingColors && ramp )
1032 if ( !btnColorRamp->isNull() )
1034 mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
1036 mModel->updateSymbology();
1041 const QModelIndex idx = viewCategories->selectionModel()->currentIndex();
1042 if ( !idx.isValid() )
1050 const QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
1052 const auto constSelectedRows = selectedRows;
1053 for (
const QModelIndex &r : constSelectedRows )
1057 rows.append( r.row() );
1066 mModel->deleteRows( categoryIndexes );
1080 const QString attrName = mExpressionWidget->currentField();
1085 QList<int> unusedIndexes;
1087 for (
int i = 0; i < catList.size(); ++i )
1090 if ( !uniqueValues.contains( cat.
value() ) )
1092 unusedIndexes.append( i );
1095 mModel->deleteRows( unusedIndexes );
1102 QList<QVariant> uniqueValues;
1118 context.setFeature( feature );
1120 if ( uniqueValues.contains( value ) )
1122 uniqueValues << value;
1129 return uniqueValues;
1138 mModel->addCategory( cat );
1146 QItemSelectionModel *m = viewCategories->selectionModel();
1147 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1149 if ( !selectedIndexes.isEmpty() )
1152 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1153 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1155 const int row = ( *indexIt ).row();
1156 QgsSymbol *s = categories[row].symbol();
1170 QItemSelectionModel *m = viewCategories->selectionModel();
1171 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1173 if ( !selectedIndexes.isEmpty() )
1175 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1176 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1178 cl.append(
mModel->category( *indexIt ) );
1197 viewCategories->selectionModel()->clear();
1205 QMessageBox::information(
this, tr(
"Matched Symbols" ), tr(
"Matched %n categories to symbols.",
nullptr, matched ) );
1209 QMessageBox::warning(
this, tr(
"Matched Symbols" ), tr(
"No categories could be matched to symbols in library." ) );
1222 QVariantList unmatchedCategories;
1223 QStringList unmatchedSymbols;
1224 const int matched =
mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
1226 mModel->updateSymbology();
1233 const QString openFileDir = settings.
value( QStringLiteral(
"UI/lastMatchToSymbolsDir" ), QDir::homePath() ).toString();
1235 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Match to Symbols from File" ), openFileDir, tr(
"XML files (*.xml *.XML)" ) );
1236 if ( fileName.isEmpty() )
1241 const QFileInfo openFileInfo( fileName );
1242 settings.
setValue( QStringLiteral(
"UI/lastMatchToSymbolsDir" ), openFileInfo.absolutePath() );
1245 if ( !importedStyle.
importXml( fileName ) )
1247 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ), tr(
"An error occurred while reading file:\n%1" ).arg( importedStyle.
errorString() ) );
1254 QMessageBox::information(
this, tr(
"Match to Symbols from File" ), tr(
"Matched %n categories to symbols from file.",
nullptr, matched ) );
1258 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ), tr(
"No categories could be matched to symbols in file." ) );
1269 mRenderer->setLegendSymbolItem( legendSymbol.ruleKey(), sym->
clone() );
1272 mRenderer->setUsingSymbolLevels( enabled );
1273 mModel->updateSymbology();
1284 if ( !selectedCats.isEmpty() )
1286 for (
const int idx : selectedCats )
1288 if (
mRenderer->categories().at( idx ).symbol()->type() != tempSymbol->type() )
1291 std::unique_ptr<QgsSymbol> newCatSymbol( tempSymbol->clone() );
1292 if ( selectedCats.count() > 1 )
1295 newCatSymbol->setColor(
mRenderer->categories().at( idx ).symbol()->color() );
1297 mRenderer->updateCategorySymbol( idx, newCatSymbol.release() );
1310void QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton()
1320 QItemSelectionModel *m = viewCategories->selectionModel();
1321 const QModelIndexList i = m->selectedRows();
1327 if ( !selectedCats.isEmpty() )
1329 const auto constSelectedCats = selectedCats;
1330 for (
const int idx : constSelectedCats )
1333 if ( selectedCats.count() > 1 )
1338 mRenderer->updateCategorySymbol( idx, newCatSymbol );
1347 mModel->updateSymbology();
1358 if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1360 mCopyBuffer.clear();
1363 else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1365 QgsCategoryList::iterator rIt = mCopyBuffer.begin();
1366 for ( ; rIt != mCopyBuffer.end(); ++rIt )
1368 rIt->mUuid = QUuid::createUuid().toString();
1369 mModel->addCategory( *rIt );
1379 expContext = lMapCanvas->createExpressionContext();
1402void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
1416void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
1421 QList<int> categoryIndexes;
1424 for (
const int i : selectedCategoryIndexes )
1426 const QVariant v = categories.at( i ).value();
1428 if ( !v.isValid() || v ==
"" )
1433 categoryIndexes.append( i );
1436 if ( categoryIndexes.count() < 2 )
1440 QVariantList values;
1441 values.reserve( categoryIndexes.count() );
1442 labels.reserve( categoryIndexes.count() );
1443 for (
const int i : categoryIndexes )
1445 const QVariant v = categories.at( i ).value();
1447 if ( v.userType() == QMetaType::Type::QVariantList )
1449 values.append( v.toList() );
1454 labels << categories.at( i ).label();
1458 mRenderer->updateCategoryLabel( categoryIndexes.at( 0 ), labels.join(
',' ) );
1459 mRenderer->updateCategoryValue( categoryIndexes.at( 0 ), values );
1461 categoryIndexes.pop_front();
1462 mModel->deleteRows( categoryIndexes );
1467void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
1470 if ( categoryIndexes.isEmpty() )
1474 for (
const int i : categoryIndexes )
1476 const QVariant v = categories.at( i ).value();
1477 if ( v.userType() != QMetaType::Type::QVariantList )
1480 const QVariantList list = v.toList();
1481 for (
int j = 1; j < list.count(); ++j )
1483 mModel->addCategory(
QgsRendererCategory( list.at( j ), categories.at( i ).symbol()->clone(), list.at( j ).toString(), categories.at( i ).renderState() ) );
1485 mRenderer->updateCategoryValue( i, list.at( 0 ) );
1486 mRenderer->updateCategoryLabel( i, list.at( 0 ).toString() );
1492void QgsCategorizedSymbolRendererWidget::showContextMenu( QPoint )
1494 mContextMenu->clear();
1495 const QList<QAction *> actions =
contextMenu->actions();
1496 for ( QAction *act : actions )
1498 mContextMenu->addAction( act );
1501 mContextMenu->addSeparator();
1503 if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1505 mContextMenu->addAction( mMergeCategoriesAction );
1507 if ( viewCategories->selectionModel()->selectedRows().count() == 1 )
1511 const QVariant v = categories.at( categoryIndexes.at( 0 ) ).value();
1512 if ( v.userType() == QMetaType::Type::QVariantList )
1513 mContextMenu->addAction( mUnmergeCategoriesAction );
1515 else if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1517 mContextMenu->addAction( mUnmergeCategoriesAction );
1520 mContextMenu->exec( QCursor::pos() );
1523void QgsCategorizedSymbolRendererWidget::selectionChanged(
const QItemSelection &,
const QItemSelection & )
1526 if ( !selectedCats.isEmpty() )
1528 whileBlocking( btnChangeCategorizedSymbol )->setSymbol(
mRenderer->categories().at( selectedCats.at( 0 ) ).symbol()->clone() );
1534 btnChangeCategorizedSymbol->setDialogTitle( selectedCats.size() == 1 ?
mRenderer->categories().at( selectedCats.at( 0 ) ).label() : tr(
"Symbol Settings" ) );
A feature renderer which represents features using a list of renderer categories.
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
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.
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...
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 prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QString expression() const
Returns the original, unmodified expression string.
QVariant evaluate()
Evaluate the feature and return the result.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Abstract base class for all 2D vector feature renderers.
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
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.
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.
bool renderState() const
Returns true if the category is currently enabled and should be rendered.
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...
Stores properties relating to a screen.
Stores settings for use within QGIS.
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.
QString errorString() const
Returns the last error from a load() operation.
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
bool importXml(const QString &filename)
Imports the symbols and colorramps into the default style database from the given XML file.
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 ...
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
QList< QgsExpressionContextScope > additionalExpressionContextScopes() const
Returns the list of additional expression context scopes to show as available within the layer.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
QgsMessageBar * messageBar() const
Returns the message bar associated with the widget.
Abstract base class for all rendered symbols.
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.
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.
Represents a vector layer which manages a vector based dataset.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
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.
QList< QgsRendererCategory > QgsCategoryList
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsgLevel(str, level)