51#include <QStandardItem>
52#include <QStandardItemModel>
55#include "moc_qgscategorizedsymbolrendererwidget.cpp"
59QgsCategorizedSymbolRendererModel::QgsCategorizedSymbolRendererModel( QObject *parent, QScreen *screen )
60 : QAbstractItemModel( parent )
61 , mMimeFormat( QStringLiteral(
"application/x-qgscategorizedsymbolrendererv2model" ) )
70 beginRemoveRows( QModelIndex(), 0, std::max<int>( mRenderer->categories().size() - 1, 0 ) );
79 beginInsertRows( QModelIndex(), 0, renderer->
categories().size() - 1 );
89 const int idx = mRenderer->categories().size();
90 beginInsertRows( QModelIndex(), idx, idx );
91 mRenderer->addCategory( cat );
102 const int row = index.row();
103 if ( row >= catList.size() )
107 return catList.at( row );
111Qt::ItemFlags QgsCategorizedSymbolRendererModel::flags(
const QModelIndex &index )
const
114 if ( !index.isValid() || !mRenderer )
116 return Qt::ItemIsDropEnabled;
119 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable;
120 if ( index.column() == 1 )
123 if ( category.
value().userType() != QMetaType::Type::QVariantList )
125 flags |= Qt::ItemIsEditable;
128 else if ( index.column() == 2 )
130 flags |= Qt::ItemIsEditable;
135Qt::DropActions QgsCategorizedSymbolRendererModel::supportedDropActions()
const
137 return Qt::MoveAction;
140QVariant QgsCategorizedSymbolRendererModel::data(
const QModelIndex &index,
int role )
const
142 if ( !index.isValid() || !mRenderer )
149 case Qt::CheckStateRole:
151 if ( index.column() == 0 )
153 return category.
renderState() ? Qt::Checked : Qt::Unchecked;
158 case Qt::DisplayRole:
159 case Qt::ToolTipRole:
161 switch ( index.column() )
165 if ( category.
value().userType() == QMetaType::Type::QVariantList )
168 const QVariantList list = category.
value().toList();
169 res.reserve( list.size() );
170 for (
const QVariant &v : list )
173 if ( role == Qt::DisplayRole )
174 return res.join(
';' );
176 return res.join(
'\n' );
180 return tr(
"all other values" );
188 return category.
label();
198 italicFont.setItalic(
true );
204 case Qt::DecorationRole:
206 if ( index.column() == 0 && category.
symbol() )
214 case Qt::ForegroundRole:
216 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
219 QColor fadedTextColor = brush.color();
220 fadedTextColor.setAlpha( 128 );
221 brush.setColor( fadedTextColor );
226 case Qt::TextAlignmentRole:
228 return ( index.column() == 0 ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignHCenter ) : static_cast<Qt::Alignment::Int>( Qt::AlignLeft );
233 switch ( index.column() )
237 if ( category.
value().userType() == QMetaType::Type::QVariantList )
240 const QVariantList list = category.
value().toList();
241 res.reserve( list.size() );
242 for (
const QVariant &v : list )
245 return res.join(
';' );
249 return category.
value();
254 return category.
label();
260 if ( index.column() == 1 )
261 return category.
value();
269bool QgsCategorizedSymbolRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
271 if ( !index.isValid() )
274 if ( index.column() == 0 && role == Qt::CheckStateRole )
276 mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
277 emit dataChanged( index, index );
281 if ( role != Qt::EditRole )
284 switch ( index.column() )
289 QVariant val = value;
290 const QVariant previousValue = mRenderer->categories().value( index.row() ).value();
291 if ( previousValue.userType() != QMetaType::Type::QString && !previousValue.toString().isEmpty() )
293 switch ( previousValue.userType() )
295 case QMetaType::Type::Int:
298 case QMetaType::Type::Double:
299 val = value.toDouble();
301 case QMetaType::Type::QVariantList:
303 const QStringList parts = value.toString().split(
';' );
305 list.reserve( parts.count() );
306 for (
const QString &p : parts )
309 if ( list.count() == 1 )
316 val = value.toString();
320 mRenderer->updateCategoryValue( index.row(), val );
324 mRenderer->updateCategoryLabel( index.row(), value.toString() );
330 emit dataChanged( index, index );
334QVariant QgsCategorizedSymbolRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
336 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
339 lst << tr(
"Symbol" ) << tr(
"Value" ) << tr(
"Legend" );
340 return lst.value( section );
345int QgsCategorizedSymbolRendererModel::rowCount(
const QModelIndex &parent )
const
347 if ( parent.isValid() || !mRenderer )
351 return mRenderer->categories().size();
354int QgsCategorizedSymbolRendererModel::columnCount(
const QModelIndex &index )
const
360QModelIndex QgsCategorizedSymbolRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const
362 if ( hasIndex( row, column, parent ) )
364 return createIndex( row, column );
366 return QModelIndex();
369QModelIndex QgsCategorizedSymbolRendererModel::parent(
const QModelIndex &index )
const
372 return QModelIndex();
375QStringList QgsCategorizedSymbolRendererModel::mimeTypes()
const
378 types << mMimeFormat;
382QMimeData *QgsCategorizedSymbolRendererModel::mimeData(
const QModelIndexList &indexes )
const
384 QMimeData *mimeData =
new QMimeData();
385 QByteArray encodedData;
387 QDataStream stream( &encodedData, QIODevice::WriteOnly );
390 const auto constIndexes = indexes;
391 for (
const QModelIndex &index : constIndexes )
393 if ( !index.isValid() || index.column() != 0 )
396 stream << index.row();
398 mimeData->setData( mMimeFormat, encodedData );
402bool QgsCategorizedSymbolRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent )
406 if ( action != Qt::MoveAction )
409 if ( !data->hasFormat( mMimeFormat ) )
412 QByteArray encodedData = data->data( mMimeFormat );
413 QDataStream stream( &encodedData, QIODevice::ReadOnly );
416 while ( !stream.atEnd() )
424 std::sort( rows.begin(), rows.end() );
431 to = mRenderer->categories().size();
432 for (
int i = rows.size() - 1; i >= 0; i-- )
434 QgsDebugMsgLevel( QStringLiteral(
"move %1 to %2" ).arg( rows[i] ).arg( to ), 2 );
439 mRenderer->moveCategory( rows[i], t );
441 for (
int j = 0; j < i; j++ )
443 if ( to < rows[j] && rows[i] > rows[j] )
450 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
455void QgsCategorizedSymbolRendererModel::deleteRows( QList<int> rows )
457 std::sort( rows.begin(), rows.end() );
458 for (
int i = rows.size() - 1; i >= 0; i-- )
460 beginRemoveRows( QModelIndex(), rows[i], rows[i] );
461 mRenderer->deleteCategory( rows[i] );
466void QgsCategorizedSymbolRendererModel::removeAllRows()
468 beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
469 mRenderer->deleteAllCategories();
473void QgsCategorizedSymbolRendererModel::sort(
int column, Qt::SortOrder order )
481 mRenderer->sortByValue( order );
483 else if ( column == 2 )
485 mRenderer->sortByLabel( order );
487 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
490void QgsCategorizedSymbolRendererModel::updateSymbology()
492 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
496QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QWidget *parent )
500void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element,
const QStyleOption *option, QPainter *painter,
const QWidget *widget )
const
502 if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
504 QStyleOption opt( *option );
505 opt.rect.setLeft( 0 );
507 opt.rect.setHeight( 0 );
509 opt.rect.setRight( widget->width() );
510 QProxyStyle::drawPrimitive( element, &opt, painter, widget );
513 QProxyStyle::drawPrimitive( element, option, painter, widget );
517QgsCategorizedRendererViewItemDelegate::QgsCategorizedRendererViewItemDelegate(
QgsFieldExpressionWidget *expressionWidget, QObject *parent )
518 : QStyledItemDelegate( parent )
519 , mFieldExpressionWidget( expressionWidget )
523QWidget *QgsCategorizedRendererViewItemDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
532 const QString fieldName { mFieldExpressionWidget->currentField( &isExpression, &isValid ) };
533 if ( !fieldName.isEmpty() && mFieldExpressionWidget->layer() && mFieldExpressionWidget->layer()->fields().lookupField( fieldName ) != -1 )
535 userType = mFieldExpressionWidget->layer()->fields().field( fieldName ).type();
537 else if ( isExpression && isValid )
541 if ( mFieldExpressionWidget->layer()->getFeatures().nextFeature( feat ) )
546 expressionContext.
appendScope( mFieldExpressionWidget->layer()->createExpressionContextScope() );
549 const QVariant value = exp.
evaluate( &expressionContext );
552 userType =
static_cast<QMetaType::Type
>( value.userType() );
561 case QMetaType::Type::Double:
567 if ( value.toDouble( &ok ); ok )
569 const QString strVal { value.toString() };
570 const int dotPosition( strVal.indexOf(
'.' ) );
571 if ( dotPosition >= 0 )
573 decimals = std::max<int>( 2, strVal.length() - dotPosition - 1 );
576 editor->setDecimals( decimals );
578 editor->setMaximum( std::numeric_limits<double>::max() );
579 editor->setMinimum( std::numeric_limits<double>::lowest() );
582 case QMetaType::Type::Int:
585 editor->setDecimals( 0 );
587 editor->setMaximum( std::numeric_limits<int>::max() );
588 editor->setMinimum( std::numeric_limits<int>::min() );
591 case QMetaType::Type::QChar:
594 editor->setDecimals( 0 );
596 editor->setMaximum( std::numeric_limits<char>::max() );
597 editor->setMinimum( std::numeric_limits<char>::min() );
600 case QMetaType::Type::UInt:
603 editor->setDecimals( 0 );
605 editor->setMaximum( std::numeric_limits<unsigned int>::max() );
606 editor->setMinimum( 0 );
609 case QMetaType::Type::LongLong:
612 editor->setDecimals( 0 );
614 editor->setMaximum(
static_cast<double>( std::numeric_limits<qlonglong>::max() ) );
615 editor->setMinimum( std::numeric_limits<qlonglong>::min() );
618 case QMetaType::Type::ULongLong:
621 editor->setDecimals( 0 );
623 editor->setMaximum(
static_cast<double>( std::numeric_limits<unsigned long long>::max() ) );
624 editor->setMinimum( 0 );
630 return editor ? editor : QStyledItemDelegate::createEditor( parent, option, index );
643 , mContextMenu( new QMenu( this ) )
658 const QString attrName =
mRenderer->classAttribute();
659 mOldClassificationAttribute = attrName;
663 layout()->setContentsMargins( 0, 0, 0, 0 );
665 mExpressionWidget->setLayer(
mLayer );
666 btnChangeCategorizedSymbol->setLayer(
mLayer );
667 btnChangeCategorizedSymbol->registerExpressionContextGenerator(
this );
670 btnColorRamp->setShowRandomColorRamp(
true );
673 std::unique_ptr<QgsColorRamp> colorRamp(
QgsProject::instance()->styleSettings()->defaultColorRamp() );
676 btnColorRamp->setColorRamp( colorRamp.get() );
680 btnColorRamp->setRandomColorRamp();
690 mModel =
new QgsCategorizedSymbolRendererModel(
this, screen() );
696 viewCategories->setModel(
mModel );
697 viewCategories->resizeColumnToContents( 0 );
698 viewCategories->resizeColumnToContents( 1 );
699 viewCategories->resizeColumnToContents( 2 );
700 viewCategories->setItemDelegateForColumn( 1,
new QgsCategorizedRendererViewItemDelegate( mExpressionWidget, viewCategories ) );
702 viewCategories->setStyle(
new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
703 connect( viewCategories->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QgsCategorizedSymbolRendererWidget::selectionChanged );
711 connect( viewCategories, &QTreeView::customContextMenuRequested,
this, &QgsCategorizedSymbolRendererWidget::showContextMenu );
713 connect( btnChangeCategorizedSymbol, &
QgsSymbolButton::changed,
this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton );
724 QMenu *advMenu =
new QMenu;
731 QAction *actionDdsLegend = advMenu->addAction( tr(
"Data-defined Size Legend…" ) );
733 connect( actionDdsLegend, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
736 btnAdvanced->setMenu( advMenu );
738 mExpressionWidget->registerExpressionContextGenerator(
this );
740 mMergeCategoriesAction =
new QAction( tr(
"Merge Categories" ),
this );
741 connect( mMergeCategoriesAction, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::mergeSelectedCategories );
742 mUnmergeCategoriesAction =
new QAction( tr(
"Unmerge Categories" ),
this );
743 connect( mUnmergeCategoriesAction, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories );
745 connect( mContextMenu, &QMenu::aboutToShow,
this, [
this] {
765 const QString attrName =
mRenderer->classAttribute();
766 mExpressionWidget->setField( attrName );
778 btnColorRamp->setColorRamp(
mRenderer->sourceColorRamp() );
790 btnChangeCategorizedSymbol->setMapCanvas(
context.mapCanvas() );
791 btnChangeCategorizedSymbol->setMessageBar(
context.messageBar() );
796 delete mActionLevels;
797 mActionLevels =
nullptr;
804 if ( !selectedCats.isEmpty() )
815 const auto constSelectedCats = selectedCats;
816 for (
const int idx : constSelectedCats )
822 mRenderer->updateCategorySymbol( idx, newCatSymbol );
842 if ( !dlg.exec() || !newSymbol )
865 if ( idx.isValid() && idx.column() == 0 )
873 std::unique_ptr<QgsSymbol> symbol;
875 if (
auto *lSymbol = category.
symbol() )
877 symbol.reset( lSymbol->clone() );
897 if ( !dlg.exec() || !symbol )
910 const QString attrName = mExpressionWidget->currentField();
911 bool valuesRetrieved;
913 if ( !valuesRetrieved )
915 QgsDebugMsgLevel( QStringLiteral(
"Unable to retrieve values from layer %1 with expression %2" ).arg(
mLayer->name() ).arg( attrName ), 2 );
920 if ( uniqueValues.size() >= 1000 )
922 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 );
923 if ( res == QMessageBox::Cancel )
930 DlgAddCategories dlg(
mStyle, createDefaultSymbol(), unique_vals,
this );
936 bool deleteExisting =
false;
938 if ( !mOldClassificationAttribute.isEmpty() && attrName != mOldClassificationAttribute && !
mRenderer->categories().isEmpty() )
940 const int res = QMessageBox::question(
this, tr(
"Delete Classification" ), tr(
"The classification field was changed from '%1' to '%2'.\n"
941 "Should the existing classes be deleted before classification?" )
942 .arg( mOldClassificationAttribute, attrName ),
943 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
944 if ( res == QMessageBox::Cancel )
949 deleteExisting = ( res == QMessageBox::Yes );
953 bool keepExistingColors =
false;
954 if ( !deleteExisting )
957 keepExistingColors = !prevCats.isEmpty();
959 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
961 for (
int i = 0; i < cats.size(); ++i )
963 bool contains =
false;
964 const QVariant value = cats.at( i ).value();
965 for (
int j = 0; j < prevCats.size() && !contains; ++j )
967 const QVariant prevCatValue = prevCats.at( j ).value();
968 if ( prevCatValue.userType() == QMetaType::Type::QVariantList )
970 const QVariantList list = prevCatValue.toList();
971 for (
const QVariant &v : list )
982 if ( prevCats.at( j ).value() == value )
993 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
996 cats.at( i ).symbol()->setColor( randomColors.
color( i ) );
998 prevCats.append( cats.at( i ) );
1004 mOldClassificationAttribute = attrName;
1021 auto r = std::make_unique<QgsCategorizedSymbolRenderer>( attrName, cats );
1023 std::unique_ptr<QgsColorRamp> ramp( btnColorRamp->colorRamp() );
1025 r->setSourceColorRamp( ramp->clone() );
1029 mModel->setRenderer( r.get() );
1032 if ( !keepExistingColors && ramp )
1039 if ( !btnColorRamp->isNull() )
1041 mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
1043 mModel->updateSymbology();
1048 const QModelIndex idx = viewCategories->selectionModel()->currentIndex();
1049 if ( !idx.isValid() )
1057 const QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
1059 const auto constSelectedRows = selectedRows;
1060 for (
const QModelIndex &r : constSelectedRows )
1064 rows.append( r.row() );
1073 mModel->deleteRows( categoryIndexes );
1087 const QString attrName = mExpressionWidget->currentField();
1088 bool valuesRetrieved;
1090 if ( !valuesRetrieved )
1092 QgsDebugMsgLevel( QStringLiteral(
"Unable to retrieve values from layer %1 with expression %2" ).arg(
mLayer->name() ).arg( attrName ), 2 );
1097 QList<int> unusedIndexes;
1099 for (
int i = 0; i < catList.size(); ++i )
1102 if ( !uniqueValues.contains( cat.
value() ) )
1104 unusedIndexes.append( i );
1107 mModel->deleteRows( unusedIndexes );
1113 bool valuesRetrieved;
1115 if ( !valuesRetrieved )
1117 QgsDebugMsgLevel( QStringLiteral(
"Unable to retrieve values from layer %1 with expression %2" ).arg(
mLayer->name() ).arg( attrName ), 2 );
1119 return uniqueValues;
1128 mModel->addCategory( cat );
1136 QItemSelectionModel *m = viewCategories->selectionModel();
1137 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1139 if ( !selectedIndexes.isEmpty() )
1142 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1143 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1145 const int row = ( *indexIt ).row();
1146 QgsSymbol *s = categories[row].symbol();
1160 QItemSelectionModel *m = viewCategories->selectionModel();
1161 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1163 if ( !selectedIndexes.isEmpty() )
1165 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1166 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1168 cl.append(
mModel->category( *indexIt ) );
1187 viewCategories->selectionModel()->clear();
1195 QMessageBox::information(
this, tr(
"Matched Symbols" ), tr(
"Matched %n categories to symbols.",
nullptr, matched ) );
1199 QMessageBox::warning(
this, tr(
"Matched Symbols" ), tr(
"No categories could be matched to symbols in library." ) );
1212 QVariantList unmatchedCategories;
1213 QStringList unmatchedSymbols;
1214 const int matched =
mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
1216 mModel->updateSymbology();
1223 const QString openFileDir = settings.
value( QStringLiteral(
"UI/lastMatchToSymbolsDir" ), QDir::homePath() ).toString();
1225 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Match to Symbols from File" ), openFileDir, tr(
"XML files (*.xml *.XML)" ) );
1226 if ( fileName.isEmpty() )
1231 const QFileInfo openFileInfo( fileName );
1232 settings.
setValue( QStringLiteral(
"UI/lastMatchToSymbolsDir" ), openFileInfo.absolutePath() );
1235 if ( !importedStyle.
importXml( fileName ) )
1237 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ), tr(
"An error occurred while reading file:\n%1" ).arg( importedStyle.
errorString() ) );
1244 QMessageBox::information(
this, tr(
"Match to Symbols from File" ), tr(
"Matched %n categories to symbols from file.",
nullptr, matched ) );
1248 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ), tr(
"No categories could be matched to symbols in file." ) );
1259 mRenderer->setLegendSymbolItem( legendSymbol.ruleKey(), sym->
clone() );
1262 mRenderer->setUsingSymbolLevels( enabled );
1263 mModel->updateSymbology();
1274 if ( !selectedCats.isEmpty() )
1276 for (
const int idx : selectedCats )
1278 if (
mRenderer->categories().at( idx ).symbol()->type() != tempSymbol->type() )
1281 std::unique_ptr<QgsSymbol> newCatSymbol( tempSymbol->clone() );
1282 if ( selectedCats.count() > 1 )
1285 newCatSymbol->setColor(
mRenderer->categories().at( idx ).symbol()->color() );
1287 mRenderer->updateCategorySymbol( idx, newCatSymbol.release() );
1300void QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton()
1310 QItemSelectionModel *m = viewCategories->selectionModel();
1311 const QModelIndexList i = m->selectedRows();
1317 if ( !selectedCats.isEmpty() )
1319 const auto constSelectedCats = selectedCats;
1320 for (
const int idx : constSelectedCats )
1323 if ( selectedCats.count() > 1 )
1328 mRenderer->updateCategorySymbol( idx, newCatSymbol );
1337 mModel->updateSymbology();
1348 if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1350 mCopyBuffer.clear();
1353 else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1355 QgsCategoryList::iterator rIt = mCopyBuffer.begin();
1356 for ( ; rIt != mCopyBuffer.end(); ++rIt )
1358 rIt->mUuid = QUuid::createUuid().toString();
1359 mModel->addCategory( *rIt );
1367 if (
auto *lMapCanvas =
mContext.mapCanvas() )
1369 expContext = lMapCanvas->createExpressionContext();
1383 const auto constAdditionalExpressionContextScopes =
mContext.additionalExpressionContextScopes();
1392void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
1406void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
1411 QList<int> categoryIndexes;
1414 for (
const int i : selectedCategoryIndexes )
1416 const QVariant v = categories.at( i ).value();
1418 if ( !v.isValid() || v ==
"" )
1423 categoryIndexes.append( i );
1426 if ( categoryIndexes.count() < 2 )
1430 QVariantList values;
1431 values.reserve( categoryIndexes.count() );
1432 labels.reserve( categoryIndexes.count() );
1433 for (
const int i : categoryIndexes )
1435 const QVariant v = categories.at( i ).value();
1437 if ( v.userType() == QMetaType::Type::QVariantList )
1439 values.append( v.toList() );
1444 labels << categories.at( i ).label();
1448 mRenderer->updateCategoryLabel( categoryIndexes.at( 0 ), labels.join(
',' ) );
1449 mRenderer->updateCategoryValue( categoryIndexes.at( 0 ), values );
1451 categoryIndexes.pop_front();
1452 mModel->deleteRows( categoryIndexes );
1457void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
1460 if ( categoryIndexes.isEmpty() )
1464 for (
const int i : categoryIndexes )
1466 const QVariant v = categories.at( i ).value();
1467 if ( v.userType() != QMetaType::Type::QVariantList )
1470 const QVariantList list = v.toList();
1471 for (
int j = 1; j < list.count(); ++j )
1473 mModel->addCategory( QgsRendererCategory( list.at( j ), categories.at( i ).symbol()->clone(), list.at( j ).toString(), categories.at( i ).renderState() ) );
1475 mRenderer->updateCategoryValue( i, list.at( 0 ) );
1476 mRenderer->updateCategoryLabel( i, list.at( 0 ).toString() );
1482void QgsCategorizedSymbolRendererWidget::showContextMenu( QPoint )
1484 mContextMenu->clear();
1485 const QList<QAction *> actions =
contextMenu->actions();
1486 for ( QAction *act : actions )
1488 mContextMenu->addAction( act );
1491 mContextMenu->addSeparator();
1493 if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1495 mContextMenu->addAction( mMergeCategoriesAction );
1497 if ( viewCategories->selectionModel()->selectedRows().count() == 1 )
1501 const QVariant v = categories.at( categoryIndexes.at( 0 ) ).value();
1502 if ( v.userType() == QMetaType::Type::QVariantList )
1503 mContextMenu->addAction( mUnmergeCategoriesAction );
1505 else if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1507 mContextMenu->addAction( mUnmergeCategoriesAction );
1510 mContextMenu->exec( QCursor::pos() );
1513void QgsCategorizedSymbolRendererWidget::selectionChanged(
const QItemSelection &,
const QItemSelection & )
1516 if ( !selectedCats.isEmpty() )
1518 whileBlocking( btnChangeCategorizedSymbol )->setSymbol(
mRenderer->categories().at( selectedCats.at( 0 ) ).symbol()->clone() );
1524 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)