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