51#include <QStandardItem>
52#include <QStandardItemModel>
56#include "moc_qgscategorizedsymbolrendererwidget.cpp"
58using namespace Qt::StringLiterals;
62QgsCategorizedSymbolRendererModel::QgsCategorizedSymbolRendererModel( QObject *parent, QScreen *screen )
63 : QAbstractItemModel( parent )
64 , mMimeFormat( u
"application/x-qgscategorizedsymbolrendererv2model"_s )
73 beginRemoveRows( QModelIndex(), 0, std::max<int>( mRenderer->categories().size() - 1, 0 ) );
82 beginInsertRows( QModelIndex(), 0, renderer->
categories().size() - 1 );
92 const int idx = mRenderer->categories().size();
93 beginInsertRows( QModelIndex(), idx, idx );
94 mRenderer->addCategory( cat );
105 const int row = index.row();
106 if ( row >= catList.size() )
110 return catList.at( row );
114Qt::ItemFlags QgsCategorizedSymbolRendererModel::flags(
const QModelIndex &index )
const
117 if ( !index.isValid() || !mRenderer )
119 return Qt::ItemIsDropEnabled;
122 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable;
123 if ( index.column() == 1 )
126 if ( category.
value().userType() != QMetaType::Type::QVariantList )
128 flags |= Qt::ItemIsEditable;
131 else if ( index.column() == 2 )
133 flags |= Qt::ItemIsEditable;
138Qt::DropActions QgsCategorizedSymbolRendererModel::supportedDropActions()
const
140 return Qt::MoveAction;
143QVariant QgsCategorizedSymbolRendererModel::data(
const QModelIndex &index,
int role )
const
145 if ( !index.isValid() || !mRenderer )
152 case Qt::CheckStateRole:
154 if ( index.column() == 0 )
156 return category.
renderState() ? Qt::Checked : Qt::Unchecked;
161 case Qt::DisplayRole:
162 case Qt::ToolTipRole:
164 switch ( index.column() )
168 if ( category.
value().userType() == QMetaType::Type::QVariantList )
171 const QVariantList list = category.
value().toList();
172 res.reserve( list.size() );
173 for (
const QVariant &v : list )
176 if ( role == Qt::DisplayRole )
177 return res.join(
';' );
179 return res.join(
'\n' );
183 return tr(
"all other values" );
191 return category.
label();
201 italicFont.setItalic(
true );
207 case Qt::DecorationRole:
209 if ( index.column() == 0 && category.
symbol() )
217 case Qt::ForegroundRole:
219 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
222 QColor fadedTextColor = brush.color();
223 fadedTextColor.setAlpha( 128 );
224 brush.setColor( fadedTextColor );
229 case Qt::TextAlignmentRole:
231 return ( index.column() == 0 ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignHCenter ) : static_cast<Qt::Alignment::Int>( Qt::AlignLeft );
236 switch ( index.column() )
240 if ( category.
value().userType() == QMetaType::Type::QVariantList )
243 const QVariantList list = category.
value().toList();
244 res.reserve( list.size() );
245 for (
const QVariant &v : list )
248 return res.join(
';' );
252 return category.
value();
257 return category.
label();
263 if ( index.column() == 1 )
264 return category.
value();
272bool QgsCategorizedSymbolRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
274 if ( !index.isValid() )
277 if ( index.column() == 0 && role == Qt::CheckStateRole )
279 mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
280 emit dataChanged( index, index );
284 if ( role != Qt::EditRole )
287 switch ( index.column() )
292 QVariant val = value;
293 const QVariant previousValue = mRenderer->categories().value( index.row() ).value();
294 if ( previousValue.userType() != QMetaType::Type::QString && !previousValue.toString().isEmpty() )
296 switch ( previousValue.userType() )
298 case QMetaType::Type::Int:
301 case QMetaType::Type::Double:
302 val = value.toDouble();
304 case QMetaType::Type::QVariantList:
306 const QStringList parts = value.toString().split(
';' );
308 list.reserve( parts.count() );
309 for (
const QString &p : parts )
312 if ( list.count() == 1 )
319 val = value.toString();
323 mRenderer->updateCategoryValue( index.row(), val );
327 mRenderer->updateCategoryLabel( index.row(), value.toString() );
333 emit dataChanged( index, index );
337QVariant QgsCategorizedSymbolRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
339 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
342 lst << tr(
"Symbol" ) << tr(
"Value" ) << tr(
"Legend" );
343 return lst.value( section );
348int QgsCategorizedSymbolRendererModel::rowCount(
const QModelIndex &parent )
const
350 if ( parent.isValid() || !mRenderer )
354 return mRenderer->categories().size();
357int QgsCategorizedSymbolRendererModel::columnCount(
const QModelIndex &index )
const
363QModelIndex QgsCategorizedSymbolRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const
365 if ( hasIndex( row, column, parent ) )
367 return createIndex( row, column );
369 return QModelIndex();
372QModelIndex QgsCategorizedSymbolRendererModel::parent(
const QModelIndex &index )
const
375 return QModelIndex();
378QStringList QgsCategorizedSymbolRendererModel::mimeTypes()
const
381 types << mMimeFormat;
385QMimeData *QgsCategorizedSymbolRendererModel::mimeData(
const QModelIndexList &indexes )
const
387 QMimeData *mimeData =
new QMimeData();
388 QByteArray encodedData;
390 QDataStream stream( &encodedData, QIODevice::WriteOnly );
393 const auto constIndexes = indexes;
394 for (
const QModelIndex &index : constIndexes )
396 if ( !index.isValid() || index.column() != 0 )
399 stream << index.row();
401 mimeData->setData( mMimeFormat, encodedData );
405bool QgsCategorizedSymbolRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent )
409 if ( action != Qt::MoveAction )
412 if ( !data->hasFormat( mMimeFormat ) )
415 QByteArray encodedData = data->data( mMimeFormat );
416 QDataStream stream( &encodedData, QIODevice::ReadOnly );
419 while ( !stream.atEnd() )
427 std::sort( rows.begin(), rows.end() );
434 to = mRenderer->categories().size();
435 for (
int i = rows.size() - 1; i >= 0; i-- )
442 mRenderer->moveCategory( rows[i], t );
444 for (
int j = 0; j < i; j++ )
446 if ( to < rows[j] && rows[i] > rows[j] )
453 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
458void QgsCategorizedSymbolRendererModel::deleteRows( QList<int> rows )
460 std::sort( rows.begin(), rows.end() );
461 for (
int i = rows.size() - 1; i >= 0; i-- )
463 beginRemoveRows( QModelIndex(), rows[i], rows[i] );
464 mRenderer->deleteCategory( rows[i] );
469void QgsCategorizedSymbolRendererModel::removeAllRows()
471 beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
472 mRenderer->deleteAllCategories();
476void QgsCategorizedSymbolRendererModel::sort(
int column, Qt::SortOrder order )
484 mRenderer->sortByValue( order );
486 else if ( column == 2 )
488 mRenderer->sortByLabel( order );
490 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
493void QgsCategorizedSymbolRendererModel::updateSymbology()
495 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
499QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QWidget *parent )
503void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element,
const QStyleOption *option, QPainter *painter,
const QWidget *widget )
const
505 if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
507 QStyleOption opt( *option );
508 opt.rect.setLeft( 0 );
510 opt.rect.setHeight( 0 );
512 opt.rect.setRight( widget->width() );
513 QProxyStyle::drawPrimitive( element, &opt, painter, widget );
516 QProxyStyle::drawPrimitive( element, option, painter, widget );
520QgsCategorizedRendererViewItemDelegate::QgsCategorizedRendererViewItemDelegate(
QgsFieldExpressionWidget *expressionWidget, QObject *parent )
521 : QStyledItemDelegate( parent )
522 , mFieldExpressionWidget( expressionWidget )
526QWidget *QgsCategorizedRendererViewItemDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
535 const QString fieldName { mFieldExpressionWidget->currentField( &isExpression, &isValid ) };
536 if ( !fieldName.isEmpty() && mFieldExpressionWidget->layer() && mFieldExpressionWidget->layer()->fields().lookupField( fieldName ) != -1 )
538 userType = mFieldExpressionWidget->layer()->fields().field( fieldName ).type();
540 else if ( isExpression && isValid )
544 if ( mFieldExpressionWidget->layer()->getFeatures().nextFeature( feat ) )
549 expressionContext.
appendScope( mFieldExpressionWidget->layer()->createExpressionContextScope() );
552 const QVariant value = exp.
evaluate( &expressionContext );
555 userType =
static_cast<QMetaType::Type
>( value.userType() );
564 case QMetaType::Type::Double:
570 if ( value.toDouble( &ok ); ok )
572 const QString strVal { value.toString() };
573 const int dotPosition( strVal.indexOf(
'.' ) );
574 if ( dotPosition >= 0 )
576 decimals = std::max<int>( 2, strVal.length() - dotPosition - 1 );
579 editor->setDecimals( decimals );
581 editor->setMaximum( std::numeric_limits<double>::max() );
582 editor->setMinimum( std::numeric_limits<double>::lowest() );
585 case QMetaType::Type::Int:
588 editor->setDecimals( 0 );
590 editor->setMaximum( std::numeric_limits<int>::max() );
591 editor->setMinimum( std::numeric_limits<int>::min() );
594 case QMetaType::Type::QChar:
597 editor->setDecimals( 0 );
599 editor->setMaximum( std::numeric_limits<char>::max() );
600 editor->setMinimum( std::numeric_limits<char>::min() );
603 case QMetaType::Type::UInt:
606 editor->setDecimals( 0 );
608 editor->setMaximum( std::numeric_limits<unsigned int>::max() );
609 editor->setMinimum( 0 );
612 case QMetaType::Type::LongLong:
615 editor->setDecimals( 0 );
617 editor->setMaximum(
static_cast<double>( std::numeric_limits<qlonglong>::max() ) );
618 editor->setMinimum( std::numeric_limits<qlonglong>::min() );
621 case QMetaType::Type::ULongLong:
624 editor->setDecimals( 0 );
626 editor->setMaximum(
static_cast<double>( std::numeric_limits<unsigned long long>::max() ) );
627 editor->setMinimum( 0 );
633 return editor ? editor : QStyledItemDelegate::createEditor( parent, option, index );
646 , mContextMenu( new QMenu( this ) )
661 const QString attrName =
mRenderer->classAttribute();
662 mOldClassificationAttribute = attrName;
666 layout()->setContentsMargins( 0, 0, 0, 0 );
668 mExpressionWidget->setLayer(
mLayer );
669 btnChangeCategorizedSymbol->setLayer(
mLayer );
670 btnChangeCategorizedSymbol->registerExpressionContextGenerator(
this );
673 btnColorRamp->setShowRandomColorRamp(
true );
676 std::unique_ptr<QgsColorRamp> colorRamp(
QgsProject::instance()->styleSettings()->defaultColorRamp() );
679 btnColorRamp->setColorRamp( colorRamp.get() );
683 btnColorRamp->setRandomColorRamp();
693 mModel =
new QgsCategorizedSymbolRendererModel(
this, screen() );
699 viewCategories->setModel(
mModel );
700 viewCategories->resizeColumnToContents( 0 );
701 viewCategories->resizeColumnToContents( 1 );
702 viewCategories->resizeColumnToContents( 2 );
703 viewCategories->setItemDelegateForColumn( 1,
new QgsCategorizedRendererViewItemDelegate( mExpressionWidget, viewCategories ) );
705 viewCategories->setStyle(
new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
706 connect( viewCategories->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QgsCategorizedSymbolRendererWidget::selectionChanged );
714 connect( viewCategories, &QTreeView::customContextMenuRequested,
this, &QgsCategorizedSymbolRendererWidget::showContextMenu );
716 connect( btnChangeCategorizedSymbol, &
QgsSymbolButton::changed,
this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton );
727 QMenu *advMenu =
new QMenu;
734 QAction *actionDdsLegend = advMenu->addAction( tr(
"Data-defined Size Legend…" ) );
736 connect( actionDdsLegend, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
739 btnAdvanced->setMenu( advMenu );
741 mExpressionWidget->registerExpressionContextGenerator(
this );
743 mMergeCategoriesAction =
new QAction( tr(
"Merge Categories" ),
this );
744 connect( mMergeCategoriesAction, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::mergeSelectedCategories );
745 mUnmergeCategoriesAction =
new QAction( tr(
"Unmerge Categories" ),
this );
746 connect( mUnmergeCategoriesAction, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories );
748 connect( mContextMenu, &QMenu::aboutToShow,
this, [
this] {
768 const QString attrName =
mRenderer->classAttribute();
769 mExpressionWidget->setField( attrName );
781 btnColorRamp->setColorRamp(
mRenderer->sourceColorRamp() );
793 btnChangeCategorizedSymbol->setMapCanvas(
context.mapCanvas() );
794 btnChangeCategorizedSymbol->setMessageBar(
context.messageBar() );
799 delete mActionLevels;
800 mActionLevels =
nullptr;
807 if ( !selectedCats.isEmpty() )
818 const auto constSelectedCats = selectedCats;
819 for (
const int idx : constSelectedCats )
825 mRenderer->updateCategorySymbol( idx, newCatSymbol );
845 if ( !dlg.exec() || !newSymbol )
868 if ( idx.isValid() && idx.column() == 0 )
876 std::unique_ptr<QgsSymbol> symbol;
878 if (
auto *lSymbol = category.
symbol() )
880 symbol.reset( lSymbol->clone() );
900 if ( !dlg.exec() || !symbol )
913 const QString attrName = mExpressionWidget->currentField();
914 bool valuesRetrieved;
916 if ( !valuesRetrieved )
918 QgsDebugMsgLevel( u
"Unable to retrieve values from layer %1 with expression %2"_s.arg(
mLayer->name() ).arg( attrName ), 2 );
923 if ( uniqueValues.size() >= 1000 )
925 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 );
926 if ( res == QMessageBox::Cancel )
933 DlgAddCategories dlg(
mStyle, createDefaultSymbol(), unique_vals,
this );
939 bool deleteExisting =
false;
941 if ( !mOldClassificationAttribute.isEmpty() && attrName != mOldClassificationAttribute && !
mRenderer->categories().isEmpty() )
943 const int res = QMessageBox::question(
this, tr(
"Delete Classification" ), tr(
"The classification field was changed from '%1' to '%2'.\n"
944 "Should the existing classes be deleted before classification?" )
945 .arg( mOldClassificationAttribute, attrName ),
946 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
947 if ( res == QMessageBox::Cancel )
952 deleteExisting = ( res == QMessageBox::Yes );
956 bool keepExistingColors =
false;
957 if ( !deleteExisting )
960 keepExistingColors = !prevCats.isEmpty();
962 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
964 for (
int i = 0; i < cats.size(); ++i )
966 bool contains =
false;
967 const QVariant value = cats.at( i ).value();
968 for (
int j = 0; j < prevCats.size() && !contains; ++j )
970 const QVariant prevCatValue = prevCats.at( j ).value();
971 if ( prevCatValue.userType() == QMetaType::Type::QVariantList )
973 const QVariantList list = prevCatValue.toList();
974 for (
const QVariant &v : list )
985 if ( prevCats.at( j ).value() == value )
996 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
999 cats.at( i ).symbol()->setColor( randomColors.
color( i ) );
1001 prevCats.append( cats.at( i ) );
1007 mOldClassificationAttribute = attrName;
1024 auto r = std::make_unique<QgsCategorizedSymbolRenderer>( attrName, cats );
1026 std::unique_ptr<QgsColorRamp> ramp( btnColorRamp->colorRamp() );
1028 r->setSourceColorRamp( ramp->clone() );
1032 mModel->setRenderer( r.get() );
1035 if ( !keepExistingColors && ramp )
1042 if ( !btnColorRamp->isNull() )
1044 mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
1046 mModel->updateSymbology();
1051 const QModelIndex idx = viewCategories->selectionModel()->currentIndex();
1052 if ( !idx.isValid() )
1060 const QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
1062 const auto constSelectedRows = selectedRows;
1063 for (
const QModelIndex &r : constSelectedRows )
1067 rows.append( r.row() );
1076 mModel->deleteRows( categoryIndexes );
1090 const QString attrName = mExpressionWidget->currentField();
1091 bool valuesRetrieved;
1093 if ( !valuesRetrieved )
1095 QgsDebugMsgLevel( u
"Unable to retrieve values from layer %1 with expression %2"_s.arg(
mLayer->name() ).arg( attrName ), 2 );
1100 QList<int> unusedIndexes;
1102 for (
int i = 0; i < catList.size(); ++i )
1105 if ( !uniqueValues.contains( cat.
value() ) )
1107 unusedIndexes.append( i );
1110 mModel->deleteRows( unusedIndexes );
1116 bool valuesRetrieved;
1118 if ( !valuesRetrieved )
1120 QgsDebugMsgLevel( u
"Unable to retrieve values from layer %1 with expression %2"_s.arg(
mLayer->name() ).arg( attrName ), 2 );
1122 return uniqueValues;
1131 mModel->addCategory( cat );
1139 QItemSelectionModel *m = viewCategories->selectionModel();
1140 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1142 if ( !selectedIndexes.isEmpty() )
1145 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1146 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1148 const int row = ( *indexIt ).row();
1149 QgsSymbol *s = categories[row].symbol();
1163 QItemSelectionModel *m = viewCategories->selectionModel();
1164 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1166 if ( !selectedIndexes.isEmpty() )
1168 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1169 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1171 cl.append(
mModel->category( *indexIt ) );
1190 viewCategories->selectionModel()->clear();
1198 QMessageBox::information(
this, tr(
"Matched Symbols" ), tr(
"Matched %n categories to symbols.",
nullptr, matched ) );
1202 QMessageBox::warning(
this, tr(
"Matched Symbols" ), tr(
"No categories could be matched to symbols in library." ) );
1215 QVariantList unmatchedCategories;
1216 QStringList unmatchedSymbols;
1217 const int matched =
mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
1219 mModel->updateSymbology();
1226 const QString openFileDir = settings.
value( u
"UI/lastMatchToSymbolsDir"_s, QDir::homePath() ).toString();
1228 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Match to Symbols from File" ), openFileDir, tr(
"XML files (*.xml *.XML)" ) );
1229 if ( fileName.isEmpty() )
1234 const QFileInfo openFileInfo( fileName );
1235 settings.
setValue( u
"UI/lastMatchToSymbolsDir"_s, openFileInfo.absolutePath() );
1238 if ( !importedStyle.
importXml( fileName ) )
1240 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ), tr(
"An error occurred while reading file:\n%1" ).arg( importedStyle.
errorString() ) );
1247 QMessageBox::information(
this, tr(
"Match to Symbols from File" ), tr(
"Matched %n categories to symbols from file.",
nullptr, matched ) );
1251 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ), tr(
"No categories could be matched to symbols in file." ) );
1262 mRenderer->setLegendSymbolItem( legendSymbol.ruleKey(), sym->
clone() );
1265 mRenderer->setUsingSymbolLevels( enabled );
1266 mModel->updateSymbology();
1277 if ( !selectedCats.isEmpty() )
1279 for (
const int idx : selectedCats )
1281 if (
mRenderer->categories().at( idx ).symbol()->type() != tempSymbol->type() )
1284 std::unique_ptr<QgsSymbol> newCatSymbol( tempSymbol->clone() );
1285 if ( selectedCats.count() > 1 )
1288 newCatSymbol->setColor(
mRenderer->categories().at( idx ).symbol()->color() );
1290 mRenderer->updateCategorySymbol( idx, newCatSymbol.release() );
1303void QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton()
1313 QItemSelectionModel *m = viewCategories->selectionModel();
1314 const QModelIndexList i = m->selectedRows();
1320 if ( !selectedCats.isEmpty() )
1322 const auto constSelectedCats = selectedCats;
1323 for (
const int idx : constSelectedCats )
1326 if ( selectedCats.count() > 1 )
1331 mRenderer->updateCategorySymbol( idx, newCatSymbol );
1340 mModel->updateSymbology();
1351 if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1353 mCopyBuffer.clear();
1356 else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1358 QgsCategoryList::iterator rIt = mCopyBuffer.begin();
1359 for ( ; rIt != mCopyBuffer.end(); ++rIt )
1361 rIt->mUuid = QUuid::createUuid().toString();
1362 mModel->addCategory( *rIt );
1370 if (
auto *lMapCanvas =
mContext.mapCanvas() )
1372 expContext = lMapCanvas->createExpressionContext();
1386 const auto constAdditionalExpressionContextScopes =
mContext.additionalExpressionContextScopes();
1395void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
1409void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
1414 QList<int> categoryIndexes;
1417 for (
const int i : selectedCategoryIndexes )
1419 const QVariant v = categories.at( i ).value();
1421 if ( !v.isValid() || v ==
"" )
1426 categoryIndexes.append( i );
1429 if ( categoryIndexes.count() < 2 )
1433 QVariantList values;
1434 values.reserve( categoryIndexes.count() );
1435 labels.reserve( categoryIndexes.count() );
1436 for (
const int i : categoryIndexes )
1438 const QVariant v = categories.at( i ).value();
1440 if ( v.userType() == QMetaType::Type::QVariantList )
1442 values.append( v.toList() );
1447 labels << categories.at( i ).label();
1451 mRenderer->updateCategoryLabel( categoryIndexes.at( 0 ), labels.join(
',' ) );
1452 mRenderer->updateCategoryValue( categoryIndexes.at( 0 ), values );
1454 categoryIndexes.pop_front();
1455 mModel->deleteRows( categoryIndexes );
1460void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
1463 if ( categoryIndexes.isEmpty() )
1467 for (
const int i : categoryIndexes )
1469 const QVariant v = categories.at( i ).value();
1470 if ( v.userType() != QMetaType::Type::QVariantList )
1473 const QVariantList list = v.toList();
1474 for (
int j = 1; j < list.count(); ++j )
1476 mModel->addCategory( QgsRendererCategory( list.at( j ), categories.at( i ).symbol()->clone(), list.at( j ).toString(), categories.at( i ).renderState() ) );
1478 mRenderer->updateCategoryValue( i, list.at( 0 ) );
1479 mRenderer->updateCategoryLabel( i, list.at( 0 ).toString() );
1485void QgsCategorizedSymbolRendererWidget::showContextMenu( QPoint )
1487 mContextMenu->clear();
1488 const QList<QAction *> actions =
contextMenu->actions();
1489 for ( QAction *act : actions )
1491 mContextMenu->addAction( act );
1494 mContextMenu->addSeparator();
1496 if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1498 mContextMenu->addAction( mMergeCategoriesAction );
1500 if ( viewCategories->selectionModel()->selectedRows().count() == 1 )
1504 const QVariant v = categories.at( categoryIndexes.at( 0 ) ).value();
1505 if ( v.userType() == QMetaType::Type::QVariantList )
1506 mContextMenu->addAction( mUnmergeCategoriesAction );
1508 else if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1510 mContextMenu->addAction( mUnmergeCategoriesAction );
1513 mContextMenu->exec( QCursor::pos() );
1516void QgsCategorizedSymbolRendererWidget::selectionChanged(
const QItemSelection &,
const QItemSelection & )
1519 if ( !selectedCats.isEmpty() )
1521 whileBlocking( btnChangeCategorizedSymbol )->setSymbol(
mRenderer->categories().at( selectedCats.at( 0 ) ).symbol()->clone() );
1527 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.
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...
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,...
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 QString displayString(const QVariant &variant, int precision=-1)
Returns a localized representation of value with the given precision, if precision is -1 then precisi...
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.
QList< QgsRendererCategory > QgsCategoryList
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsgLevel(str, level)