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