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