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