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 )
72 beginRemoveRows( QModelIndex(), 0, std::max<int>( mRenderer->categories().size() - 1, 0 ) );
81 beginInsertRows( QModelIndex(), 0, renderer->
categories().size() - 1 );
91 const int idx = mRenderer->categories().size();
92 beginInsertRows( QModelIndex(), idx, idx );
93 mRenderer->addCategory( cat );
104 const int row = index.row();
105 if ( row >= catList.size() )
109 return catList.at( row );
113Qt::ItemFlags QgsCategorizedSymbolRendererModel::flags(
const QModelIndex &index )
const
116 if ( !index.isValid() || !mRenderer )
118 return Qt::ItemIsDropEnabled;
121 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable;
122 if ( index.column() == 1 )
125 if ( category.
value().userType() != QMetaType::Type::QVariantList )
127 flags |= Qt::ItemIsEditable;
130 else if ( index.column() == 2 )
132 flags |= Qt::ItemIsEditable;
137Qt::DropActions QgsCategorizedSymbolRendererModel::supportedDropActions()
const
139 return Qt::MoveAction;
142QVariant QgsCategorizedSymbolRendererModel::data(
const QModelIndex &index,
int role )
const
144 if ( !index.isValid() || !mRenderer )
151 case Qt::CheckStateRole:
153 if ( index.column() == 0 )
155 return category.
renderState() ? Qt::Checked : Qt::Unchecked;
160 case Qt::DisplayRole:
161 case Qt::ToolTipRole:
163 switch ( index.column() )
167 if ( category.
value().userType() == QMetaType::Type::QVariantList )
170 const QVariantList list = category.
value().toList();
171 res.reserve( list.size() );
172 for (
const QVariant &v : list )
175 if ( role == Qt::DisplayRole )
176 return res.join(
';' );
178 return res.join(
'\n' );
182 return tr(
"all other values" );
190 return category.
label();
200 italicFont.setItalic(
true );
206 case Qt::DecorationRole:
208 if ( index.column() == 0 && category.
symbol() )
216 case Qt::ForegroundRole:
218 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
221 QColor fadedTextColor = brush.color();
222 fadedTextColor.setAlpha( 128 );
223 brush.setColor( fadedTextColor );
228 case Qt::TextAlignmentRole:
230 return ( index.column() == 0 ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignHCenter ) : static_cast<Qt::Alignment::Int>( Qt::AlignLeft );
235 switch ( index.column() )
239 if ( category.
value().userType() == QMetaType::Type::QVariantList )
242 const QVariantList list = category.
value().toList();
243 res.reserve( list.size() );
244 for (
const QVariant &v : list )
247 return res.join(
';' );
251 return category.
value();
256 return category.
label();
262 if ( index.column() == 1 )
263 return category.
value();
271bool QgsCategorizedSymbolRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
273 if ( !index.isValid() )
276 if ( index.column() == 0 && role == Qt::CheckStateRole )
278 mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
279 emit dataChanged( index, index );
283 if ( role != Qt::EditRole )
286 switch ( index.column() )
291 QVariant val = value;
292 const QVariant previousValue = mRenderer->categories().value( index.row() ).value();
293 if ( previousValue.userType() != QMetaType::Type::QString && !previousValue.toString().isEmpty() )
295 switch ( previousValue.userType() )
297 case QMetaType::Type::Int:
300 case QMetaType::Type::Double:
301 val = value.toDouble();
303 case QMetaType::Type::QVariantList:
305 const QStringList parts = value.toString().split(
';' );
307 list.reserve( parts.count() );
308 for (
const QString &p : parts )
311 if ( list.count() == 1 )
318 val = value.toString();
322 mRenderer->updateCategoryValue( index.row(), val );
326 mRenderer->updateCategoryLabel( index.row(), value.toString() );
332 emit dataChanged( index, index );
336QVariant QgsCategorizedSymbolRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
338 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
341 lst << tr(
"Symbol" ) << tr(
"Value" ) << tr(
"Legend" );
342 return lst.value( section );
347int QgsCategorizedSymbolRendererModel::rowCount(
const QModelIndex &parent )
const
349 if ( parent.isValid() || !mRenderer )
353 return mRenderer->categories().size();
356int QgsCategorizedSymbolRendererModel::columnCount(
const QModelIndex &index )
const
362QModelIndex QgsCategorizedSymbolRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const
364 if ( hasIndex( row, column, parent ) )
366 return createIndex( row, column );
368 return QModelIndex();
371QModelIndex QgsCategorizedSymbolRendererModel::parent(
const QModelIndex &index )
const
374 return QModelIndex();
377QStringList QgsCategorizedSymbolRendererModel::mimeTypes()
const
380 types << mMimeFormat;
384QMimeData *QgsCategorizedSymbolRendererModel::mimeData(
const QModelIndexList &indexes )
const
386 QMimeData *mimeData =
new QMimeData();
387 QByteArray encodedData;
389 QDataStream stream( &encodedData, QIODevice::WriteOnly );
392 const auto constIndexes = indexes;
393 for (
const QModelIndex &index : constIndexes )
395 if ( !index.isValid() || index.column() != 0 )
398 stream << index.row();
400 mimeData->setData( mMimeFormat, encodedData );
404bool QgsCategorizedSymbolRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent )
408 if ( action != Qt::MoveAction )
411 if ( !data->hasFormat( mMimeFormat ) )
414 QByteArray encodedData = data->data( mMimeFormat );
415 QDataStream stream( &encodedData, QIODevice::ReadOnly );
418 while ( !stream.atEnd() )
426 std::sort( rows.begin(), rows.end() );
433 to = mRenderer->categories().size();
434 for (
int i = rows.size() - 1; i >= 0; i-- )
441 mRenderer->moveCategory( rows[i], t );
443 for (
int j = 0; j < i; j++ )
445 if ( to < rows[j] && rows[i] > rows[j] )
452 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
457void QgsCategorizedSymbolRendererModel::deleteRows( QList<int> rows )
459 std::sort( rows.begin(), rows.end() );
460 for (
int i = rows.size() - 1; i >= 0; i-- )
462 beginRemoveRows( QModelIndex(), rows[i], rows[i] );
463 mRenderer->deleteCategory( rows[i] );
468void QgsCategorizedSymbolRendererModel::removeAllRows()
470 beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
471 mRenderer->deleteAllCategories();
475void QgsCategorizedSymbolRendererModel::sort(
int column, Qt::SortOrder order )
483 mRenderer->sortByValue( order );
485 else if ( column == 2 )
487 mRenderer->sortByLabel( order );
489 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
492void QgsCategorizedSymbolRendererModel::updateSymbology()
494 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
498QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QWidget *parent )
502void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element,
const QStyleOption *option, QPainter *painter,
const QWidget *widget )
const
504 if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
506 QStyleOption opt( *option );
507 opt.rect.setLeft( 0 );
509 opt.rect.setHeight( 0 );
511 opt.rect.setRight( widget->width() );
512 QProxyStyle::drawPrimitive( element, &opt, painter, widget );
515 QProxyStyle::drawPrimitive( element, option, painter, widget );
519QgsCategorizedRendererViewItemDelegate::QgsCategorizedRendererViewItemDelegate(
QgsFieldExpressionWidget *expressionWidget, QObject *parent )
520 : QStyledItemDelegate( parent )
521 , mFieldExpressionWidget( expressionWidget )
524QWidget *QgsCategorizedRendererViewItemDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
533 const QString fieldName { mFieldExpressionWidget->currentField( &isExpression, &isValid ) };
534 if ( !fieldName.isEmpty() && mFieldExpressionWidget->layer() && mFieldExpressionWidget->layer()->fields().lookupField( fieldName ) != -1 )
536 userType = mFieldExpressionWidget->layer()->fields().field( fieldName ).type();
538 else if ( isExpression && isValid )
542 if ( mFieldExpressionWidget->layer()->getFeatures().nextFeature( feat ) )
547 expressionContext.
appendScope( mFieldExpressionWidget->layer()->createExpressionContextScope() );
550 const QVariant value = exp.
evaluate( &expressionContext );
553 userType =
static_cast<QMetaType::Type
>( value.userType() );
562 case QMetaType::Type::Double:
568 if ( value.toDouble( &ok ); ok )
570 const QString strVal { value.toString() };
571 const int dotPosition( strVal.indexOf(
'.' ) );
572 if ( dotPosition >= 0 )
574 decimals = std::max<int>( 2, strVal.length() - dotPosition - 1 );
577 editor->setDecimals( decimals );
579 editor->setMaximum( std::numeric_limits<double>::max() );
580 editor->setMinimum( std::numeric_limits<double>::lowest() );
583 case QMetaType::Type::Int:
586 editor->setDecimals( 0 );
588 editor->setMaximum( std::numeric_limits<int>::max() );
589 editor->setMinimum( std::numeric_limits<int>::min() );
592 case QMetaType::Type::QChar:
595 editor->setDecimals( 0 );
597 editor->setMaximum( std::numeric_limits<char>::max() );
598 editor->setMinimum( std::numeric_limits<char>::min() );
601 case QMetaType::Type::UInt:
604 editor->setDecimals( 0 );
606 editor->setMaximum( std::numeric_limits<unsigned int>::max() );
607 editor->setMinimum( 0 );
610 case QMetaType::Type::LongLong:
613 editor->setDecimals( 0 );
615 editor->setMaximum(
static_cast<double>( std::numeric_limits<qlonglong>::max() ) );
616 editor->setMinimum( std::numeric_limits<qlonglong>::min() );
619 case QMetaType::Type::ULongLong:
622 editor->setDecimals( 0 );
624 editor->setMaximum(
static_cast<double>( std::numeric_limits<unsigned long long>::max() ) );
625 editor->setMinimum( 0 );
631 return editor ? editor : QStyledItemDelegate::createEditor( parent, option, index );
644 , mContextMenu( new QMenu( this ) )
659 const QString attrName =
mRenderer->classAttribute();
660 mOldClassificationAttribute = attrName;
664 layout()->setContentsMargins( 0, 0, 0, 0 );
666 mExpressionWidget->setLayer(
mLayer );
667 btnChangeCategorizedSymbol->setLayer(
mLayer );
668 btnChangeCategorizedSymbol->registerExpressionContextGenerator(
this );
671 btnColorRamp->setShowRandomColorRamp(
true );
674 std::unique_ptr<QgsColorRamp> colorRamp(
QgsProject::instance()->styleSettings()->defaultColorRamp() );
677 btnColorRamp->setColorRamp( colorRamp.get() );
681 btnColorRamp->setRandomColorRamp();
691 mModel =
new QgsCategorizedSymbolRendererModel(
this, screen() );
697 viewCategories->setModel(
mModel );
698 viewCategories->resizeColumnToContents( 0 );
699 viewCategories->resizeColumnToContents( 1 );
700 viewCategories->resizeColumnToContents( 2 );
701 viewCategories->setItemDelegateForColumn( 1,
new QgsCategorizedRendererViewItemDelegate( mExpressionWidget, viewCategories ) );
703 viewCategories->setStyle(
new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
704 connect( viewCategories->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QgsCategorizedSymbolRendererWidget::selectionChanged );
712 connect( viewCategories, &QTreeView::customContextMenuRequested,
this, &QgsCategorizedSymbolRendererWidget::showContextMenu );
714 connect( btnChangeCategorizedSymbol, &
QgsSymbolButton::changed,
this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton );
725 QMenu *advMenu =
new QMenu;
732 QAction *actionDdsLegend = advMenu->addAction( tr(
"Data-defined Size Legend…" ) );
734 connect( actionDdsLegend, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
737 btnAdvanced->setMenu( advMenu );
739 mExpressionWidget->registerExpressionContextGenerator(
this );
741 mMergeCategoriesAction =
new QAction( tr(
"Merge Categories" ),
this );
742 connect( mMergeCategoriesAction, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::mergeSelectedCategories );
743 mUnmergeCategoriesAction =
new QAction( tr(
"Unmerge Categories" ),
this );
744 connect( mUnmergeCategoriesAction, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories );
746 connect( mContextMenu, &QMenu::aboutToShow,
this, [
this] {
766 const QString attrName =
mRenderer->classAttribute();
767 mExpressionWidget->setField( attrName );
779 btnColorRamp->setColorRamp(
mRenderer->sourceColorRamp() );
791 btnChangeCategorizedSymbol->setMapCanvas(
context.mapCanvas() );
792 btnChangeCategorizedSymbol->setMessageBar(
context.messageBar() );
797 delete mActionLevels;
798 mActionLevels =
nullptr;
805 if ( !selectedCats.isEmpty() )
816 const auto constSelectedCats = selectedCats;
817 for (
const int idx : constSelectedCats )
823 mRenderer->updateCategorySymbol( idx, newCatSymbol );
843 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( u
"Unable to retrieve values from layer %1 with expression %2"_s.arg(
mLayer->name() ).arg( attrName ), 2 );
920 if ( uniqueValues.size() >= 1000 )
922 const int res = QMessageBox::
923 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 );
924 if ( res == QMessageBox::Cancel )
931 DlgAddCategories dlg(
mStyle, createDefaultSymbol(), unique_vals,
this );
937 bool deleteExisting =
false;
939 if ( !mOldClassificationAttribute.isEmpty() && attrName != mOldClassificationAttribute && !
mRenderer->categories().isEmpty() )
941 const int res = QMessageBox::question(
943 tr(
"Delete Classification" ),
945 "The classification field was changed from '%1' to '%2'.\n"
946 "Should the existing classes be deleted before classification?"
948 .arg( mOldClassificationAttribute, attrName ),
949 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel
951 if ( res == QMessageBox::Cancel )
956 deleteExisting = ( res == QMessageBox::Yes );
960 bool keepExistingColors =
false;
961 if ( !deleteExisting )
964 keepExistingColors = !prevCats.isEmpty();
966 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
968 for (
int i = 0; i < cats.size(); ++i )
970 bool contains =
false;
971 const QVariant value = cats.at( i ).value();
972 for (
int j = 0; j < prevCats.size() && !contains; ++j )
974 const QVariant prevCatValue = prevCats.at( j ).value();
975 if ( prevCatValue.userType() == QMetaType::Type::QVariantList )
977 const QVariantList list = prevCatValue.toList();
978 for (
const QVariant &v : list )
989 if ( prevCats.at( j ).value() == value )
1000 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
1003 cats.at( i ).symbol()->setColor( randomColors.
color( i ) );
1005 prevCats.append( cats.at( i ) );
1011 mOldClassificationAttribute = attrName;
1028 auto r = std::make_unique<QgsCategorizedSymbolRenderer>( attrName, cats );
1030 std::unique_ptr<QgsColorRamp> ramp( btnColorRamp->colorRamp() );
1032 r->setSourceColorRamp( ramp->clone() );
1036 mModel->setRenderer( r.get() );
1039 if ( !keepExistingColors && ramp )
1046 if ( !btnColorRamp->isNull() )
1048 mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
1050 mModel->updateSymbology();
1055 const QModelIndex idx = viewCategories->selectionModel()->currentIndex();
1056 if ( !idx.isValid() )
1064 const QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
1066 const auto constSelectedRows = selectedRows;
1067 for (
const QModelIndex &r : constSelectedRows )
1071 rows.append( r.row() );
1080 mModel->deleteRows( categoryIndexes );
1094 const QString attrName = mExpressionWidget->currentField();
1095 bool valuesRetrieved;
1097 if ( !valuesRetrieved )
1099 QgsDebugMsgLevel( u
"Unable to retrieve values from layer %1 with expression %2"_s.arg(
mLayer->name() ).arg( attrName ), 2 );
1104 QList<int> unusedIndexes;
1106 for (
int i = 0; i < catList.size(); ++i )
1109 if ( !uniqueValues.contains( cat.
value() ) )
1111 unusedIndexes.append( i );
1114 mModel->deleteRows( unusedIndexes );
1120 bool valuesRetrieved;
1122 if ( !valuesRetrieved )
1124 QgsDebugMsgLevel( u
"Unable to retrieve values from layer %1 with expression %2"_s.arg(
mLayer->name() ).arg( attrName ), 2 );
1126 return uniqueValues;
1135 mModel->addCategory( cat );
1143 QItemSelectionModel *m = viewCategories->selectionModel();
1144 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1146 if ( !selectedIndexes.isEmpty() )
1149 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1150 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1152 const int row = ( *indexIt ).row();
1153 QgsSymbol *s = categories[row].symbol();
1167 QItemSelectionModel *m = viewCategories->selectionModel();
1168 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1170 if ( !selectedIndexes.isEmpty() )
1172 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1173 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1175 cl.append(
mModel->category( *indexIt ) );
1194 viewCategories->selectionModel()->clear();
1202 QMessageBox::information(
this, tr(
"Matched Symbols" ), tr(
"Matched %n categories to symbols.",
nullptr, matched ) );
1206 QMessageBox::warning(
this, tr(
"Matched Symbols" ), tr(
"No categories could be matched to symbols in library." ) );
1219 QVariantList unmatchedCategories;
1220 QStringList unmatchedSymbols;
1221 const int matched =
mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
1223 mModel->updateSymbology();
1230 const QString openFileDir = settings.
value( u
"UI/lastMatchToSymbolsDir"_s, QDir::homePath() ).toString();
1232 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Match to Symbols from File" ), openFileDir, tr(
"XML files (*.xml *.XML)" ) );
1233 if ( fileName.isEmpty() )
1238 const QFileInfo openFileInfo( fileName );
1239 settings.
setValue( u
"UI/lastMatchToSymbolsDir"_s, openFileInfo.absolutePath() );
1242 if ( !importedStyle.
importXml( fileName ) )
1244 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ), tr(
"An error occurred while reading file:\n%1" ).arg( importedStyle.
errorString() ) );
1251 QMessageBox::information(
this, tr(
"Match to Symbols from File" ), tr(
"Matched %n categories to symbols from file.",
nullptr, matched ) );
1255 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ), tr(
"No categories could be matched to symbols in file." ) );
1266 mRenderer->setLegendSymbolItem( legendSymbol.ruleKey(), sym->
clone() );
1269 mRenderer->setUsingSymbolLevels( enabled );
1270 mModel->updateSymbology();
1281 if ( !selectedCats.isEmpty() )
1283 for (
const int idx : selectedCats )
1285 if (
mRenderer->categories().at( idx ).symbol()->type() != tempSymbol->type() )
1288 std::unique_ptr<QgsSymbol> newCatSymbol( tempSymbol->clone() );
1289 if ( selectedCats.count() > 1 )
1292 newCatSymbol->setColor(
mRenderer->categories().at( idx ).symbol()->color() );
1294 mRenderer->updateCategorySymbol( idx, newCatSymbol.release() );
1307void QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton()
1317 QItemSelectionModel *m = viewCategories->selectionModel();
1318 const QModelIndexList i = m->selectedRows();
1324 if ( !selectedCats.isEmpty() )
1326 const auto constSelectedCats = selectedCats;
1327 for (
const int idx : constSelectedCats )
1330 if ( selectedCats.count() > 1 )
1335 mRenderer->updateCategorySymbol( idx, newCatSymbol );
1344 mModel->updateSymbology();
1355 if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1357 mCopyBuffer.clear();
1360 else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1362 QgsCategoryList::iterator rIt = mCopyBuffer.begin();
1363 for ( ; rIt != mCopyBuffer.end(); ++rIt )
1365 rIt->mUuid = QUuid::createUuid().toString();
1366 mModel->addCategory( *rIt );
1374 if (
auto *lMapCanvas =
mContext.mapCanvas() )
1376 expContext = lMapCanvas->createExpressionContext();
1391 const auto constAdditionalExpressionContextScopes =
mContext.additionalExpressionContextScopes();
1400void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
1414void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
1419 QList<int> categoryIndexes;
1422 for (
const int i : selectedCategoryIndexes )
1424 const QVariant v = categories.at( i ).value();
1426 if ( !v.isValid() || v ==
"" )
1431 categoryIndexes.append( i );
1434 if ( categoryIndexes.count() < 2 )
1438 QVariantList values;
1439 values.reserve( categoryIndexes.count() );
1440 labels.reserve( categoryIndexes.count() );
1441 for (
const int i : categoryIndexes )
1443 const QVariant v = categories.at( i ).value();
1445 if ( v.userType() == QMetaType::Type::QVariantList )
1447 values.append( v.toList() );
1452 labels << categories.at( i ).label();
1456 mRenderer->updateCategoryLabel( categoryIndexes.at( 0 ), labels.join(
',' ) );
1457 mRenderer->updateCategoryValue( categoryIndexes.at( 0 ), values );
1459 categoryIndexes.pop_front();
1460 mModel->deleteRows( categoryIndexes );
1465void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
1468 if ( categoryIndexes.isEmpty() )
1472 for (
const int i : categoryIndexes )
1474 const QVariant v = categories.at( i ).value();
1475 if ( v.userType() != QMetaType::Type::QVariantList )
1478 const QVariantList list = v.toList();
1479 for (
int j = 1; j < list.count(); ++j )
1481 mModel->addCategory( QgsRendererCategory( list.at( j ), categories.at( i ).symbol()->clone(), list.at( j ).toString(), categories.at( i ).renderState() ) );
1483 mRenderer->updateCategoryValue( i, list.at( 0 ) );
1484 mRenderer->updateCategoryLabel( i, list.at( 0 ).toString() );
1490void QgsCategorizedSymbolRendererWidget::showContextMenu( QPoint )
1492 mContextMenu->clear();
1493 const QList<QAction *> actions =
contextMenu->actions();
1494 for ( QAction *act : actions )
1496 mContextMenu->addAction( act );
1499 mContextMenu->addSeparator();
1501 if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1503 mContextMenu->addAction( mMergeCategoriesAction );
1505 if ( viewCategories->selectionModel()->selectedRows().count() == 1 )
1509 const QVariant v = categories.at( categoryIndexes.at( 0 ) ).value();
1510 if ( v.userType() == QMetaType::Type::QVariantList )
1511 mContextMenu->addAction( mUnmergeCategoriesAction );
1513 else if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1515 mContextMenu->addAction( mUnmergeCategoriesAction );
1518 mContextMenu->exec( QCursor::pos() );
1521void QgsCategorizedSymbolRendererWidget::selectionChanged(
const QItemSelection &,
const QItemSelection & )
1524 if ( !selectedCats.isEmpty() )
1526 whileBlocking( btnChangeCategorizedSymbol )->setSymbol(
mRenderer->categories().at( selectedCats.at( 0 ) ).symbol()->clone() );
1532 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)