QGIS API Documentation 4.3.0-Master (621ced71bd7)
Loading...
Searching...
No Matches
qgssymbollayermodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssymbollayermodel.h
3 ---------------------
4 begin : June 2026
5 copyright : (C) 2026 by Valentin Buira
6 email : valentin dot buira at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgssymbollayermodel.h"
17
18#include "qgsapplication.h"
20#include "qgsguiutils.h"
21#include "qgslinesymbol.h"
22#include "qgsmarkersymbol.h"
23#include "qgsstyle.h"
24#include "qgssymbol.h"
25#include "qgssymbollayer.h"
27#include "qgssymbollayerutils.h"
28#include "qgsvectorlayer.h"
29
30#include <qscreen.h>
31
32#include "moc_qgssymbollayermodel.cpp"
33
34using namespace Qt::StringLiterals;
35
38
40 : mVectorLayer( vectorLayer )
41 , mScreen( screen )
42{
43 setLayer( layer, symbolType );
44}
45
47 : mVectorLayer( vectorLayer )
48 , mScreen( screen )
49{
50 setSymbol( symbol );
51}
52
57
59{
60 mLayer = layer;
61 mIsLayer = true;
62 mSymbol = nullptr;
63 mSymbolType = symbolType;
64}
65
67{
68 mScreen = screen;
69}
70
71void QgsSymbolLayerModelNode::setSymbol( QgsSymbol *symbol )
72{
73 mSymbol = symbol;
74 mIsLayer = false;
75 mLayer = nullptr;
76}
77
79{
80 const int size = QgsGuiUtils::scaleIconSize( 16 );
81 const QSize iconSize = QSize( size, size );
82
83 QIcon icon;
84 if ( mIsLayer )
86 symbolLayerPreviewIcon( mLayer, Qgis::RenderUnit::Millimeters, iconSize, QgsMapUnitScale(), mSymbol ? mSymbol->type() : mSymbolType, mVectorLayer, QgsScreenProperties( mScreen.data() ) );
87 else
88 {
89 QgsExpressionContext expContext;
90 // TODO -- this model should have a way to register an explicit expression context, so that the preview icons
91 // correctly reflect atlas features/map scales/etc
93 icon = QIcon( QgsSymbolLayerUtils::symbolPreviewPixmap( mSymbol, iconSize, 0, nullptr, false, &expContext, nullptr, QgsScreenProperties( mScreen.data() ) ) );
94 }
95 return icon;
96}
97
98QVariant QgsSymbolLayerModelNode::data( int role ) const
99{
100 if ( role == Qt::DisplayRole || role == Qt::EditRole )
101 {
102 if ( mIsLayer )
103 {
105 if ( m )
106 return m->visibleName();
107 else
108 return QString();
109 }
110 else
111 {
112 switch ( mSymbol->type() )
113 {
115 return QCoreApplication::translate( "QgsSymbolLayerModelNode", "Marker" );
117 return QCoreApplication::translate( "QgsSymbolLayerModelNode", "Fill" );
119 return QCoreApplication::translate( "QgsSymbolLayerModelNode", "Line" );
121 return "Symbol";
122 default:
123 QgsDebugError( "Unhandled Symbol Type" );
124 }
125 }
126 }
127 else if ( role == Qt::ForegroundRole && mIsLayer )
128 {
129 if ( !mLayer->enabled() )
130 {
131 QPalette pal = qApp->palette();
132 QBrush brush = QBrush();
133 brush.setColor( pal.color( QPalette::Disabled, QPalette::WindowText ) );
134 return brush;
135 }
136 else
137 {
138 return QVariant();
139 }
140 }
141 else if ( role == Qt::DecorationRole )
142 {
143 return icon();
144 }
145 else if ( role == Qt::FontRole && !mIsLayer )
146 {
147 QFont font;
148 font.setBold( true );
149 return font;
150 }
151 return QVariant();
152}
153
155{
156 mChildren.clear();
157}
158
159QgsSymbolLayerModelNode *QgsSymbolLayerModelNode::addChildNode( std::unique_ptr<QgsSymbolLayerModelNode> node )
160{
161 if ( !node )
162 return nullptr;
163
164 Q_ASSERT( !node->mParent );
165 node->mParent = this;
166
167 return mChildren.emplace_back( std::move( node ) ).get();
168}
169
170QgsSymbolLayerModelNode *QgsSymbolLayerModelNode::insertChildNode( int index, std::unique_ptr<QgsSymbolLayerModelNode> node )
171{
172 if ( !node )
173 return nullptr;
174
175 Q_ASSERT( !node->mParent );
176 node->mParent = this;
177
178 return mChildren.insert( mChildren.begin() + index, std::move( node ) )->get();
179}
180
182{
183 if ( !node )
184 return;
185
186 // Only allow moving a node that is a child of this current node
187 Q_ASSERT( node->mParent == this );
188
189 int idx = indexOf( node );
190 if ( idx != -1 && idx != to )
191 {
192 std::unique_ptr<QgsSymbolLayerModelNode> n = std::move( mChildren.at( idx ) );
193 mChildren.erase( mChildren.begin() + idx );
194 mChildren.insert( mChildren.begin() + to, std::move( n ) );
195 }
196}
197
199{
200 if ( !node )
201 return;
202
203 int idx = indexOf( node );
204 if ( idx != -1 )
205 {
206 mChildren.erase( mChildren.begin() + idx );
207 }
208}
209
211{
212 return mChildren.at( index ).get();
213}
214
216{
217 Q_ASSERT( node->mParent == this );
218
219 auto it = std::find_if( mChildren.begin(), mChildren.end(), [&]( const std::unique_ptr<QgsSymbolLayerModelNode> &p ) { return p.get() == node; } );
220
221 if ( it != mChildren.end() )
222 return std::distance( mChildren.begin(), it );
223 return -1;
224}
225
227{
228 return mChildren.size();
229}
230
232{
233 if ( !mParent )
234 return -1;
235
236 return mParent->indexOf( this );
237}
238
240 : QAbstractItemModel( parent )
241 , mRootNode( std::make_unique<QgsSymbolLayerModelNode>() )
242 , mVectorLayer( vl )
243 , mScreen( screen )
244{}
245
246
247QVariant QgsSymbolLayerModel::data( const QModelIndex &index, int role ) const
248{
249 if ( !index.isValid() )
250 return QVariant();
251
253 if ( !node )
254 return QVariant();
255
256 return node->data( role );
257};
258
259int QgsSymbolLayerModel::rowCount( const QModelIndex &parent ) const
260{
262 if ( !n )
263 return 0;
264
265 return n->rowCount();
266};
267
268int QgsSymbolLayerModel::columnCount( const QModelIndex & ) const
269{
270 return 1;
271};
272
273QModelIndex QgsSymbolLayerModel::index( int row, int column, const QModelIndex &parent ) const
274{
275 if ( !hasIndex( row, column, parent ) )
276 return QModelIndex();
277
279 if ( !node )
280 return QModelIndex(); // have no children
281
282 return createIndex( row, column, node->childAt( row ) );
283};
284
285QModelIndex QgsSymbolLayerModel::parent( const QModelIndex &child ) const
286{
287 if ( !child.isValid() )
288 return QModelIndex();
289
290 if ( QgsSymbolLayerModelNode *node = index2node( child ) )
291 {
292 return indexOfParentTreeNode( node->parent() ); // must not be null
293 }
294 else
295 {
296 Q_ASSERT( false ); // no other node types!
297 return QModelIndex();
298 }
299};
300
301QModelIndex QgsSymbolLayerModel::indexOfParentTreeNode( QgsSymbolLayerModelNode *parentNode ) const
302{
303 Q_ASSERT( parentNode );
304
305 QgsSymbolLayerModelNode *grandParentNode = parentNode->parent();
306 if ( !grandParentNode )
307 return QModelIndex(); // root node -> invalid index
308
309 int row = grandParentNode->indexOf( parentNode );
310 Q_ASSERT( row >= 0 );
311
312 return createIndex( row, 0, parentNode );
313};
314
316{
317 if ( !index.isValid() )
318 return mRootNode.get();
319
320 return reinterpret_cast<QgsSymbolLayerModelNode *>( index.internalPointer() );
321};
322
324{
325 if ( !node || !node->parent() )
326 return QModelIndex(); // this is the only root item -> invalid index
327
328 QModelIndex parentIndex = node2index( node->parent() );
329
330 int row = node->parent()->indexOf( node );
331 Q_ASSERT( row >= 0 );
332 return index( row, 0, parentIndex );
333};
334
335void QgsSymbolLayerModel::rebuild()
336{
337 beginResetModel();
338 mRootNode->deleteChildren();
339 loadSymbol( mSymbol, mRootNode.get() );
340 endResetModel();
341}
342
344{
345 const QModelIndex parentIndex = node2index( parent );
346 beginRemoveRows( parentIndex, 0, rowCount( parentIndex ) - 1 );
347 parent->deleteChildren();
348 endRemoveRows();
349
350 beginInsertRows( parentIndex, 0, symbol->symbolLayerCount() - 1 );
351
352 loadSymbol( symbol, parent );
353
354 endInsertRows();
355}
356
358{
359 if ( !node->isLayer() )
360 return;
361
363 QModelIndex parentIndex = node2index( parent );
364 const int row = node->rowIndex();
365
366 QgsSymbol *parentSymbol = parent->symbol();
367
368 const int layerIdx = parent->rowCount() - row - 1;
369 // switch layers
370 QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
371 parentSymbol->insertSymbolLayer( layerIdx - offset, tmpLayer );
372
373 beginMoveRows( parentIndex, row, row, parentIndex, row + offset + ( offset > 0 ? 1 : 0 ) );
374 parent->moveChildNode( node, row + offset );
375 endMoveRows();
376}
377
379{
380 if ( !node->isLayer() )
381 return nullptr;
382
383 QgsSymbolLayer *source = node->layer();
384 const int insertIdx = node->rowIndex();
386 QModelIndex parentIndex = node2index( parent );
387
388 QgsSymbol *parentSymbol = parent->symbol();
389
390 QgsSymbolLayer *newLayer = source->clone();
392 if ( insertIdx == -1 )
393 parentSymbol->appendSymbolLayer( newLayer );
394 else
395 parentSymbol->insertSymbolLayer( parent->rowCount() - insertIdx, newLayer );
396
397
398 beginInsertRows( parentIndex, insertIdx, insertIdx );
399
400 auto newNode = std::make_unique<QgsSymbolLayerModelNode>( newLayer, parentSymbol->type(), mVectorLayer, mScreen );
401 QgsSymbolLayerModelNode *newNodePtr;
402 if ( insertIdx == -1 )
403 newNodePtr = parent->addChildNode( std::move( newNode ) );
404 else
405 newNodePtr = parent->insertChildNode( insertIdx, std::move( newNode ) );
406
407 if ( newLayer->subSymbol() )
408 {
409 loadSymbol( newLayer->subSymbol(), newNodePtr );
410 }
411
412 endInsertRows();
413
414 return newNodePtr;
415}
416
418{
419 if ( !index.isValid() )
420 return nullptr;
421
422 int insertIdx = -1;
424 if ( node->isLayer() )
425 {
426 insertIdx = node->rowIndex();
427 node = node->parent();
428 }
429
430
431 QgsSymbol *parentSymbol = node->symbol();
432
433 // save data-defined values from symbol, to apply to the new symbol layer
434 const QgsProperty ddSize( parentSymbol->type() == Qgis::SymbolType::Marker ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedSize() : QgsProperty() );
435 const QgsProperty ddAngle( parentSymbol->type() == Qgis::SymbolType::Marker ? static_cast<QgsMarkerSymbol *>( parentSymbol )->dataDefinedAngle() : QgsProperty() );
436 const QgsProperty ddWidth( parentSymbol->type() == Qgis::SymbolType::Line ? static_cast<QgsLineSymbol *>( parentSymbol )->dataDefinedWidth() : QgsProperty() );
437
438 QgsSymbolLayer *newLayer = QgsSymbolLayerRegistry::defaultSymbolLayer( parentSymbol->type() ).release();
439 {
440 // Transfer the ownership to the parent symbol, which takes ownership of the layer
441 if ( insertIdx == -1 )
442 parentSymbol->appendSymbolLayer( newLayer );
443 else
444 parentSymbol->insertSymbolLayer( node->rowCount() - insertIdx, newLayer );
445 }
446
447 // restore data-defined values from the symbol
448 if ( ddSize )
449 static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedSize( ddSize );
450 if ( ddAngle )
451 static_cast<QgsMarkerSymbol *>( parentSymbol )->setDataDefinedAngle( ddAngle );
452 if ( ddWidth )
453 static_cast<QgsLineSymbol *>( parentSymbol )->setDataDefinedWidth( ddWidth );
454
455
456 const int atRowIndex = ( insertIdx == -1 ) ? 0 : insertIdx;
457 beginInsertRows( node2index( node ), atRowIndex, atRowIndex );
458
459 auto newNode = std::make_unique<QgsSymbolLayerModelNode>( newLayer, parentSymbol->type(), mVectorLayer, mScreen );
460
461 QgsSymbolLayerModelNode *newNodePtr = node->insertChildNode( atRowIndex, std::move( newNode ) );
462
463 endInsertRows();
464
465 return newNodePtr;
466}
467
469{
470 if ( !node->isLayer() )
471 return;
472
473 const int row = node->rowIndex();
475 const int layerIdx = parent->rowCount() - row - 1; // The index in the model and the symbol are inverted
476 QgsSymbol *parentSymbol = parent->symbol();
477
478 beginRemoveRows( node2index( parent ), row, row );
479 parent->removeChildNode( node );
480
481 QgsSymbolLayer *tmpLayer = parentSymbol->takeSymbolLayer( layerIdx );
482 //finally delete the removed layer pointer
483 delete tmpLayer;
484 endRemoveRows();
485}
486
488{
489 QgsSymbolLayerModelNode *parentNode = node->parent();
490 QgsSymbol *parentSymbol = parentNode->symbol();
491
492 const int layerIdx = parentNode->rowCount() - node->rowIndex() - 1;
493 parentSymbol->changeSymbolLayer( layerIdx, newLayer );
494
495 if ( node->rowCount() > 0 )
496 {
497 beginRemoveRows( node2index( node ), 0, node->rowCount() - 1 );
498 node->deleteChildren();
499 endRemoveRows();
500 }
501
502 node->setLayer( newLayer, parentSymbol->type() );
503 if ( newLayer->subSymbol() )
504 {
505 updateNode( newLayer->subSymbol(), node );
506 }
507
508 updatePreview( node );
509}
510
512{
513 const QModelIndex index = node2index( node );
514 emit dataChanged( index, index, QVector<int>() << Qt::DecorationRole );
515
516 // Recursively update the parent's preview up to the root node.
517 if ( QgsSymbolLayerModelNode *lParent = node->parent() )
518 updatePreview( lParent );
519}
520
521
523{
524 if ( symbol )
525 {
526 mSymbol = symbol;
527 }
528
529 rebuild();
530}
531
532void QgsSymbolLayerModel::setScreen( QScreen *screen )
533{
534 mScreen = screen;
535
536 // BFS traversal to set the screen for all nodes and update their preview icons
537 QList<QgsSymbolLayerModelNode * > stack;
538 stack.append( mRootNode.get() );
539
540 while ( !stack.isEmpty() )
541 {
542 QgsSymbolLayerModelNode *node = stack.takeLast();
543 node->setScreen( mScreen );
544 updatePreview( node );
545
546 for ( int i = 0; i < node->rowCount(); ++i )
547 {
548 stack.append( node->childAt( i ) );
549 }
550 }
551}
552
553void QgsSymbolLayerModel::loadSymbol( QgsSymbol *symbol, QgsSymbolLayerModelNode *parent )
554{
555 if ( !symbol )
556 return;
557
558 if ( !parent )
559 {
560 return;
561 }
562
563 auto symbolNode = std::make_unique<QgsSymbolLayerModelNode>( symbol, mVectorLayer, mScreen );
564 const int count = symbol->symbolLayerCount();
565 for ( int i = count - 1; i >= 0; i-- )
566 {
567 auto layerNode = std::make_unique<QgsSymbolLayerModelNode>( symbol->symbolLayer( i ), symbol->type(), mVectorLayer, mScreen );
568 if ( symbol->symbolLayer( i )->subSymbol() )
569 {
570 loadSymbol( symbol->symbolLayer( i )->subSymbol(), layerNode.get() );
571 }
572 symbolNode->addChildNode( std::move( layerNode ) );
573 }
574 parent->addChildNode( std::move( symbolNode ) );
575}
@ Millimeters
Millimeters.
Definition qgis.h:5656
SymbolType
Symbol types.
Definition qgis.h:651
@ Marker
Marker symbol.
Definition qgis.h:652
@ Line
Line symbol.
Definition qgis.h:653
@ Fill
Fill symbol.
Definition qgis.h:654
@ Hybrid
Hybrid symbol.
Definition qgis.h:655
static QgsSymbolLayerRegistry * symbolLayerRegistry()
Returns the application's symbol layer registry, used for managing symbol layers.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
A line symbol type, for rendering LineString and MultiLineString geometries.
QgsProperty dataDefinedWidth() const
Returns data defined width for whole symbol (including all symbol layers).
Struct for storing maximum and minimum scales for measurements in map units.
A marker symbol type, for rendering Point and MultiPoint geometries.
QgsProperty dataDefinedAngle() const
Returns data defined angle for whole symbol (including all symbol layers).
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
A store for object properties.
Stores properties relating to a screen.
Stores metadata about one symbol layer class.
A node contained within a QgsSymbolLayerModel.
QgsSymbolLayerModelNode * parent()
Returns the pointer of the parent node. If parent is nullptr, the node is a root node.
void setLayer(QgsSymbolLayer *layer, Qgis::SymbolType symbolType)
Sets the layer associated with the node, and the symbolType of the QgsSymbol it belongs to.
QgsSymbol * symbol()
Returns the symbol pointer; helpful in determining a layer's parent symbol.
QgsSymbolLayer * layer()
Returns the symbol layer pointer.
int rowCount() const
Returns the number of rows(children) belonging to the node.
QgsSymbolLayerModelNode * childAt(int index) const
Returns the child node at the given index.
void moveChildNode(QgsSymbolLayerModelNode *node, int to)
Move a child node of the current node, the ownership of the moved node is transferred to the current ...
int rowIndex() const
Returns the row index of the node within its parent.
QgsSymbolLayerModelNode * insertChildNode(int index, std::unique_ptr< QgsSymbolLayerModelNode > node)
Inserts a child node to this node at index, transferring ownership of the node.
void removeChildNode(QgsSymbolLayerModelNode *node)
Remove a child node of the current node.
QIcon icon() const
Returns the item preview icon.
void setScreen(QScreen *screen)
Sets the screen associated with the node, which is used to render the preview icons.
bool isLayer() const
Returns true if the node is a symbol layer. And otherwise, it is a symbol.
void deleteChildren()
Deletes all child nodes from this node.
QgsSymbolLayerModelNode()
Constructor for QgsSymbolLayerModelNode for an empty node.
int indexOf(const QgsSymbolLayerModelNode *node)
Returns the index position of the given node within the current node's children.
QVariant data(int role) const
Returns the item's data for the given role.
QgsSymbolLayerModelNode * addChildNode(std::unique_ptr< QgsSymbolLayerModelNode > node)
Adds a child node to this node, transferring ownership of the node.
int columnCount(const QModelIndex &=QModelIndex()) const override
void updateNode(QgsSymbol *symbol, QgsSymbolLayerModelNode *parent)
Updates the descendants of the given parent node with the given symbol.
QModelIndex parent(const QModelIndex &child) const override
QVariant data(const QModelIndex &index, int role) const override
QgsSymbolLayerModelNode * addLayer(QModelIndex index)
Adds the symbol layer node inserted right before the idx.
void removeLayer(QgsSymbolLayerModelNode *node)
Removes the symbol layer node from it's parent node hierarchy.
void updatePreview(QgsSymbolLayerModelNode *node)
Updates the preview icon of the given node.
void moveLayerByOffset(QgsSymbolLayerModelNode *node, int offset)
Moves the symbol layer node by the offset in the parent hierarchy.
QgsSymbolLayerModelNode * duplicateLayer(QgsSymbolLayerModelNode *node)
Duplicates the symbol layer node in the parent node hierarchy.
QModelIndex node2index(QgsSymbolLayerModelNode *node) const
Returns the model index corresponding to the given node.
QgsSymbolLayerModel(QgsVectorLayer *vl, QObject *parent=nullptr, QScreen *screen=nullptr)
Constructor for QgsSymbolLayerModel, with the specified parent object.
void setSymbol(QgsSymbol *symbol)
Sets the symbol associated with the model.
void setScreen(QScreen *screen)
Set the QScreen of the model itself.
void changeLayer(QgsSymbolLayerModelNode *node, QgsSymbolLayer *newLayer)
Change the symbol layer associated with node by a newLayer symbol layer.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QgsSymbolLayerModelNode * index2node(const QModelIndex &index) const
Returns the model node corresponding to the given index.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
static std::unique_ptr< QgsSymbolLayer > defaultSymbolLayer(Qgis::SymbolType type)
create a new instance of symbol layer for specified symbol type with default settings
QgsSymbolLayerAbstractMetadata * symbolLayerMetadata(const QString &name) const
Returns metadata for specified symbol layer. Returns nullptr if not found.
static QIcon symbolLayerPreviewIcon(const QgsSymbolLayer *layer, Qgis::RenderUnit u, QSize size, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::SymbolType parentSymbolType=Qgis::SymbolType::Hybrid, QgsMapLayer *mapLayer=nullptr, const QgsScreenProperties &screen=QgsScreenProperties())
Draws a symbol layer preview to an icon.
static QPixmap symbolPreviewPixmap(const QgsSymbol *symbol, QSize size, int padding=0, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *shape=nullptr, const QgsScreenProperties &screen=QgsScreenProperties())
Returns a pixmap preview for a color ramp.
static void resetSymbolLayerIds(QgsSymbol *symbol)
Regenerate recursively unique id from all symbol symbol layers.
Abstract base class for symbol layers.
virtual QgsSymbolLayer * clone() const =0
Shall be reimplemented by subclasses to create a deep copy of the instance.
virtual QgsSymbol * subSymbol()
Returns the symbol's sub symbol, if present.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:227
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
bool appendSymbolLayer(QgsSymbolLayer *layer)
Appends a symbol layer at the end of the current symbol layer list.
bool insertSymbolLayer(int index, QgsSymbolLayer *layer)
Inserts a symbol layer to specified index.
bool changeSymbolLayer(int index, QgsSymbolLayer *layer)
Deletes the current layer at the specified index and replaces it with layer.
QgsSymbolLayer * takeSymbolLayer(int index)
Removes a symbol layer from the list and returns a pointer to it.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition qgssymbol.h:357
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:296
Represents a vector layer which manages a vector based dataset.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
#define QgsDebugError(str)
Definition qgslogger.h:71