QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgssvgselectorwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssvgselectorwidget.cpp - group and preview selector for SVG files
3 built off of work in qgssymbollayerwidget
4
5 ---------------------
6 begin : April 2, 2013
7 copyright : (C) 2013 by Larry Shaffer
8 email : larrys at dakcarto dot com
9 ***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
18
19#include "qgsapplication.h"
21#include "qgsgui.h"
22#include "qgslogger.h"
23#include "qgspathresolver.h"
24#include "qgsproject.h"
25#include "qgssettings.h"
26#include "qgssvgcache.h"
27#include "qgssymbollayerutils.h"
29#include "qgsvectorlayer.h"
30
31#include <QAbstractListModel>
32#include <QCheckBox>
33#include <QDir>
34#include <QFileDialog>
35#include <QMenu>
36#include <QModelIndex>
37#include <QPixmapCache>
38#include <QSortFilterProxyModel>
39#include <QStyle>
40#include <QTime>
41
42#include "moc_qgssvgselectorwidget.cpp"
43
44// QgsSvgSelectorLoader
45
47QgsSvgSelectorLoader::QgsSvgSelectorLoader( QObject *parent )
48 : QThread( parent )
49{
50}
51
52QgsSvgSelectorLoader::~QgsSvgSelectorLoader()
53{
54 stop();
55}
56
57void QgsSvgSelectorLoader::run()
58{
59 mCanceled = false;
60 mQueuedSvgs.clear();
61 mTraversedPaths.clear();
62
63 // start with a small initial timeout (ms)
64 mTimerThreshold = 10;
65 mTimer.start();
66
67 loadPath( mPath );
68
69 if ( !mQueuedSvgs.isEmpty() )
70 {
71 // make sure we notify model of any remaining queued svgs (ie svgs added since last foundSvgs() signal was emitted)
72 emit foundSvgs( mQueuedSvgs );
73 }
74 mQueuedSvgs.clear();
75}
76
77void QgsSvgSelectorLoader::stop()
78{
79 mCanceled = true;
80 while ( isRunning() )
81 {
82 }
83}
84
85void QgsSvgSelectorLoader::loadPath( const QString &path )
86{
87 if ( mCanceled )
88 return;
89
90 QgsDebugMsgLevel( QStringLiteral( "loading path: %1" ).arg( path ), 2 );
91
92 if ( path.isEmpty() )
93 {
94 QStringList svgPaths = QgsApplication::svgPaths();
95 const auto constSvgPaths = svgPaths;
96 for ( const QString &svgPath : constSvgPaths )
97 {
98 if ( mCanceled )
99 return;
100
101 if ( !svgPath.isEmpty() )
102 {
103 loadPath( svgPath );
104 }
105 }
106 }
107 else
108 {
109 QDir dir( path );
110
111 //guard against circular symbolic links
112 QString canonicalPath = dir.canonicalPath();
113 if ( mTraversedPaths.contains( canonicalPath ) )
114 return;
115
116 mTraversedPaths.insert( canonicalPath );
117
118 loadImages( path );
119
120 const auto constEntryList = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot );
121 for ( const QString &item : constEntryList )
122 {
123 if ( mCanceled )
124 return;
125
126 QString newPath = dir.path() + '/' + item;
127 loadPath( newPath );
128 QgsDebugMsgLevel( QStringLiteral( "added path: %1" ).arg( newPath ), 2 );
129 }
130 }
131}
132
133void QgsSvgSelectorLoader::loadImages( const QString &path )
134{
135 QDir dir( path );
136 const auto constEntryList = dir.entryList( QStringList( "*.svg" ), QDir::Files );
137 for ( const QString &item : constEntryList )
138 {
139 if ( mCanceled )
140 return;
141
142 // TODO test if it is correct SVG
143 QString svgPath = dir.path() + '/' + item;
144 // QgsDebugMsgLevel( QStringLiteral( "adding svg: %1" ).arg( svgPath ), 2 );
145
146 // add it to the list of queued SVGs
147 mQueuedSvgs << svgPath;
148
149 // we need to avoid spamming the model with notifications about new svgs, so foundSvgs
150 // is only emitted for blocks of SVGs (otherwise the view goes all flickery)
151 if ( mTimer.elapsed() > mTimerThreshold && !mQueuedSvgs.isEmpty() )
152 {
153 emit foundSvgs( mQueuedSvgs );
154 mQueuedSvgs.clear();
155
156 // increase the timer threshold - this ensures that the first lots of svgs loaded are added
157 // to the view quickly, but as the list grows new svgs are added at a slower rate.
158 // ie, good for initial responsiveness but avoid being spammy as the list grows.
159 if ( mTimerThreshold < 1000 )
160 mTimerThreshold *= 2;
161 mTimer.restart();
162 }
163 }
164}
165
166
167//
168// QgsSvgGroupLoader
169//
170
171QgsSvgGroupLoader::QgsSvgGroupLoader( QObject *parent )
172 : QThread( parent )
173{
174}
175
176QgsSvgGroupLoader::~QgsSvgGroupLoader()
177{
178 stop();
179}
180
181void QgsSvgGroupLoader::run()
182{
183 mCanceled = false;
184 mTraversedPaths.clear();
185
186 while ( !mCanceled && !mParentPaths.isEmpty() )
187 {
188 QString parentPath = mParentPaths.takeFirst();
189 loadGroup( parentPath );
190 }
191}
192
193void QgsSvgGroupLoader::stop()
194{
195 mCanceled = true;
196 while ( isRunning() )
197 {
198 }
199}
200
201void QgsSvgGroupLoader::loadGroup( const QString &parentPath )
202{
203 QDir parentDir( parentPath );
204
205 //guard against circular symbolic links
206 QString canonicalPath = parentDir.canonicalPath();
207 if ( mTraversedPaths.contains( canonicalPath ) )
208 return;
209
210 mTraversedPaths.insert( canonicalPath );
211
212 const auto constEntryList = parentDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot );
213 for ( const QString &item : constEntryList )
214 {
215 if ( mCanceled )
216 return;
217
218 emit foundPath( parentPath, item );
219 mParentPaths.append( parentDir.path() + '/' + item );
220 }
221}
222
224
225
226QgsSvgSelectorFilterModel::QgsSvgSelectorFilterModel( QObject *parent, const QString &path, int iconSize )
227 : QSortFilterProxyModel( parent )
228{
229 mModel = new QgsSvgSelectorListModel( parent, path, iconSize );
230 setFilterCaseSensitivity( Qt::CaseInsensitive );
231 setSourceModel( mModel );
232 setFilterRole( Qt::UserRole );
233}
234
235//,
236// QgsSvgSelectorListModel
237//
238
240 : QgsSvgSelectorListModel( parent, QString(), iconSize )
241{}
242
243QgsSvgSelectorListModel::QgsSvgSelectorListModel( QObject *parent, const QString &path, int iconSize )
244 : QAbstractListModel( parent )
245 , mSvgLoader( new QgsSvgSelectorLoader( this ) )
246 , mIconSize( iconSize )
247{
248 mSvgLoader->setPath( path );
249 connect( mSvgLoader, &QgsSvgSelectorLoader::foundSvgs, this, &QgsSvgSelectorListModel::addSvgs );
250 mSvgLoader->start();
251}
252
253int QgsSvgSelectorListModel::rowCount( const QModelIndex &parent ) const
254{
255 Q_UNUSED( parent )
256 return mSvgFiles.count();
257}
258
259QPixmap QgsSvgSelectorListModel::createPreview( const QString &entry ) const
260{
261 // render SVG file
262 QColor fill, stroke;
263 double strokeWidth, fillOpacity, strokeOpacity;
264 bool fillParam, fillOpacityParam, strokeParam, strokeWidthParam, strokeOpacityParam;
265 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false,
266 hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
267 QgsApplication::svgCache()->containsParams( entry, fillParam, hasDefaultFillColor, fill, fillOpacityParam, hasDefaultFillOpacity, fillOpacity, strokeParam, hasDefaultStrokeColor, stroke, strokeWidthParam, hasDefaultStrokeWidth, strokeWidth, strokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
268
269 //if defaults not set in symbol, use these values
270 if ( !hasDefaultFillColor )
271 fill = QColor( 200, 200, 200 );
272 fill.setAlphaF( hasDefaultFillOpacity ? fillOpacity : 1.0 );
273 if ( !hasDefaultStrokeColor )
274 stroke = Qt::black;
275 stroke.setAlphaF( hasDefaultStrokeOpacity ? strokeOpacity : 1.0 );
276 if ( !hasDefaultStrokeWidth )
277 strokeWidth = 0.2;
278
279 bool fitsInCache; // should always fit in cache at these sizes (i.e. under 559 px ^ 2, or half cache size)
280 QImage img = QgsApplication::svgCache()->svgAsImage( entry, mIconSize, fill, stroke, strokeWidth, 3.5 /*appr. 88 dpi*/, fitsInCache );
281 return QPixmap::fromImage( img );
282}
283
284QVariant QgsSvgSelectorListModel::data( const QModelIndex &index, int role ) const
285{
286 QString entry = mSvgFiles.at( index.row() );
287
288 if ( role == Qt::DecorationRole ) // icon
289 {
290 QPixmap pixmap;
291 if ( !QPixmapCache::find( entry, &pixmap ) )
292 {
293 QPixmap newPixmap = createPreview( entry );
294 QPixmapCache::insert( entry, newPixmap );
295 return newPixmap;
296 }
297 else
298 {
299 return pixmap;
300 }
301 }
302 else if ( role == Qt::UserRole || role == Qt::ToolTipRole )
303 {
304 return entry;
305 }
306
307 return QVariant();
308}
309
310void QgsSvgSelectorListModel::addSvgs( const QStringList &svgs )
311{
312 beginInsertRows( QModelIndex(), mSvgFiles.count(), mSvgFiles.count() + svgs.size() - 1 );
313 mSvgFiles.append( svgs );
314 endInsertRows();
315}
316
317
318//--- QgsSvgSelectorGroupsModel
319
321 : QStandardItemModel( parent )
322 , mLoader( new QgsSvgGroupLoader( this ) )
323{
324 QStringList svgPaths = QgsApplication::svgPaths();
325 QStandardItem *parentItem = invisibleRootItem();
326 QStringList parentPaths;
327 parentPaths.reserve( svgPaths.size() );
328
329 for ( int i = 0; i < svgPaths.size(); i++ )
330 {
331 QDir dir( svgPaths.at( i ) );
332 QStandardItem *baseGroup = nullptr;
333
334 if ( dir.path().contains( QgsApplication::pkgDataPath() ) )
335 {
336 baseGroup = new QStandardItem( tr( "App Symbols" ) );
337 }
338 else if ( dir.path().contains( QgsApplication::qgisSettingsDirPath() ) )
339 {
340 baseGroup = new QStandardItem( tr( "User Symbols" ) );
341 }
342 else
343 {
344 baseGroup = new QStandardItem( dir.dirName() );
345 }
346 baseGroup->setData( QVariant( svgPaths.at( i ) ) );
347 baseGroup->setEditable( false );
348 baseGroup->setCheckable( false );
349 baseGroup->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconFolder.svg" ) ) );
350 baseGroup->setToolTip( dir.path() );
351 parentItem->appendRow( baseGroup );
352 parentPaths << svgPaths.at( i );
353 mPathItemHash.insert( svgPaths.at( i ), baseGroup );
354 QgsDebugMsgLevel( QStringLiteral( "SVG base path %1: %2" ).arg( i ).arg( baseGroup->data().toString() ), 2 );
355 }
356 mLoader->setParentPaths( parentPaths );
357 connect( mLoader, &QgsSvgGroupLoader::foundPath, this, &QgsSvgSelectorGroupsModel::addPath );
358 mLoader->start();
359}
360
365
366void QgsSvgSelectorGroupsModel::addPath( const QString &parentPath, const QString &item )
367{
368 QStandardItem *parentGroup = mPathItemHash.value( parentPath );
369 if ( !parentGroup )
370 return;
371
372 QString fullPath = parentPath + '/' + item;
373 QStandardItem *group = new QStandardItem( item );
374 group->setData( QVariant( fullPath ) );
375 group->setEditable( false );
376 group->setCheckable( false );
377 group->setToolTip( fullPath );
378 group->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconFolder.svg" ) ) );
379 parentGroup->appendRow( group );
380 mPathItemHash.insert( fullPath, group );
381}
382
383
384//-- QgsSvgSelectorWidget
385
387 : QWidget( parent )
388{
389 // TODO: in-code gui setup with option to vertically or horizontally stack SVG groups/images widgets
390 setupUi( this );
391
392 mIconSize = std::max( 30, static_cast<int>( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 3 ) ) );
393 mImagesListView->setGridSize( QSize( mIconSize * 1.2, mIconSize * 1.2 ) );
394 mImagesListView->setUniformItemSizes( false );
395
396 mGroupsTreeView->setHeaderHidden( true );
397 populateList();
398
399 connect( mSvgFilterLineEdit, &QgsFilterLineEdit::textChanged, this, [this]( const QString &filterText ) {
400 if ( !mImagesListView->selectionModel()->selectedIndexes().isEmpty() )
401 {
402 disconnect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSvgSelectorWidget::svgSelectionChanged );
403 mImagesListView->selectionModel()->clearSelection();
404 connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSvgSelectorWidget::svgSelectionChanged );
405 }
406 qobject_cast<QgsSvgSelectorFilterModel *>( mImagesListView->model() )->setFilterFixedString( filterText );
407 } );
408
409
410 mParametersModel = new QgsSvgParametersModel( this );
411 mParametersTreeView->setModel( mParametersModel );
412 mParametersGroupBox->setVisible( mAllowParameters );
413
414 mParametersTreeView->setItemDelegateForColumn( static_cast<int>( QgsSvgParametersModel::Column::ExpressionColumn ), new QgsSvgParameterValueDelegate( this ) );
415 mParametersTreeView->header()->setSectionResizeMode( QHeaderView::ResizeToContents );
416 mParametersTreeView->header()->setStretchLastSection( true );
417 mParametersTreeView->setSelectionBehavior( QAbstractItemView::SelectRows );
418 mParametersTreeView->setSelectionMode( QAbstractItemView::MultiSelection );
419 mParametersTreeView->setEditTriggers( QAbstractItemView::DoubleClicked );
420
421 connect( mParametersModel, &QgsSvgParametersModel::parametersChanged, this, &QgsSvgSelectorWidget::svgParametersChanged );
422 connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSvgSelectorWidget::svgSelectionChanged );
423 connect( mGroupsTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSvgSelectorWidget::populateIcons );
424 connect( mAddParameterButton, &QToolButton::clicked, mParametersModel, &QgsSvgParametersModel::addParameter );
425 connect( mRemoveParameterButton, &QToolButton::clicked, this, [this]() {
426 const QModelIndexList selectedRows = mParametersTreeView->selectionModel()->selectedRows();
427 if ( selectedRows.count() > 0 )
428 mParametersModel->removeParameters( selectedRows );
429 } );
430
431 connect( mSourceLineEdit, &QgsPictureSourceLineEditBase::sourceChanged, this, &QgsSvgSelectorWidget::updateCurrentSvgPath );
432}
433
435{
436 mParametersModel->setExpressionContextGenerator( generator );
437 mParametersModel->setLayer( layer );
438}
439
440void QgsSvgSelectorWidget::setSvgPath( const QString &svgPath )
441{
442 mCurrentSvgPath = svgPath;
443
444 whileBlocking( mSourceLineEdit )->setSource( svgPath );
445
446 mImagesListView->selectionModel()->blockSignals( true );
447 QAbstractItemModel *m = mImagesListView->model();
448 QItemSelectionModel *selModel = mImagesListView->selectionModel();
449 for ( int i = 0; i < m->rowCount(); i++ )
450 {
451 QModelIndex idx( m->index( i, 0 ) );
452 if ( m->data( idx ).toString() == svgPath )
453 {
454 selModel->select( idx, QItemSelectionModel::SelectCurrent );
455 selModel->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent );
456 mImagesListView->scrollTo( idx );
457 break;
458 }
459 }
460 mImagesListView->selectionModel()->blockSignals( false );
461}
462
463void QgsSvgSelectorWidget::setSvgParameters( const QMap<QString, QgsProperty> &parameters )
464{
465 mParametersModel->setParameters( parameters );
466}
467
469{
470 return mCurrentSvgPath;
471}
472
474{
475 if ( mAllowParameters == allow )
476 return;
477
478 mAllowParameters = allow;
479 mParametersGroupBox->setVisible( allow );
480}
481
483{
484 if ( mBrowserVisible == visible )
485 return;
486
487 mBrowserVisible = visible;
488 mSvgBrowserGroupBox->setVisible( visible );
489}
490
492{
493 return mSourceLineEdit->propertyOverrideToolButton();
494}
495
496void QgsSvgSelectorWidget::updateCurrentSvgPath( const QString &svgPath )
497{
498 mCurrentSvgPath = svgPath;
499 emit svgSelected( currentSvgPath() );
500}
501
502void QgsSvgSelectorWidget::svgSelectionChanged( const QModelIndex &idx )
503{
504 QString filePath = idx.data( Qt::UserRole ).toString();
505 whileBlocking( mSourceLineEdit )->setSource( filePath );
506 updateCurrentSvgPath( filePath );
507}
508
509void QgsSvgSelectorWidget::populateIcons( const QModelIndex &idx )
510{
511 QString path = idx.data( Qt::UserRole + 1 ).toString();
512
513 QAbstractItemModel *oldModel = mImagesListView->model();
514 QgsSvgSelectorFilterModel *m = new QgsSvgSelectorFilterModel( mImagesListView, path, mIconSize );
515 mImagesListView->setModel( m );
516 connect( mSvgFilterLineEdit, &QgsFilterLineEdit::textChanged, m, &QSortFilterProxyModel::setFilterFixedString );
517 delete oldModel; //explicitly delete old model to force any background threads to stop
518
519 connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSvgSelectorWidget::svgSelectionChanged );
520}
521
522void QgsSvgSelectorWidget::svgSourceChanged( const QString &text )
523{
524 QString resolvedPath = QgsSymbolLayerUtils::svgSymbolNameToPath( text, QgsProject::instance()->pathResolver() );
525 bool validSVG = !resolvedPath.isNull();
526
527 updateCurrentSvgPath( validSVG ? resolvedPath : text );
528}
529
531{
532 QgsSvgSelectorGroupsModel *g = new QgsSvgSelectorGroupsModel( mGroupsTreeView );
533 mGroupsTreeView->setModel( g );
534 // Set the tree expanded at the first level
535 int rows = g->rowCount( g->indexFromItem( g->invisibleRootItem() ) );
536 for ( int i = 0; i < rows; i++ )
537 {
538 mGroupsTreeView->setExpanded( g->indexFromItem( g->item( i ) ), true );
539 }
540
541 // Initially load the icons in the List view without any grouping
542 QAbstractItemModel *oldModel = mImagesListView->model();
543 QgsSvgSelectorFilterModel *m = new QgsSvgSelectorFilterModel( mImagesListView );
544 mImagesListView->setModel( m );
545 delete oldModel; //explicitly delete old model to force any background threads to stop
546}
547
548//-- QgsSvgSelectorDialog
549
550QgsSvgSelectorDialog::QgsSvgSelectorDialog( QWidget *parent, Qt::WindowFlags fl, QDialogButtonBox::StandardButtons buttons, Qt::Orientation orientation )
551 : QDialog( parent, fl )
552{
553 // TODO: pass 'orientation' to QgsSvgSelectorWidget for customizing its layout, once implemented
554 Q_UNUSED( orientation )
555
556 // create buttonbox
557 mButtonBox = new QDialogButtonBox( buttons, orientation, this );
558 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
559 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
560
561 setMinimumSize( 480, 320 );
562
563 // dialog's layout
564 mLayout = new QVBoxLayout();
566 mLayout->addWidget( mSvgSelector );
567
568 mLayout->addWidget( mButtonBox );
569 setLayout( mLayout );
570}
571
572
574
575
576QgsSvgParametersModel::QgsSvgParametersModel( QObject *parent )
577 : QAbstractTableModel( parent )
578{
579 connect( this, &QAbstractTableModel::rowsInserted, this, [this]() { emit parametersChanged( parameters() ); } );
580 connect( this, &QAbstractTableModel::rowsRemoved, this, [this]() { emit parametersChanged( parameters() ); } );
581 connect( this, &QAbstractTableModel::dataChanged, this, [this]() { emit parametersChanged( parameters() ); } );
582}
583
584void QgsSvgParametersModel::setParameters( const QMap<QString, QgsProperty> &parameters )
585{
586 beginResetModel();
587 mParameters.clear();
588 QMap<QString, QgsProperty>::const_iterator paramIt = parameters.constBegin();
589 for ( ; paramIt != parameters.constEnd(); ++paramIt )
590 {
591 mParameters << Parameter( paramIt.key(), paramIt.value() );
592 }
593 endResetModel();
594}
595
596QMap<QString, QgsProperty> QgsSvgParametersModel::parameters() const
597{
598 QMap<QString, QgsProperty> params;
599 for ( const Parameter &param : std::as_const( mParameters ) )
600 {
601 if ( !param.name.isEmpty() )
602 params.insert( param.name, param.property );
603 }
604 return params;
605}
606
607void QgsSvgParametersModel::removeParameters( const QModelIndexList &indexList )
608{
609 if ( indexList.isEmpty() )
610 return;
611
612 auto mm = std::minmax_element( indexList.constBegin(), indexList.constEnd(), []( const QModelIndex &i1, const QModelIndex &i2 ) { return i1.row() < i2.row(); } );
613
614 beginRemoveRows( QModelIndex(), ( *mm.first ).row(), ( *mm.second ).row() );
615 for ( const QModelIndex &index : indexList )
616 mParameters.removeAt( index.row() );
617 endRemoveRows();
618}
619
620void QgsSvgParametersModel::setLayer( QgsVectorLayer *layer )
621{
622 mLayer = layer;
623}
624
625void QgsSvgParametersModel::setExpressionContextGenerator( const QgsExpressionContextGenerator *generator )
626{
627 mExpressionContextGenerator = generator;
628}
629
630int QgsSvgParametersModel::rowCount( const QModelIndex &parent ) const
631{
632 Q_UNUSED( parent )
633 return mParameters.count();
634}
635
636int QgsSvgParametersModel::columnCount( const QModelIndex &parent ) const
637{
638 Q_UNUSED( parent )
639 return 2;
640}
641
642QVariant QgsSvgParametersModel::data( const QModelIndex &index, int role ) const
643{
644 QgsSvgParametersModel::Column col = static_cast<QgsSvgParametersModel::Column>( index.column() );
645 if ( role == Qt::DisplayRole )
646 {
647 switch ( col )
648 {
649 case QgsSvgParametersModel::Column::NameColumn:
650 return mParameters.at( index.row() ).name;
651 case QgsSvgParametersModel::Column::ExpressionColumn:
652 return mParameters.at( index.row() ).property.expressionString();
653 }
654 }
655
656 return QVariant();
657}
658
659bool QgsSvgParametersModel::setData( const QModelIndex &index, const QVariant &value, int role )
660{
661 if ( !index.isValid() || role != Qt::EditRole )
662 return false;
663
664 QgsSvgParametersModel::Column col = static_cast<QgsSvgParametersModel::Column>( index.column() );
665 switch ( col )
666 {
667 case QgsSvgParametersModel::Column::NameColumn:
668 {
669 QString oldName = mParameters.at( index.row() ).name;
670 QString newName = value.toString();
671 for ( const Parameter &param : std::as_const( mParameters ) )
672 {
673 if ( param.name == newName && param.name != oldName )
674 {
675 // names must be unique!
676 return false;
677 }
678 }
679 mParameters[index.row()].name = newName;
680 emit dataChanged( index, index );
681 return true;
682 }
683
684 case QgsSvgParametersModel::Column::ExpressionColumn:
685 mParameters[index.row()].property = QgsProperty::fromExpression( value.toString() );
686 emit dataChanged( index, index );
687 return true;
688 }
689
690 return false;
691}
692
693QVariant QgsSvgParametersModel::headerData( int section, Qt::Orientation orientation, int role ) const
694{
695 if ( role == Qt::DisplayRole && orientation == Qt::Horizontal )
696 {
697 QgsSvgParametersModel::Column col = static_cast<QgsSvgParametersModel::Column>( section );
698 switch ( col )
699 {
700 case QgsSvgParametersModel::Column::NameColumn:
701 return tr( "Name" );
702 case QgsSvgParametersModel::Column::ExpressionColumn:
703 return tr( "Expression" );
704 }
705 }
706
707 return QVariant();
708}
709
710void QgsSvgParametersModel::addParameter()
711{
712 int c = rowCount( QModelIndex() );
713 beginInsertRows( QModelIndex(), c, c );
714 int i = 1;
715 QStringList currentNames;
716 std::transform( mParameters.begin(), mParameters.end(), std::back_inserter( currentNames ), []( const Parameter &parameter ) { return parameter.name; } );
717 while ( currentNames.contains( QStringLiteral( "param%1" ).arg( i ) ) )
718 i++;
719 mParameters.append( Parameter( QStringLiteral( "param%1" ).arg( i ), QgsProperty() ) );
720 endResetModel();
721}
722
723
724Qt::ItemFlags QgsSvgParametersModel::flags( const QModelIndex &index ) const
725{
726 Q_UNUSED( index )
727 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
728}
729
730
731QWidget *QgsSvgParameterValueDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
732{
733 Q_UNUSED( option )
735 const QgsSvgParametersModel *model = qobject_cast<const QgsSvgParametersModel *>( index.model() );
736 w->registerExpressionContextGenerator( model->expressionContextGenerator() );
737 w->setLayer( model->layer() );
738 return w;
739}
740
741void QgsSvgParameterValueDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
742{
743 QgsFieldExpressionWidget *w = qobject_cast<QgsFieldExpressionWidget *>( editor );
744 if ( !w )
745 return;
746
747 w->setExpression( index.model()->data( index ).toString() );
748}
749
750void QgsSvgParameterValueDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
751{
752 QgsFieldExpressionWidget *w = qobject_cast<QgsFieldExpressionWidget *>( editor );
753 if ( !w )
754 return;
755 model->setData( index, w->currentField() );
756}
757
758void QgsSvgParameterValueDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const
759{
760 Q_UNUSED( index )
761 editor->setGeometry( option.rect );
762}
763
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:6222
void sourceChanged(const QString &source)
Emitted whenever the file source is changed in the widget.
static QString pkgDataPath()
Returns the common root path of all application data directories.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QStringList svgPaths()
Returns the paths to svg directories.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user's home dir.
Abstract interface for generating an expression context.
A widget for selection of layer fields or expression creation.
void setExpression(const QString &expression)
Sets the current expression text and if applicable also the field.
void setLayer(QgsMapLayer *layer)
Sets the layer used to display the fields and expression.
void registerExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
QString currentField(bool *isExpression=nullptr, bool *isValid=nullptr) const
currentField returns the currently selected field or expression if allowed
static QgsProject * instance()
Returns the QgsProject singleton instance.
A button for controlling property overrides which may apply to a widget.
A store for object properties.
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth, bool blocking=false) const
Tests if an SVG file contains parameters for fill, stroke color, stroke width.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QImage.
QgsSvgSelectorDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags, QDialogButtonBox::StandardButtons buttons=QDialogButtonBox::Close|QDialogButtonBox::Ok, Qt::Orientation orientation=Qt::Horizontal)
Constructor for QgsSvgSelectorDialog.
QDialogButtonBox * mButtonBox
QgsSvgSelectorWidget * mSvgSelector
A model for displaying SVG files with a preview icon which can be filtered by file name.
QgsSvgSelectorFilterModel(QObject *parent, const QString &path=QString(), int iconSize=30)
Constructor for creating a model for SVG files in a specific path.
A model for displaying SVG search paths.
A model for displaying SVG files with a preview icon.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QgsSvgSelectorListModel(QObject *parent, int iconSize=30)
Constructor for QgsSvgSelectorListModel.
A widget allowing selection of an SVG file, and configuration of SVG related parameters.
void setAllowParameters(bool allow)
Defines if the group box to fill parameters is visible.
void initParametersModel(const QgsExpressionContextGenerator *generator, QgsVectorLayer *layer=nullptr)
Initialize the parameters model so the context and the layer are referenced.
void svgParametersChanged(const QMap< QString, QgsProperty > &parameters)
Emitted when the parameters have changed.
void setBrowserVisible(bool visible)
Defines if the SVG browser should be visible.
void setSvgPath(const QString &svgPath)
Accepts absolute paths.
void setSvgParameters(const QMap< QString, QgsProperty > &parameters)
Sets the dynamic parameters.
void svgSelected(const QString &path)
Emitted when an SVG is selected in the widget.
QgsSvgSelectorWidget(QWidget *parent=nullptr)
Constructor for QgsSvgSelectorWidget.
QgsPropertyOverrideButton * propertyOverrideToolButton() const
Returns the property override tool button of the file line edit.
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol's path from its name.
Represents a vector layer which manages a vector based dataset.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:6511
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61