QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
qgsstylemodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsstylemodel.cpp
3 ---------------
4 begin : September 2018
5 copyright : (C) 2018 by Nyall Dawson
6 email : nyall dot dawson 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 "qgsstylemodel.h"
17#include "qgsstyle.h"
18#include "qgssymbollayerutils.h"
19#include "qgsapplication.h"
20#include "qgssvgcache.h"
21#include "qgsimagecache.h"
24
25#include <QIcon>
26#include <QBuffer>
27#include <QDir>
28
29const double ICON_PADDING_FACTOR = 0.16;
30
32
33QgsAbstractStyleEntityIconGenerator *QgsStyleModel::sIconGenerator = nullptr;
34
35//
36// QgsAbstractStyleEntityIconGenerator
37//
38
40 : QObject( parent )
41{
42}
43
45{
46 mIconSizes = sizes;
47}
48
50{
51 return mIconSizes;
52}
53
54void QgsAbstractStyleEntityIconGenerator::setTargetScreenProperties( const QSet<QgsScreenProperties> &properties )
55{
56 mTargetScreenProperties = properties;
57}
58
60{
61 return mTargetScreenProperties;
62}
63
64
65//
66// QgsStyleModel
67//
68
69QgsStyleModel::QgsStyleModel( QgsStyle *style, QObject *parent )
70 : QAbstractItemModel( parent )
71 , mStyle( style )
72{
73 Q_ASSERT( mStyle );
74
75 for ( QgsStyle::StyleEntity entity : ENTITIES )
76 {
77 mEntityNames.insert( entity, mStyle->allNames( entity ) );
78 }
79
80 // ensure we always generate icons using default screen properties
81 // in addition to actual target screen properties (ie device pixel ratio of 1, 96 dpi)
82 mTargetScreenProperties.insert( QgsScreenProperties() );
83
84 connect( mStyle, &QgsStyle::entityAdded, this, &QgsStyleModel::onEntityAdded );
85 connect( mStyle, &QgsStyle::entityRemoved, this, &QgsStyleModel::onEntityRemoved );
86 connect( mStyle, &QgsStyle::entityRenamed, this, &QgsStyleModel::onEntityRename );
87 connect( mStyle, &QgsStyle::entityChanged, this, &QgsStyleModel::onEntityChanged );
88 connect( mStyle, &QgsStyle::favoritedChanged, this, &QgsStyleModel::onFavoriteChanged );
89 connect( mStyle, &QgsStyle::entityTagsChanged, this, &QgsStyleModel::onTagsChanged );
90 connect( mStyle, &QgsStyle::rebuildIconPreviews, this, &QgsStyleModel::rebuildSymbolIcons );
91
92 // when a remote svg or image has been fetched, update the model's decorations.
93 // this is required if a symbol utilizes remote svgs, and the current icons
94 // have been generated using the temporary "downloading" svg. In this case
95 // we require the preview to be regenerated to use the correct fetched
96 // svg
97 connect( QgsApplication::svgCache(), &QgsSvgCache::remoteSvgFetched, this, &QgsStyleModel::rebuildSymbolIcons );
98 connect( QgsApplication::imageCache(), &QgsImageCache::remoteImageFetched, this, &QgsStyleModel::rebuildSymbolIcons );
99
100 if ( sIconGenerator )
101 connect( sIconGenerator, &QgsAbstractStyleEntityIconGenerator::iconGenerated, this, &QgsStyleModel::iconGenerated, Qt::QueuedConnection );
102}
103
104QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const
105{
106 if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) )
107 return QVariant();
108
109
110 QgsStyle::StyleEntity entityType = entityTypeFromRow( index.row() );
111
112 QString name;
113 switch ( entityType )
114 {
117 break;
118
119 default:
120 name = mEntityNames[ entityType ].value( index.row() - offsetForEntity( entityType ) );
121 break;
122 }
123
124 switch ( role )
125 {
126 case Qt::DisplayRole:
127 case Qt::ToolTipRole:
128 case Qt::EditRole:
129 {
130 switch ( index.column() )
131 {
132 case Name:
133 {
134 const QStringList tags = mStyle->tagsOfSymbol( entityType, name );
135
136 if ( role == Qt::ToolTipRole )
137 {
138 QString tooltip = QStringLiteral( "<h3>%1</h3><p><i>%2</i>" ).arg( name,
139 tags.count() > 0 ? tags.join( QLatin1String( ", " ) ) : tr( "Not tagged" ) );
140
141 // generate tooltips for the largest device pixel ratio for all attached screens
142 QgsScreenProperties maxDevicePixelRatioScreen;
143 for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
144 {
145 if ( !maxDevicePixelRatioScreen.isValid() || it->devicePixelRatio() > maxDevicePixelRatioScreen.devicePixelRatio() )
146 maxDevicePixelRatioScreen = *it;
147 }
148
149 switch ( entityType )
150 {
152 {
153 // create very large preview image
154 std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( name ) );
155 if ( symbol )
156 {
157 int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
158 int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
159 QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( width, height ), height / 20, nullptr, false, mExpressionContext.get(), nullptr, maxDevicePixelRatioScreen );
160 QByteArray data;
161 QBuffer buffer( &data );
162 pm.save( &buffer, "PNG", 100 );
163 tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3' width=\"%4\">" ).arg( QString( data.toBase64() ) ).arg( width );
164 }
165 break;
166 }
167
169 {
170 int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
171 int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
172 const QgsTextFormat format = mStyle->textFormat( name );
173 QPixmap pm = QgsTextFormat::textFormatPreviewPixmap( format, QSize( width, height ), QString(), height / 20, maxDevicePixelRatioScreen );
174 QByteArray data;
175 QBuffer buffer( &data );
176 pm.save( &buffer, "PNG", 100 );
177 tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3' width=\"%4\">" ).arg( QString( data.toBase64() ) ).arg( width );
178 break;
179 }
180
182 {
183 int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
184 int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
185 const QgsPalLayerSettings settings = mStyle->labelSettings( name );
186 QPixmap pm = QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( width, height ), QString(), height / 20, maxDevicePixelRatioScreen );
187 QByteArray data;
188 QBuffer buffer( &data );
189 pm.save( &buffer, "PNG", 100 );
190 tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3' width=\"%4\">" ).arg( QString( data.toBase64() ) ).arg( width );
191 break;
192 }
193
195 {
196 int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
197 int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
198
199 const QgsLegendPatchShape shape = mStyle->legendPatchShape( name );
200 if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) )
201 {
202 QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( width, height ), height / 20, nullptr, false, nullptr, &shape, maxDevicePixelRatioScreen );
203 QByteArray data;
204 QBuffer buffer( &data );
205 pm.save( &buffer, "PNG", 100 );
206 tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3' width=\"%4\">" ).arg( QString( data.toBase64() ) ).arg( width );
207 }
208 break;
209 }
210
215 break;
216 }
217 return tooltip;
218 }
219 else
220 {
221 return name;
222 }
223 }
224 case Tags:
225 return mStyle->tagsOfSymbol( entityType, name ).join( QLatin1String( ", " ) );
226 }
227 return QVariant();
228 }
229
230 case Qt::DecorationRole:
231 {
232 // Generate icons at all additional sizes specified for the model.
233 // This allows the model to have size responsive icons.
234
235 if ( !mExpressionContext )
236 {
237 // build the expression context once, and keep it around. Usually this is a no-no, but in this
238 // case we want to avoid creating potentially thousands of contexts one-by-one (usually one context
239 // is created for a batch of multiple evalutions like this), and we only use a very minimal context
240 // anyway...
241 mExpressionContext = std::make_unique< QgsExpressionContext >();
242 mExpressionContext->appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
243 }
244
245 switch ( index.column() )
246 {
247 case Name:
248 switch ( entityType )
249 {
251 {
252 // use cached icon if possible
253 QIcon icon = mIconCache[ entityType ].value( name );
254 if ( !icon.isNull() )
255 return icon;
256
257 std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( name ) );
258 if ( symbol )
259 {
260 for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
261 {
262 if ( mAdditionalSizes.isEmpty() )
263 icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get(), nullptr, *it ) );
264
265 for ( const QSize &s : mAdditionalSizes )
266 {
267 icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get(), nullptr, *it ) );
268 }
269 }
270
271 }
272 mIconCache[ entityType ].insert( name, icon );
273 return icon;
274 }
276 {
277 // use cached icon if possible
278 QIcon icon = mIconCache[ entityType ].value( name );
279 if ( !icon.isNull() )
280 return icon;
281
282 std::unique_ptr< QgsColorRamp > ramp( mStyle->colorRamp( name ) );
283 if ( ramp )
284 {
285 if ( mAdditionalSizes.isEmpty() )
286 icon.addPixmap( QgsSymbolLayerUtils::colorRampPreviewPixmap( ramp.get(), QSize( 24, 24 ), 1 ) );
287 for ( const QSize &s : mAdditionalSizes )
288 {
289 icon.addPixmap( QgsSymbolLayerUtils::colorRampPreviewPixmap( ramp.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ) ) );
290 }
291
292 }
293 mIconCache[ entityType ].insert( name, icon );
294 return icon;
295 }
296
298 {
299 // use cached icon if possible
300 QIcon icon = mIconCache[ entityType ].value( name );
301 if ( !icon.isNull() )
302 return icon;
303
304 const QgsTextFormat format( mStyle->textFormat( name ) );
305 for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
306 {
307 if ( mAdditionalSizes.isEmpty() )
308 icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, QSize( 24, 24 ), QString(), 1, *it ) );
309 for ( const QSize &s : mAdditionalSizes )
310 {
311 icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ), *it ) );
312 }
313 }
314 mIconCache[ entityType ].insert( name, icon );
315 return icon;
316 }
317
319 {
320 // use cached icon if possible
321 QIcon icon = mIconCache[ entityType ].value( name );
322 if ( !icon.isNull() )
323 return icon;
324
325 const QgsPalLayerSettings settings( mStyle->labelSettings( name ) );
326 for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
327 {
328 if ( mAdditionalSizes.isEmpty() )
329 icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( 24, 24 ), QString(), 1, *it ) );
330 for ( const QSize &s : mAdditionalSizes )
331 {
332 icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ), *it ) );
333 }
334 }
335 mIconCache[ entityType ].insert( name, icon );
336 return icon;
337 }
338
340 {
341 // use cached icon if possible
342 QIcon icon = mIconCache[ entityType ].value( name );
343 if ( !icon.isNull() )
344 return icon;
345
346 const QgsLegendPatchShape shape = mStyle->legendPatchShape( name );
347 if ( !shape.isNull() )
348 {
349 for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
350 {
351 if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) )
352 {
353 if ( mAdditionalSizes.isEmpty() )
354 icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get(), &shape, *it ) );
355
356 for ( const QSize &s : mAdditionalSizes )
357 {
358 icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get(), &shape, *it ) );
359 }
360 }
361 }
362 }
363 mIconCache[ entityType ].insert( name, icon );
364 return icon;
365 }
366
368 {
369 // hack for now -- we just use a generic "3d icon" svg file.
370 // TODO - render proper thumbnails
371
372 // use cached icon if possible
373 QIcon icon = mIconCache[ entityType ].value( name );
374 if ( !icon.isNull() )
375 return icon;
376
377 if ( sIconGenerator && !mPending3dSymbolIcons.contains( name ) )
378 {
379 mPending3dSymbolIcons.insert( name );
380 sIconGenerator->generateIcon( mStyle, QgsStyle::Symbol3DEntity, name );
381 }
382
383 // TODO - use hourglass icon
384 if ( mAdditionalSizes.isEmpty() )
385 icon.addFile( QgsApplication::defaultThemePath() + QDir::separator() + QStringLiteral( "3d.svg" ), QSize( 24, 24 ) );
386 for ( const QSize &s : mAdditionalSizes )
387 {
388 icon.addFile( QgsApplication::defaultThemePath() + QDir::separator() + QStringLiteral( "3d.svg" ), s );
389 }
390 mIconCache[ entityType ].insert( name, icon );
391 return icon;
392 }
393
396 return QVariant();
397 }
398 break;
399
400 case Tags:
401 return QVariant();
402 }
403 return QVariant();
404 }
405
406 case TypeRole:
407 return entityType;
408
409 case TagRole:
410 return mStyle->tagsOfSymbol( entityType, name );
411
412 case IsFavoriteRole:
413 return mStyle->isFavorite( entityType, name );
414
415 case SymbolTypeRole:
416 {
417 switch ( entityType )
418 {
420 {
421 const QgsSymbol *symbol = mStyle->symbolRef( name );
422 return symbol ? static_cast< int >( symbol->type() ) : QVariant();
423 }
424
426 return static_cast< int >( mStyle->legendPatchShapeSymbolType( name ) );
427
434 return QVariant();
435 }
436 return QVariant();
437 }
438
439 case LayerTypeRole:
440 {
441 switch ( entityType )
442 {
444 return static_cast< int >( mStyle->labelSettingsLayerType( name ) );
445
453 return QVariant();
454 }
455 return QVariant();
456 }
457
459 {
460 switch ( entityType )
461 {
463 {
464 QVariantList res;
465 const QList< Qgis::GeometryType > types = mStyle->symbol3DCompatibleGeometryTypes( name );
466 res.reserve( types.size() );
467 for ( Qgis::GeometryType type : types )
468 {
469 res << static_cast< int >( type );
470 }
471 return res;
472 }
473
481 return QVariant();
482 }
483 return QVariant();
484 }
485
486 case EntityName:
487 return name;
488
489 case StyleName:
490 return mStyle->name();
491
492 case StyleFileName:
493 return mStyle->fileName();
494
495 default:
496 return QVariant();
497 }
498#ifndef _MSC_VER // avoid warning
499 return QVariant(); // avoid warning
500#endif
501}
502
503bool QgsStyleModel::setData( const QModelIndex &index, const QVariant &value, int role )
504{
505 if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) || role != Qt::EditRole )
506 return false;
507
508 switch ( index.column() )
509 {
510 case Name:
511 {
512 QgsStyle::StyleEntity entityType = entityTypeFromRow( index.row() );
513 QString name;
514 switch ( entityType )
515 {
518 return false;
519
520 default:
521 name = mEntityNames[ entityType ].value( index.row() - offsetForEntity( entityType ) );
522 break;
523 }
524
525 const QString newName = value.toString();
526 return mStyle->renameEntity( entityType, name, newName );
527 }
528
529 case Tags:
530 return false;
531 }
532
533 return false;
534}
535
536Qt::ItemFlags QgsStyleModel::flags( const QModelIndex &index ) const
537{
538 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
539 if ( index.isValid() && index.column() == Name )
540 {
541 return flags | Qt::ItemIsEditable;
542 }
543 else
544 {
545 return flags;
546 }
547}
548
549QVariant QgsStyleModel::headerData( int section, Qt::Orientation orientation, int role ) const
550{
551 return headerDataStatic( section, orientation, role );
552}
553
554QVariant QgsStyleModel::headerDataStatic( int section, Qt::Orientation orientation, int role )
555{
556 if ( role == Qt::DisplayRole )
557 {
558 if ( orientation == Qt::Vertical ) //row
559 {
560 return QVariant( section );
561 }
562 else
563 {
564 switch ( section )
565 {
566 case Name:
567 return QVariant( tr( "Name" ) );
568
569 case Tags:
570 return QVariant( tr( "Tags" ) );
571
572 default:
573 return QVariant();
574 }
575 }
576 }
577 else
578 {
579 return QVariant();
580 }
581}
582
583QModelIndex QgsStyleModel::index( int row, int column, const QModelIndex &parent ) const
584{
585 if ( !hasIndex( row, column, parent ) )
586 return QModelIndex();
587
588 if ( !parent.isValid() )
589 {
590 return createIndex( row, column );
591 }
592
593 return QModelIndex();
594}
595
596QModelIndex QgsStyleModel::parent( const QModelIndex & ) const
597{
598 //all items are top level for now
599 return QModelIndex();
600}
601
602int QgsStyleModel::rowCount( const QModelIndex &parent ) const
603{
604 if ( !parent.isValid() )
605 {
606 int count = 0;
607 for ( QgsStyle::StyleEntity type : ENTITIES )
608 count += mEntityNames[ type ].size();
609 return count;
610 }
611 return 0;
612}
613
614int QgsStyleModel::columnCount( const QModelIndex & ) const
615{
616 return 2;
617}
618
620{
621 if ( mAdditionalSizes.contains( size ) )
622 return;
623
624 mAdditionalSizes << size;
625
626 if ( sIconGenerator )
627 sIconGenerator->setIconSizes( mAdditionalSizes );
628
629 mIconCache.clear();
630}
631
633{
634 if ( mTargetScreenProperties.contains( properties ) )
635 return;
636
637 mTargetScreenProperties.insert( properties );
638
639 if ( sIconGenerator )
640 sIconGenerator->setTargetScreenProperties( mTargetScreenProperties );
641
642 mIconCache.clear();
643}
644
646{
647 sIconGenerator = generator;
648 connect( sIconGenerator, &QgsAbstractStyleEntityIconGenerator::iconGenerated, QgsApplication::defaultStyleModel(), &QgsStyleModel::iconGenerated, Qt::QueuedConnection );
649}
650
651void QgsStyleModel::onEntityAdded( QgsStyle::StyleEntity type, const QString &name )
652{
653 mIconCache[ type ].remove( name );
654 const QStringList newSymbolNames = mStyle->allNames( type );
655
656 // find index of newly added symbol
657 const int newNameIndex = newSymbolNames.indexOf( name );
658 if ( newNameIndex < 0 )
659 return; // shouldn't happen
660
661 const int offset = offsetForEntity( type );
662 beginInsertRows( QModelIndex(), newNameIndex + offset, newNameIndex + offset );
663 mEntityNames[ type ] = newSymbolNames;
664 endInsertRows();
665}
666
667void QgsStyleModel::onEntityRemoved( QgsStyle::StyleEntity type, const QString &name )
668{
669 mIconCache[ type ].remove( name );
670 const QStringList oldSymbolNames = mEntityNames[ type ];
671 const QStringList newSymbolNames = mStyle->allNames( type );
672
673 // find index of removed symbol
674 const int oldNameIndex = oldSymbolNames.indexOf( name );
675 if ( oldNameIndex < 0 )
676 return; // shouldn't happen
677
678 const int offset = offsetForEntity( type );
679 beginRemoveRows( QModelIndex(), oldNameIndex + offset, oldNameIndex + offset );
680 mEntityNames[ type ] = newSymbolNames;
681 endRemoveRows();
682}
683
684void QgsStyleModel::onEntityChanged( QgsStyle::StyleEntity type, const QString &name )
685{
686 mIconCache[ type ].remove( name );
687
688 const int offset = offsetForEntity( type );
689 QModelIndex i = index( offset + mEntityNames[ type ].indexOf( name ), Tags );
690 emit dataChanged( i, i, QVector< int >() << Qt::DecorationRole );
691}
692
693void QgsStyleModel::onFavoriteChanged( QgsStyle::StyleEntity type, const QString &name, bool )
694{
695 const int offset = offsetForEntity( type );
696 QModelIndex i = index( offset + mEntityNames[ type ].indexOf( name ), Name );
697 emit dataChanged( i, i, QVector< int >() << Role::IsFavoriteRole );
698}
699
700void QgsStyleModel::onEntityRename( QgsStyle::StyleEntity type, const QString &oldName, const QString &newName )
701{
702 mIconCache[ type ].remove( oldName );
703 const QStringList oldSymbolNames = mEntityNames[ type ];
704 const QStringList newSymbolNames = mStyle->allNames( type );
705
706 // find index of removed symbol
707 const int oldNameIndex = oldSymbolNames.indexOf( oldName );
708 if ( oldNameIndex < 0 )
709 return; // shouldn't happen
710
711 // find index of added symbol
712 const int newNameIndex = newSymbolNames.indexOf( newName );
713 if ( newNameIndex < 0 )
714 return; // shouldn't happen
715
716 if ( newNameIndex == oldNameIndex )
717 {
718 mEntityNames[ type ] = newSymbolNames;
719 return;
720 }
721
722 const int offset = offsetForEntity( type );
723 beginMoveRows( QModelIndex(), oldNameIndex + offset, oldNameIndex + offset, QModelIndex(), ( newNameIndex > oldNameIndex ? newNameIndex + 1 : newNameIndex ) + offset );
724 mEntityNames[ type ] = newSymbolNames;
725 endMoveRows();
726}
727
728void QgsStyleModel::onTagsChanged( int entity, const QString &name, const QStringList & )
729{
730 QgsStyle::StyleEntity type = static_cast< QgsStyle::StyleEntity >( entity );
731 int row = mEntityNames[type].indexOf( name ) + offsetForEntity( type );
732 switch ( static_cast< QgsStyle::StyleEntity >( entity ) )
733 {
736 return;
737
738 default:
739 break;
740 }
741 emit dataChanged( index( row, Name ), index( row, Tags ) );
742}
743
744void QgsStyleModel::rebuildSymbolIcons()
745{
746 mIconCache[ QgsStyle::SymbolEntity ].clear();
747 mExpressionContext.reset();
748 emit dataChanged( index( 0, 0 ), index( mEntityNames[ QgsStyle::SymbolEntity ].count() - 1, 0 ), QVector<int>() << Qt::DecorationRole );
749}
750
751void QgsStyleModel::iconGenerated( QgsStyle::StyleEntity type, const QString &name, const QIcon &icon )
752{
753 int row = mEntityNames[type].indexOf( name ) + offsetForEntity( type );
754
755 switch ( type )
756 {
758 mPending3dSymbolIcons.remove( name );
759 mIconCache[ QgsStyle::Symbol3DEntity ].insert( name, icon );
760 emit dataChanged( index( row, 0 ), index( row, 0 ) );
761 break;
762
770 break;
771 }
772}
773
774QgsStyle::StyleEntity QgsStyleModel::entityTypeFromRow( int row ) const
775{
776 int maxRowForEntity = 0;
777 for ( QgsStyle::StyleEntity type : ENTITIES )
778 {
779 maxRowForEntity += mEntityNames[ type ].size();
780 if ( row < maxRowForEntity )
781 return type;
782 }
783
784 // should never happen
785 Q_ASSERT( false );
787}
788
789int QgsStyleModel::offsetForEntity( QgsStyle::StyleEntity entity ) const
790{
791 int offset = 0;
792 for ( QgsStyle::StyleEntity type : ENTITIES )
793 {
794 if ( type == entity )
795 return offset;
796
797 offset += mEntityNames[ type ].size();
798 }
799 return 0;
800}
801
802//
803// QgsStyleProxyModel
804//
805
807 : QSortFilterProxyModel( parent )
808 , mStyle( style )
809{
810 mModel = new QgsStyleModel( mStyle, this );
811 setSourceModel( mModel );
812 initialize();
813}
814
815void QgsStyleProxyModel::initialize()
816{
817 setSortCaseSensitivity( Qt::CaseInsensitive );
818// setSortLocaleAware( true );
819 setDynamicSortFilter( true );
820 sort( 0 );
821
822 if ( mStyle )
823 {
824 connect( mStyle, &QgsStyle::entityTagsChanged, this, [ = ]
825 {
826 // update tagged symbols if filtering by tag
827 if ( mTagId >= 0 )
828 setTagId( mTagId );
829 if ( mSmartGroupId >= 0 )
830 setSmartGroupId( mSmartGroupId );
831 } );
832
833 connect( mStyle, &QgsStyle::entityRenamed, this, [ = ]( QgsStyle::StyleEntity entity, const QString &, const QString & )
834 {
835 switch ( entity )
836 {
839 return;
840
841 default:
842 break;
843 }
844
845 if ( mSmartGroupId >= 0 )
846 setSmartGroupId( mSmartGroupId );
847 } );
848 }
849}
850
852 : QSortFilterProxyModel( parent )
853 , mModel( model )
854 , mStyle( model->style() )
855{
856 setSourceModel( mModel );
857 initialize();
858}
859
861 : QSortFilterProxyModel( parent )
862 , mCombinedModel( model )
863{
864 setSourceModel( mCombinedModel );
865 initialize();
866}
867
868bool QgsStyleProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
869{
870 if ( mFilterString.isEmpty() && !mEntityFilterEnabled && !mSymbolTypeFilterEnabled && mTagId < 0 && mSmartGroupId < 0 && !mFavoritesOnly && mTagFilter.isEmpty() )
871 return true;
872
873 const QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
874
875 if ( sourceModel()->data( index, QgsStyleModel::IsTitleRole ).toBool() )
876 return true;
877
878 const QString name = sourceModel()->data( index ).toString();
879 const QStringList tags = sourceModel()->data( index, QgsStyleModel::TagRole ).toStringList();
880
881 QgsStyle::StyleEntity styleEntityType = static_cast< QgsStyle::StyleEntity >( sourceModel()->data( index, QgsStyleModel::TypeRole ).toInt() );
882 if ( mEntityFilterEnabled && ( mEntityFilters.empty() || !mEntityFilters.contains( styleEntityType ) ) )
883 return false;
884
885 Qgis::SymbolType symbolType = static_cast< Qgis::SymbolType >( sourceModel()->data( index, QgsStyleModel::SymbolTypeRole ).toInt() );
886 if ( mSymbolTypeFilterEnabled && symbolType != mSymbolType )
887 return false;
888
889 if ( mLayerType != Qgis::GeometryType::Unknown )
890 {
891 switch ( styleEntityType )
892 {
899 break;
900
902 {
903 if ( mLayerType != static_cast< Qgis::GeometryType >( sourceModel()->data( index, QgsStyleModel::LayerTypeRole ).toInt() ) )
904 return false;
905 break;
906 }
907
909 {
910 const QVariantList types = sourceModel()->data( index, QgsStyleModel::CompatibleGeometryTypesRole ).toList();
911 if ( !types.empty() && !types.contains( QVariant::fromValue( mLayerType ) ) )
912 return false;
913 break;
914 }
915 }
916 }
917
918 if ( mTagId >= 0 && !mTaggedSymbolNames.contains( name ) )
919 return false;
920
921 if ( mSmartGroupId >= 0 && !mSmartGroupSymbolNames.contains( name ) )
922 return false;
923
924 if ( !mTagFilter.isEmpty() && !tags.contains( mTagFilter, Qt::CaseInsensitive ) )
925 return false;
926
927 if ( mFavoritesOnly && !sourceModel()->data( index, QgsStyleModel::IsFavoriteRole ).toBool() )
928 return false;
929
930 if ( !mFilterString.isEmpty() )
931 {
932 // filter by word, in both filter string and style entity name/tags
933 // this allows matching of a filter string "hash line" to the symbol "hashed red lines"
934 const QStringList partsToMatch = mFilterString.trimmed().split( ' ' );
935
936 QStringList partsToSearch = name.split( ' ' );
937 for ( const QString &tag : tags )
938 {
939 partsToSearch.append( tag.split( ' ' ) );
940 }
941
942 for ( const QString &part : partsToMatch )
943 {
944 bool found = false;
945 for ( const QString &partToSearch : std::as_const( partsToSearch ) )
946 {
947 if ( partToSearch.contains( part, Qt::CaseInsensitive ) )
948 {
949 found = true;
950 break;
951 }
952 }
953 if ( !found )
954 return false; // couldn't find a match for this word, so hide entity
955 }
956 }
957
958 return true;
959}
960
961bool QgsStyleProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
962{
963 const QString leftSource = sourceModel()->data( left, QgsStyleModel::StyleFileName ).toString();
964 const QString rightSource = sourceModel()->data( right, QgsStyleModel::StyleFileName ).toString();
965 if ( leftSource != rightSource )
966 return QString::localeAwareCompare( leftSource, rightSource ) < 0;
967
968 const QString leftName = sourceModel()->data( left, QgsStyleModel::EntityName ).toString();
969 const QString rightName = sourceModel()->data( right, QgsStyleModel::EntityName ).toString();
970 return QString::localeAwareCompare( leftName, rightName ) < 0;
971}
972
973void QgsStyleProxyModel::setFilterString( const QString &filter )
974{
975 mFilterString = filter;
976 invalidateFilter();
977}
978
979
981{
982 return mFavoritesOnly;
983}
984
985void QgsStyleProxyModel::setFavoritesOnly( bool favoritesOnly )
986{
987 mFavoritesOnly = favoritesOnly;
988 invalidateFilter();
989}
990
992{
993 if ( mModel )
994 mModel->addDesiredIconSize( size );
995 if ( mCombinedModel )
996 mCombinedModel->addDesiredIconSize( size );
997}
998
1000{
1001 if ( mModel )
1002 mModel->addTargetScreenProperties( properties );
1003 if ( mCombinedModel )
1004 mCombinedModel->addTargetScreenProperties( properties );
1005}
1006
1008{
1009 return mSymbolTypeFilterEnabled;
1010}
1011
1013{
1014 mSymbolTypeFilterEnabled = enabled;
1015 invalidateFilter();
1016}
1017
1019{
1020 return mLayerType;
1021}
1022
1024{
1025 mLayerType = type;
1026 invalidateFilter();
1027}
1028
1030{
1031 if ( !mStyle )
1032 return;
1033 mTagId = id;
1034
1035 mTaggedSymbolNames.clear();
1036 if ( mTagId >= 0 )
1037 {
1038 for ( QgsStyle::StyleEntity entity : ENTITIES )
1039 mTaggedSymbolNames.append( mStyle->symbolsWithTag( entity, mTagId ) );
1040 }
1041
1042 invalidateFilter();
1043}
1044
1046{
1047 return mTagId;
1048}
1049
1050void QgsStyleProxyModel::setTagString( const QString &tag )
1051{
1052 mTagFilter = tag;
1053
1054 invalidateFilter();
1055}
1056
1058{
1059 return mTagFilter;
1060}
1061
1063{
1064 if ( !mStyle )
1065 return;
1066
1067 mSmartGroupId = id;
1068
1069 mSmartGroupSymbolNames.clear();
1070 if ( mSmartGroupId >= 0 )
1071 {
1072 for ( QgsStyle::StyleEntity entity : ENTITIES )
1073 mSmartGroupSymbolNames.append( mStyle->symbolsOfSmartgroup( entity, mSmartGroupId ) );
1074 }
1075
1076 invalidateFilter();
1077}
1078
1080{
1081 return mSmartGroupId;
1082}
1083
1085{
1086 return mSymbolType;
1087}
1088
1090{
1091 mSymbolType = symbolType;
1092 invalidateFilter();
1093}
1094
1096{
1097 return mEntityFilterEnabled;
1098}
1099
1100void QgsStyleProxyModel::setEntityFilterEnabled( bool entityFilterEnabled )
1101{
1102 mEntityFilterEnabled = entityFilterEnabled;
1103 invalidateFilter();
1104}
1105
1107{
1108 return mEntityFilters.empty() ? QgsStyle::SymbolEntity : mEntityFilters.at( 0 );
1109}
1110
1112{
1113 mEntityFilters = QList< QgsStyle::StyleEntity >() << entityFilter;
1114 invalidateFilter();
1115}
1116
1117void QgsStyleProxyModel::setEntityFilters( const QList<QgsStyle::StyleEntity> &filters )
1118{
1119 mEntityFilters = filters;
1120 invalidateFilter();
1121}
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:255
@ Unknown
Unknown types.
SymbolType
Attribute editing capabilities which may be supported by vector data providers.
Definition qgis.h:368
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:4086
An abstract base class for icon generators for a QgsStyleModel.
void setTargetScreenProperties(const QSet< QgsScreenProperties > &properties)
Sets the target screen properties to use when generating icons.
void setIconSizes(const QList< QSize > &sizes)
Sets the list of icon sizes to generate.
virtual void generateIcon(QgsStyle *style, QgsStyle::StyleEntity type, const QString &name)=0
Triggers generation of an icon for an entity from the specified style database, with matching entity ...
QgsAbstractStyleEntityIconGenerator(QObject *parent)
Constructor for QgsAbstractStyleEntityIconGenerator, with the specified parent object.
QSet< QgsScreenProperties > targetScreenProperties() const
Returns the target screen properties to use when generating icons.
QList< QSize > iconSizes() const
Returns the list of icon sizes to generate.
void iconGenerated(QgsStyle::StyleEntity type, const QString &name, const QIcon &icon)
Emitted when the icon for the style entity with matching type and name has been generated.
static QString defaultThemePath()
Returns the path to the default theme directory.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsStyleModel * defaultStyleModel()
Returns a shared QgsStyleModel containing the default style library (see QgsStyle::defaultStyle()).
A model which contains entities from multiple QgsStyle databases.
void addTargetScreenProperties(const QgsScreenProperties &properties)
Adds additional target screen properties to use when generating icons for Qt::DecorationRole data.
void addDesiredIconSize(QSize size)
Adds an additional icon size to generate for Qt::DecorationRole data.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
void remoteImageFetched(const QString &url)
Emitted when the cache has finished retrieving an image file from a remote url.
Represents a patch shape for use in map legends.
bool isNull() const
Returns true if the patch shape is a null QgsLegendPatchShape, which indicates that the default legen...
Contains settings for how a map layer will be labeled.
static QPixmap labelSettingsPreviewPixmap(const QgsPalLayerSettings &settings, QSize size, const QString &previewText=QString(), int padding=0, const QgsScreenProperties &screen=QgsScreenProperties())
Returns a pixmap preview for label settings.
Stores properties relating to a screen.
double devicePixelRatio() const
Returns the ratio between physical pixels and device-independent pixels for the screen.
bool isValid() const
Returns true if the properties are valid.
A QAbstractItemModel subclass for showing symbol and color ramp entities contained within a QgsStyle ...
Qt::ItemFlags flags(const QModelIndex &index) const override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role) const override
static void setIconGenerator(QgsAbstractStyleEntityIconGenerator *generator)
Sets the icon generator to use for deferred style entity icon generation.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QModelIndex parent(const QModelIndex &index) const override
@ StyleName
Name of associated QgsStyle (QgsStyle::name()) (since QGIS 3.26)
@ LayerTypeRole
Layer type (for label settings entities)
@ StyleFileName
File name of associated QgsStyle (QgsStyle::fileName()) (since QGIS 3.26)
@ EntityName
Entity name (since QGIS 3.26)
@ TypeRole
Style entity type, see QgsStyle::StyleEntity.
@ CompatibleGeometryTypesRole
Compatible layer geometry types (for 3D symbols)
@ IsFavoriteRole
Whether entity is flagged as a favorite.
@ SymbolTypeRole
Symbol type (for symbol or legend patch shape entities)
@ IsTitleRole
True if the index corresponds to a title item (since QGIS 3.26)
@ TagRole
String list of tags.
void addTargetScreenProperties(const QgsScreenProperties &properties)
Adds additional target screen properties to use when generating icons for Qt::DecorationRole data.
QgsStyleModel(QgsStyle *style, QObject *parent=nullptr)
Constructor for QgsStyleModel, for the specified style and parent object.
@ Name
Name column.
@ Tags
Tags column.
void addDesiredIconSize(QSize size)
Adds an additional icon size to generate for Qt::DecorationRole data.
void setEntityFilter(QgsStyle::StyleEntity filter)
Sets the style entity type filter.
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
bool favoritesOnly() const
Returns true if the model is showing only favorited entities.
void setSymbolTypeFilterEnabled(bool enabled)
Sets whether filtering by symbol type is enabled.
void setTagId(int id)
Sets a tag id to filter style entities by.
QString tagString() const
Returns the tag string used to filter style entities by.
Qgis::SymbolType symbolType() const
Returns the symbol type filter.
void setTagString(const QString &tag)
Sets a tag to filter style entities by.
QgsStyle::StyleEntity entityFilter() const
Returns the style entity type filter.
QgsStyleProxyModel(QgsStyle *style, QObject *parent=nullptr)
Constructor for QgsStyleProxyModel, for the specified style and parent object.
int tagId() const
Returns the tag id used to filter style entities by.
void setEntityFilters(const QList< QgsStyle::StyleEntity > &filters)
Sets the style entity type filters.
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
void setFavoritesOnly(bool favoritesOnly)
Sets whether the model should show only favorited entities.
void setSymbolType(Qgis::SymbolType type)
Sets the symbol type filter.
bool entityFilterEnabled() const
Returns true if filtering by entity type is enabled.
Qgis::GeometryType layerType() const
Returns the layer type filter, or Qgis::GeometryType::Unknown if no layer type filter is present.
void addDesiredIconSize(QSize size)
Adds an additional icon size to generate for Qt::DecorationRole data.
void addTargetScreenProperties(const QgsScreenProperties &properties)
Adds additional target screen properties to use when generating icons for Qt::DecorationRole data.
void setEntityFilterEnabled(bool enabled)
Sets whether filtering by entity type is enabled.
void setSmartGroupId(int id)
Sets a smart group id to filter style entities by.
bool symbolTypeFilterEnabled() const
Returns true if filtering by symbol type is enabled.
int smartGroupId() const
Returns the smart group id used to filter style entities by.
void setFilterString(const QString &filter)
Sets a filter string, such that only symbol entities with names matching the specified string will be...
void setLayerType(Qgis::GeometryType type)
Sets the layer type filter.
QgsTextFormat textFormat(const QString &name) const
Returns the text format with the specified name.
QStringList allNames(StyleEntity type) const
Returns a list of the names of all existing entities of the specified type.
void entityChanged(QgsStyle::StyleEntity entity, const QString &name)
Emitted whenever an entity's definition is changed.
void rebuildIconPreviews()
Emitted whenever icon previews for entities in the style must be rebuilt.
QStringList symbolsWithTag(StyleEntity type, int tagid) const
Returns the symbol names with which have the given tag.
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
Definition qgsstyle.cpp:291
QStringList symbolsOfSmartgroup(StyleEntity type, int id)
Returns the symbols for the smartgroup.
StyleEntity
Enum for Entities involved in a style.
Definition qgsstyle.h:179
@ LabelSettingsEntity
Label settings.
Definition qgsstyle.h:185
@ TextFormatEntity
Text formats.
Definition qgsstyle.h:184
@ SmartgroupEntity
Smart groups.
Definition qgsstyle.h:183
@ Symbol3DEntity
3D symbol entity (since QGIS 3.14)
Definition qgsstyle.h:187
@ SymbolEntity
Symbols.
Definition qgsstyle.h:180
@ TagEntity
Tags.
Definition qgsstyle.h:181
@ ColorrampEntity
Color ramps.
Definition qgsstyle.h:182
@ LegendPatchShapeEntity
Legend patch shape (since QGIS 3.14)
Definition qgsstyle.h:186
QStringList tagsOfSymbol(StyleEntity type, const QString &symbol)
Returns the tags associated with the symbol.
QList< Qgis::GeometryType > symbol3DCompatibleGeometryTypes(const QString &name) const
Returns the list of the vector layer geometry types which are compatible with the 3D symbol with the ...
void entityRenamed(QgsStyle::StyleEntity entity, const QString &oldName, const QString &newName)
Emitted whenever a entity of the specified type has been renamed from oldName to newName.
const QgsSymbol * symbolRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance)
Definition qgsstyle.cpp:303
QString fileName() const
Returns the current file name of the style database.
Definition qgsstyle.h:887
QgsColorRamp * colorRamp(const QString &name) const
Returns a new copy of the specified color ramp.
Definition qgsstyle.cpp:468
bool renameEntity(StyleEntity type, const QString &oldName, const QString &newName)
Renames an entity of the specified type from oldName to newName.
Definition qgsstyle.cpp:262
bool isFavorite(StyleEntity type, const QString &name)
Returns true if the symbol with matching type and name is marked as a favorite.
QString name() const
Returns the name of the style.
Definition qgsstyle.cpp:106
Qgis::GeometryType labelSettingsLayerType(const QString &name) const
Returns the layer geometry type corresponding to the label settings with the specified name,...
void entityTagsChanged(QgsStyle::StyleEntity entity, const QString &name, const QStringList &newTags)
Emitted whenever an entity's tags are changed.
void favoritedChanged(QgsStyle::StyleEntity entity, const QString &name, bool isFavorite)
Emitted whenever an entity is either favorited or un-favorited.
QgsPalLayerSettings labelSettings(const QString &name) const
Returns the label settings with the specified name.
void entityRemoved(QgsStyle::StyleEntity entity, const QString &name)
Emitted whenever an entity of the specified type is removed from the style and the database has been ...
Qgis::SymbolType legendPatchShapeSymbolType(const QString &name) const
Returns the symbol type corresponding to the legend patch shape with the specified name,...
const QgsSymbol * previewSymbolForPatchShape(const QgsLegendPatchShape &shape) const
Returns a symbol to use for rendering preview icons for a patch shape.
QgsLegendPatchShape legendPatchShape(const QString &name) const
Returns the legend patch shape with the specified name.
void entityAdded(QgsStyle::StyleEntity entity, const QString &name)
Emitted every time a new entity has been added to the database.
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
static QPixmap colorRampPreviewPixmap(QgsColorRamp *ramp, QSize size, int padding=0, Qt::Orientation direction=Qt::Horizontal, bool flipDirection=false, bool drawTransparentBackground=true)
Returns a pixmap preview for a color ramp.
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.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:94
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:153
Container for all settings relating to text rendering.
static QPixmap textFormatPreviewPixmap(const QgsTextFormat &format, QSize size, const QString &previewText=QString(), int padding=0, const QgsScreenProperties &screen=QgsScreenProperties())
Returns a pixmap preview for a text format.
const double ICON_PADDING_FACTOR
const auto ENTITIES