47 #include <QMessageBox>
48 #include <QStandardItemModel>
49 #include <QStandardItem>
52 #include <QFileDialog>
57 QgsCategorizedSymbolRendererModel::QgsCategorizedSymbolRendererModel( QObject *parent ) : QAbstractItemModel( parent )
58 , mMimeFormat( QStringLiteral(
"application/x-qgscategorizedsymbolrendererv2model" ) )
66 beginRemoveRows( QModelIndex(), 0, std::max< int >( mRenderer->categories().size() - 1, 0 ) );
75 beginInsertRows( QModelIndex(), 0, renderer->
categories().size() - 1 );
83 if ( !mRenderer )
return;
84 const int idx = mRenderer->categories().size();
85 beginInsertRows( QModelIndex(), idx, idx );
86 mRenderer->addCategory( cat );
97 const int row = index.row();
98 if ( row >= catList.size() )
102 return catList.at( row );
106 Qt::ItemFlags QgsCategorizedSymbolRendererModel::flags(
const QModelIndex &index )
const
108 if ( !index.isValid() || !mRenderer )
110 return Qt::ItemIsDropEnabled;
113 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
114 if ( index.column() == 1 )
117 if ( category.
value().type() != QVariant::List )
119 flags |= Qt::ItemIsEditable;
122 else if ( index.column() == 2 )
124 flags |= Qt::ItemIsEditable;
129 Qt::DropActions QgsCategorizedSymbolRendererModel::supportedDropActions()
const
131 return Qt::MoveAction;
134 QVariant QgsCategorizedSymbolRendererModel::data(
const QModelIndex &index,
int role )
const
136 if ( !index.isValid() || !mRenderer )
143 case Qt::CheckStateRole:
145 if ( index.column() == 0 )
147 return category.
renderState() ? Qt::Checked : Qt::Unchecked;
152 case Qt::DisplayRole:
153 case Qt::ToolTipRole:
155 switch ( index.column() )
159 if ( category.
value().type() == QVariant::List )
162 const QVariantList list = category.
value().toList();
163 res.reserve( list.size() );
164 for (
const QVariant &v : list )
167 if ( role == Qt::DisplayRole )
168 return res.join(
';' );
170 return res.join(
'\n' );
172 else if ( !category.
value().isValid() || category.
value().isNull() || category.
value().toString().isEmpty() )
174 return tr(
"all other values" );
182 return category.
label();
189 if ( index.column() == 1 && category.
value().type() != QVariant::List && ( !category.
value().isValid() || category.
value().isNull() || category.
value().toString().isEmpty() ) )
192 italicFont.setItalic(
true );
198 case Qt::DecorationRole:
200 if ( index.column() == 0 && category.
symbol() )
208 case Qt::ForegroundRole:
210 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
211 if ( index.column() == 1 && ( category.
value().type() == QVariant::List
212 || !category.
value().isValid() || category.
value().isNull() || category.
value().toString().isEmpty() ) )
214 QColor fadedTextColor = brush.color();
215 fadedTextColor.setAlpha( 128 );
216 brush.setColor( fadedTextColor );
221 case Qt::TextAlignmentRole:
223 return ( index.column() == 0 ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignHCenter ) :
static_cast<Qt::Alignment::Int
>( Qt::AlignLeft );
228 switch ( index.column() )
232 if ( category.
value().type() == QVariant::List )
235 const QVariantList list = category.
value().toList();
236 res.reserve( list.size() );
237 for (
const QVariant &v : list )
240 return res.join(
';' );
244 return category.
value();
249 return category.
label();
253 case QgsCategorizedSymbolRendererWidget::CustomRoles::ValueRole:
255 if ( index.column() == 1 )
256 return category.
value();
264 bool QgsCategorizedSymbolRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
266 if ( !index.isValid() )
269 if ( index.column() == 0 && role == Qt::CheckStateRole )
271 mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
272 emit dataChanged( index, index );
276 if ( role != Qt::EditRole )
279 switch ( index.column() )
284 QVariant val = value;
285 const QVariant previousValue = mRenderer->categories().value( index.row() ).value();
286 if ( previousValue.type() != QVariant::String && ! previousValue.toString().isEmpty() )
288 switch ( previousValue.type() )
293 case QVariant::Double:
294 val = value.toDouble();
298 const QStringList parts = value.toString().split(
';' );
300 list.reserve( parts.count() );
301 for (
const QString &p : parts )
304 if ( list.count() == 1 )
311 val = value.toString();
315 mRenderer->updateCategoryValue( index.row(), val );
319 mRenderer->updateCategoryLabel( index.row(), value.toString() );
325 emit dataChanged( index, index );
329 QVariant QgsCategorizedSymbolRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
331 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
334 lst << tr(
"Symbol" ) << tr(
"Value" ) << tr(
"Legend" );
335 return lst.value( section );
340 int QgsCategorizedSymbolRendererModel::rowCount(
const QModelIndex &parent )
const
342 if ( parent.isValid() || !mRenderer )
346 return mRenderer->categories().size();
349 int QgsCategorizedSymbolRendererModel::columnCount(
const QModelIndex &index )
const
355 QModelIndex QgsCategorizedSymbolRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const
357 if ( hasIndex( row, column, parent ) )
359 return createIndex( row, column );
361 return QModelIndex();
364 QModelIndex QgsCategorizedSymbolRendererModel::parent(
const QModelIndex &index )
const
367 return QModelIndex();
370 QStringList QgsCategorizedSymbolRendererModel::mimeTypes()
const
373 types << mMimeFormat;
377 QMimeData *QgsCategorizedSymbolRendererModel::mimeData(
const QModelIndexList &indexes )
const
379 QMimeData *mimeData =
new QMimeData();
380 QByteArray encodedData;
382 QDataStream stream( &encodedData, QIODevice::WriteOnly );
385 const auto constIndexes = indexes;
386 for (
const QModelIndex &index : constIndexes )
388 if ( !index.isValid() || index.column() != 0 )
391 stream << index.row();
393 mimeData->setData( mMimeFormat, encodedData );
397 bool QgsCategorizedSymbolRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent )
401 if ( action != Qt::MoveAction )
return true;
403 if ( !data->hasFormat( mMimeFormat ) )
return false;
405 QByteArray encodedData = data->data( mMimeFormat );
406 QDataStream stream( &encodedData, QIODevice::ReadOnly );
409 while ( !stream.atEnd() )
416 int to = parent.row();
419 if ( to == -1 ) to = mRenderer->categories().size();
420 for (
int i = rows.size() - 1; i >= 0; i-- )
422 QgsDebugMsg( QStringLiteral(
"move %1 to %2" ).arg( rows[i] ).arg( to ) );
425 if ( rows[i] < t ) t--;
426 mRenderer->moveCategory( rows[i], t );
428 for (
int j = 0; j < i; j++ )
430 if ( to < rows[j] && rows[i] > rows[j] ) rows[j] += 1;
433 if ( rows[i] < to ) to--;
435 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
440 void QgsCategorizedSymbolRendererModel::deleteRows( QList<int> rows )
442 std::sort( rows.begin(), rows.end() );
443 for (
int i = rows.size() - 1; i >= 0; i-- )
445 beginRemoveRows( QModelIndex(), rows[i], rows[i] );
446 mRenderer->deleteCategory( rows[i] );
451 void QgsCategorizedSymbolRendererModel::removeAllRows()
453 beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
454 mRenderer->deleteAllCategories();
458 void QgsCategorizedSymbolRendererModel::sort(
int column, Qt::SortOrder order )
466 mRenderer->sortByValue( order );
468 else if ( column == 2 )
470 mRenderer->sortByLabel( order );
472 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
475 void QgsCategorizedSymbolRendererModel::updateSymbology()
477 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
481 QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QWidget *parent )
485 void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element,
const QStyleOption *option, QPainter *painter,
const QWidget *widget )
const
487 if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
489 QStyleOption opt( *option );
490 opt.rect.setLeft( 0 );
492 opt.rect.setHeight( 0 );
493 if ( widget ) opt.rect.setRight( widget->width() );
494 QProxyStyle::drawPrimitive( element, &opt, painter, widget );
497 QProxyStyle::drawPrimitive( element, option, painter, widget );
501 QgsCategorizedRendererViewItemDelegate::QgsCategorizedRendererViewItemDelegate(
QgsFieldExpressionWidget *expressionWidget, QObject *parent )
502 : QStyledItemDelegate( parent )
503 , mFieldExpressionWidget( expressionWidget )
507 QWidget *QgsCategorizedRendererViewItemDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
509 QVariant::Type userType { index.data( QgsCategorizedSymbolRendererWidget::CustomRoles::ValueRole ).type() };
512 if ( userType == QVariant::String && index.data( QgsCategorizedSymbolRendererWidget::CustomRoles::ValueRole ).isNull() )
516 const QString fieldName { mFieldExpressionWidget->currentField( &isExpression, &isValid ) };
517 if ( ! fieldName.isEmpty() && mFieldExpressionWidget->layer() && mFieldExpressionWidget->layer()->fields().lookupField( fieldName ) != -1 )
519 userType = mFieldExpressionWidget->layer()->fields().field( fieldName ).type();
521 else if ( isExpression && isValid )
525 if ( mFieldExpressionWidget->layer()->getFeatures().nextFeature( feat ) )
530 expressionContext.
appendScope( mFieldExpressionWidget->layer()->createExpressionContextScope() );
533 const QVariant value = exp.evaluate( &expressionContext );
534 if ( !exp.hasEvalError() )
536 userType = value.type();
545 case QVariant::Type::Double:
549 const QVariant value = index.data( QgsCategorizedSymbolRendererWidget::CustomRoles::ValueRole );
551 if ( value.toDouble( &ok ); ok )
553 const QString strVal { value.toString() };
554 const int dotPosition( strVal.indexOf(
'.' ) );
555 if ( dotPosition >= 0 )
557 decimals = std::max<int>( 2, strVal.length() - dotPosition - 1 );
560 editor->setDecimals( decimals );
562 editor->setMaximum( std::numeric_limits<double>::max() );
563 editor->setMinimum( std::numeric_limits<double>::lowest() );
566 case QVariant::Type::Int:
569 editor->setDecimals( 0 );
571 editor->setMaximum( std::numeric_limits<int>::max() );
572 editor->setMinimum( std::numeric_limits<int>::min() );
575 case QVariant::Type::Char:
578 editor->setDecimals( 0 );
580 editor->setMaximum( std::numeric_limits<char>::max() );
581 editor->setMinimum( std::numeric_limits<char>::min() );
584 case QVariant::Type::UInt:
587 editor->setDecimals( 0 );
589 editor->setMaximum( std::numeric_limits<unsigned int>::max() );
590 editor->setMinimum( 0 );
593 case QVariant::Type::LongLong:
596 editor->setDecimals( 0 );
598 editor->setMaximum(
static_cast<double>( std::numeric_limits<qlonglong>::max() ) );
599 editor->setMinimum( std::numeric_limits<qlonglong>::min() );
602 case QVariant::Type::ULongLong:
605 editor->setDecimals( 0 );
607 editor->setMaximum(
static_cast<double>( std::numeric_limits<unsigned long long>::max() ) );
608 editor->setMinimum( 0 );
614 return editor ? editor : QStyledItemDelegate::createEditor( parent, option, index );
627 , mContextMenu( new QMenu( this ) )
643 const QString attrName =
mRenderer->classAttribute();
644 mOldClassificationAttribute = attrName;
648 layout()->setContentsMargins( 0, 0, 0, 0 );
650 mExpressionWidget->setLayer(
mLayer );
651 btnChangeCategorizedSymbol->setLayer(
mLayer );
652 btnChangeCategorizedSymbol->registerExpressionContextGenerator(
this );
655 btnColorRamp->setShowRandomColorRamp(
true );
658 const QString defaultColorRamp =
QgsProject::instance()->
readEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/ColorRamp" ), QString() );
659 if ( !defaultColorRamp.isEmpty() )
661 btnColorRamp->setColorRampFromName( defaultColorRamp );
665 btnColorRamp->setRandomColorRamp();
675 mModel =
new QgsCategorizedSymbolRendererModel(
this );
681 viewCategories->setModel(
mModel );
682 viewCategories->resizeColumnToContents( 0 );
683 viewCategories->resizeColumnToContents( 1 );
684 viewCategories->resizeColumnToContents( 2 );
685 viewCategories->setItemDelegateForColumn( 1,
new QgsCategorizedRendererViewItemDelegate( mExpressionWidget, viewCategories ) );
687 viewCategories->setStyle(
new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
688 connect( viewCategories->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QgsCategorizedSymbolRendererWidget::selectionChanged );
696 connect( viewCategories, &QTreeView::customContextMenuRequested,
this, &QgsCategorizedSymbolRendererWidget::showContextMenu );
698 connect( btnChangeCategorizedSymbol, &
QgsSymbolButton::changed,
this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton );
708 QMenu *advMenu =
new QMenu;
715 QAction *actionDdsLegend = advMenu->addAction( tr(
"Data-defined Size Legend…" ) );
717 connect( actionDdsLegend, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
720 btnAdvanced->setMenu( advMenu );
722 mExpressionWidget->registerExpressionContextGenerator(
this );
724 mMergeCategoriesAction =
new QAction( tr(
"Merge Categories" ),
this );
725 connect( mMergeCategoriesAction, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::mergeSelectedCategories );
726 mUnmergeCategoriesAction =
new QAction( tr(
"Unmerge Categories" ),
this );
727 connect( mUnmergeCategoriesAction, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories );
729 connect( mContextMenu, &QMenu::aboutToShow,
this, [ = ]
750 const QString attrName =
mRenderer->classAttribute();
751 mExpressionWidget->setField( attrName );
763 btnColorRamp->setColorRamp(
mRenderer->sourceColorRamp() );
781 delete mActionLevels;
782 mActionLevels =
nullptr;
789 if ( !selectedCats.isEmpty() )
800 const auto constSelectedCats = selectedCats;
801 for (
const int idx : constSelectedCats )
807 mRenderer->updateCategorySymbol( idx, newCatSymbol );
830 if ( !dlg.exec() || !newSymbol )
853 if ( idx.isValid() && idx.column() == 0 )
861 std::unique_ptr< QgsSymbol > symbol;
863 if (
auto *lSymbol = category.
symbol() )
865 symbol.reset( lSymbol->clone() );
886 if ( !dlg.exec() || !symbol )
899 const QString attrName = mExpressionWidget->currentField();
901 QList<QVariant> uniqueValues;
919 if ( uniqueValues.contains( value ) )
921 uniqueValues << value;
930 if ( uniqueValues.size() >= 1000 )
932 const int res = QMessageBox::warning(
nullptr, tr(
"Classify Categories" ),
933 tr(
"High number of classes. Classification would yield %n entries which might not be expected. Continue?",
nullptr, uniqueValues.size() ),
934 QMessageBox::Ok | QMessageBox::Cancel,
935 QMessageBox::Cancel );
936 if ( res == QMessageBox::Cancel )
943 DlgAddCategories dlg(
mStyle, createDefaultSymbol(), unique_vals,
this );
949 bool deleteExisting =
false;
951 if ( !mOldClassificationAttribute.isEmpty() &&
952 attrName != mOldClassificationAttribute &&
955 const int res = QMessageBox::question(
this,
956 tr(
"Delete Classification" ),
957 tr(
"The classification field was changed from '%1' to '%2'.\n"
958 "Should the existing classes be deleted before classification?" )
959 .arg( mOldClassificationAttribute, attrName ),
960 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
961 if ( res == QMessageBox::Cancel )
966 deleteExisting = ( res == QMessageBox::Yes );
970 bool keepExistingColors =
false;
971 if ( !deleteExisting )
974 keepExistingColors = !prevCats.isEmpty();
976 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
978 for (
int i = 0; i < cats.size(); ++i )
980 bool contains =
false;
981 const QVariant value = cats.at( i ).value();
982 for (
int j = 0; j < prevCats.size() && !contains; ++j )
984 const QVariant prevCatValue = prevCats.at( j ).value();
985 if ( prevCatValue.type() == QVariant::List )
987 const QVariantList list = prevCatValue.toList();
988 for (
const QVariant &v : list )
999 if ( prevCats.at( j ).value() == value )
1010 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
1013 cats.at( i ).symbol()->setColor( randomColors.
color( i ) );
1015 prevCats.append( cats.at( i ) );
1021 mOldClassificationAttribute = attrName;
1038 std::unique_ptr< QgsCategorizedSymbolRenderer > r = std::make_unique< QgsCategorizedSymbolRenderer >( attrName, cats );
1040 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
1042 r->setSourceColorRamp( ramp->clone() );
1046 mModel->setRenderer( r.get() );
1049 if ( ! keepExistingColors && ramp )
1056 if ( !btnColorRamp->isNull() )
1058 mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
1060 mModel->updateSymbology();
1065 const QModelIndex idx = viewCategories->selectionModel()->currentIndex();
1066 if ( !idx.isValid() )
1074 const QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
1076 const auto constSelectedRows = selectedRows;
1077 for (
const QModelIndex &r : constSelectedRows )
1081 rows.append( r.row() );
1090 mModel->deleteRows( categoryIndexes );
1105 mModel->addCategory( cat );
1113 QItemSelectionModel *m = viewCategories->selectionModel();
1114 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1116 if ( !selectedIndexes.isEmpty() )
1119 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1120 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1122 const int row = ( *indexIt ).row();
1123 QgsSymbol *s = categories[row].symbol();
1137 QItemSelectionModel *m = viewCategories->selectionModel();
1138 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1140 if ( !selectedIndexes.isEmpty() )
1142 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1143 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1145 cl.append(
mModel->category( *indexIt ) );
1164 viewCategories->selectionModel()->clear();
1172 QMessageBox::information(
this, tr(
"Matched Symbols" ),
1173 tr(
"Matched %n categories to symbols.",
nullptr, matched ) );
1177 QMessageBox::warning(
this, tr(
"Matched Symbols" ),
1178 tr(
"No categories could be matched to symbols in library." ) );
1191 QVariantList unmatchedCategories;
1192 QStringList unmatchedSymbols;
1193 const int matched =
mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
1195 mModel->updateSymbology();
1202 const QString openFileDir = settings.
value( QStringLiteral(
"UI/lastMatchToSymbolsDir" ), QDir::homePath() ).toString();
1204 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Match to Symbols from File" ), openFileDir,
1205 tr(
"XML files (*.xml *.XML)" ) );
1206 if ( fileName.isEmpty() )
1211 const QFileInfo openFileInfo( fileName );
1212 settings.
setValue( QStringLiteral(
"UI/lastMatchToSymbolsDir" ), openFileInfo.absolutePath() );
1215 if ( !importedStyle.
importXml( fileName ) )
1217 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ),
1218 tr(
"An error occurred while reading file:\n%1" ).arg( importedStyle.
errorString() ) );
1225 QMessageBox::information(
this, tr(
"Match to Symbols from File" ),
1226 tr(
"Matched %n categories to symbols from file.",
nullptr, matched ) );
1230 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ),
1231 tr(
"No categories could be matched to symbols in file." ) );
1242 mRenderer->setLegendSymbolItem( legendSymbol.ruleKey(), sym->
clone() );
1245 mRenderer->setUsingSymbolLevels( enabled );
1246 mModel->updateSymbology();
1257 if ( !selectedCats.isEmpty() )
1259 for (
const int idx : selectedCats )
1261 if (
mRenderer->categories().at( idx ).symbol()->type() != tempSymbol->type() )
1264 std::unique_ptr< QgsSymbol > newCatSymbol( tempSymbol->clone() );
1265 if ( selectedCats.count() > 1 )
1268 newCatSymbol->setColor(
mRenderer->categories().at( idx ).symbol()->color() );
1270 mRenderer->updateCategorySymbol( idx, newCatSymbol.release() );
1276 void QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector(
QgsPanelWidget *container )
1285 void QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget()
1293 void QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton()
1303 QItemSelectionModel *m = viewCategories->selectionModel();
1304 const QModelIndexList i = m->selectedRows();
1310 if ( !selectedCats.isEmpty() )
1312 const auto constSelectedCats = selectedCats;
1313 for (
const int idx : constSelectedCats )
1316 if ( selectedCats.count() > 1 )
1321 mRenderer->updateCategorySymbol( idx, newCatSymbol );
1330 mModel->updateSymbology();
1341 if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1343 mCopyBuffer.clear();
1346 else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1348 QgsCategoryList::const_iterator rIt = mCopyBuffer.constBegin();
1349 for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1351 mModel->addCategory( *rIt );
1369 expContext << generator->createExpressionContextScope();
1390 void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
1405 void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
1410 QList< int > categoryIndexes;
1413 for (
const int i : selectedCategoryIndexes )
1415 const QVariant v = categories.at( i ).value();
1417 if ( !v.isValid() || v ==
"" )
1422 categoryIndexes.append( i );
1425 if ( categoryIndexes.count() < 2 )
1429 QVariantList values;
1430 values.reserve( categoryIndexes.count() );
1431 labels.reserve( categoryIndexes.count() );
1432 for (
const int i : categoryIndexes )
1434 const QVariant v = categories.at( i ).value();
1436 if ( v.type() == QVariant::List )
1438 values.append( v.toList() );
1443 labels << categories.at( i ).label();
1447 mRenderer->updateCategoryLabel( categoryIndexes.at( 0 ), labels.join(
',' ) );
1448 mRenderer->updateCategoryValue( categoryIndexes.at( 0 ), values );
1450 categoryIndexes.pop_front();
1451 mModel->deleteRows( categoryIndexes );
1456 void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
1459 if ( categoryIndexes.isEmpty() )
1463 for (
const int i : categoryIndexes )
1465 const QVariant v = categories.at( i ).value();
1466 if ( v.type() != QVariant::List )
1469 const QVariantList list = v.toList();
1470 for (
int j = 1; j < list.count(); ++j )
1472 mModel->addCategory(
QgsRendererCategory( list.at( j ), categories.at( i ).symbol()->clone(), list.at( j ).toString(), categories.at( i ).renderState() ) );
1474 mRenderer->updateCategoryValue( i, list.at( 0 ) );
1475 mRenderer->updateCategoryLabel( i, list.at( 0 ).toString() );
1481 void QgsCategorizedSymbolRendererWidget::showContextMenu( QPoint )
1483 mContextMenu->clear();
1484 const QList< QAction * > actions =
contextMenu->actions();
1485 for ( QAction *act : actions )
1487 mContextMenu->addAction( act );
1490 mContextMenu->addSeparator();
1492 if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1494 mContextMenu->addAction( mMergeCategoriesAction );
1496 if ( viewCategories->selectionModel()->selectedRows().count() == 1 )
1500 const QVariant v = categories.at( categoryIndexes.at( 0 ) ).value();
1501 if ( v.type() == QVariant::List )
1502 mContextMenu->addAction( mUnmergeCategoriesAction );
1504 else if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1506 mContextMenu->addAction( mUnmergeCategoriesAction );
1509 mContextMenu->exec( QCursor::pos() );
1512 void QgsCategorizedSymbolRendererWidget::selectionChanged(
const QItemSelection &,
const QItemSelection & )
1515 if ( !selectedCats.isEmpty() )
1517 whileBlocking( btnChangeCategorizedSymbol )->setSymbol(
mRenderer->categories().at( selectedCats.at( 0 ) ).symbol()->clone() );
1523 btnChangeCategorizedSymbol->setDialogTitle( selectedCats.size() == 1 ?
mRenderer->categories().at( selectedCats.at( 0 ) ).label() : tr(
"Symbol Settings" ) );
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...
Abstract interface for generating an expression context scope.
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.
Class for 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)
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...
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
The QgsMapSettings class contains configuration for rendering of the map.
A marker symbol type, for rendering Point and MultiPoint geometries.
static QgsProject * instance()
Returns the QgsProject singleton instance.
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
Reads a string from the specified scope and key.
A QProxyStyle subclass which correctly sets the base style to match the QGIS application style,...
Totally random color ramp.
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...
This class is a composition of two QSettings instances:
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.
QString errorString()
Returns last error from load/save operation.
static QgsStyle * defaultStyle()
Returns 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 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)
Returns an icon preview for a color ramp.
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.
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
void setColor(const QColor &color)
Sets the color for the symbol.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
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