28 #include <QAbstractListModel>    31 #include <QFileDialog>    32 #include <QModelIndex>    33 #include <QPixmapCache>    41 QgsSvgSelectorLoader::QgsSvgSelectorLoader( QObject *parent )
    46 QgsSvgSelectorLoader::~QgsSvgSelectorLoader()
    51 void QgsSvgSelectorLoader::run()
    55   mTraversedPaths.clear();
    63   if ( !mQueuedSvgs.isEmpty() )
    66     emit foundSvgs( mQueuedSvgs );
    71 void QgsSvgSelectorLoader::stop()
    74   while ( isRunning() ) {}
    77 void QgsSvgSelectorLoader::loadPath( 
const QString &path )
    87     const auto constSvgPaths = svgPaths;
    88     for ( 
const QString &svgPath : constSvgPaths )
    93       if ( !svgPath.isEmpty() )
   104     QString canonicalPath = dir.canonicalPath();
   105     if ( mTraversedPaths.contains( canonicalPath ) )
   108     mTraversedPaths.insert( canonicalPath );
   112     const auto constEntryList = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot );
   113     for ( 
const QString &item : constEntryList )
   118       QString newPath = dir.path() + 
'/' + item;
   125 void QgsSvgSelectorLoader::loadImages( 
const QString &path )
   128   const auto constEntryList = dir.entryList( QStringList( 
"*.svg" ), QDir::Files );
   129   for ( 
const QString &item : constEntryList )
   135     QString svgPath = dir.path() + 
'/' + item;
   139     mQueuedSvgs << svgPath;
   143     if ( mTimer.elapsed() > mTimerThreshold && !mQueuedSvgs.isEmpty() )
   145       emit foundSvgs( mQueuedSvgs );
   151       if ( mTimerThreshold < 1000 )
   152         mTimerThreshold *= 2;
   163 QgsSvgGroupLoader::QgsSvgGroupLoader( QObject *parent )
   169 QgsSvgGroupLoader::~QgsSvgGroupLoader()
   174 void QgsSvgGroupLoader::run()
   177   mTraversedPaths.clear();
   179   while ( !mCanceled && !mParentPaths.isEmpty() )
   181     QString parentPath = mParentPaths.takeFirst();
   182     loadGroup( parentPath );
   186 void QgsSvgGroupLoader::stop()
   189   while ( isRunning() ) {}
   192 void QgsSvgGroupLoader::loadGroup( 
const QString &parentPath )
   194   QDir parentDir( parentPath );
   197   QString canonicalPath = parentDir.canonicalPath();
   198   if ( mTraversedPaths.contains( canonicalPath ) )
   201   mTraversedPaths.insert( canonicalPath );
   203   const auto constEntryList = parentDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot );
   204   for ( 
const QString &item : constEntryList )
   209     emit foundPath( parentPath, item );
   210     mParentPaths.append( parentDir.path() + 
'/' + item );
   221   : QAbstractListModel( parent )
   222   , mSvgLoader( new QgsSvgSelectorLoader( this ) )
   223   , mIconSize( iconSize )
   225   mSvgLoader->setPath( QString() );
   226   connect( mSvgLoader, &QgsSvgSelectorLoader::foundSvgs, 
this, &QgsSvgSelectorListModel::addSvgs );
   231   : QAbstractListModel( parent )
   232   , mSvgLoader( new QgsSvgSelectorLoader( this ) )
   233   , mIconSize( iconSize )
   235   mSvgLoader->setPath( path );
   236   connect( mSvgLoader, &QgsSvgSelectorLoader::foundSvgs, 
this, &QgsSvgSelectorListModel::addSvgs );
   246 QPixmap QgsSvgSelectorListModel::createPreview( 
const QString &entry )
 const   250   double strokeWidth, fillOpacity, strokeOpacity;
   251   bool fillParam, fillOpacityParam, strokeParam, strokeWidthParam, strokeOpacityParam;
   252   bool hasDefaultFillColor = 
false, hasDefaultFillOpacity = 
false, hasDefaultStrokeColor = 
false,
   253        hasDefaultStrokeWidth = 
false, hasDefaultStrokeOpacity = 
false;
   255       fillOpacityParam, hasDefaultFillOpacity, fillOpacity,
   256       strokeParam, hasDefaultStrokeColor, stroke,
   257       strokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
   258       strokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
   261   if ( !hasDefaultFillColor )
   262     fill = QColor( 200, 200, 200 );
   263   fill.setAlphaF( hasDefaultFillOpacity ? fillOpacity : 1.0 );
   264   if ( !hasDefaultStrokeColor )
   266   stroke.setAlphaF( hasDefaultStrokeOpacity ? strokeOpacity : 1.0 );
   267   if ( !hasDefaultStrokeWidth )
   272   return QPixmap::fromImage( img );
   277   QString entry = 
mSvgFiles.at( index.row() );
   279   if ( role == Qt::DecorationRole ) 
   281     QPixmap *pixmap = 
nullptr;
   282     if ( !QPixmapCache::find( entry, pixmap ) || !pixmap )
   284       QPixmap newPixmap = createPreview( entry );
   285       QPixmapCache::insert( entry, newPixmap );
   293   else if ( role == Qt::UserRole || role == Qt::ToolTipRole )
   301 void QgsSvgSelectorListModel::addSvgs( 
const QStringList &svgs )
   303   beginInsertRows( QModelIndex(), 
mSvgFiles.count(), 
mSvgFiles.count() + svgs.size() - 1 );
   315   : QStandardItemModel( parent )
   316   , mLoader( new QgsSvgGroupLoader( this ) )
   319   QStandardItem *parentItem = invisibleRootItem();
   320   QStringList parentPaths;
   321   parentPaths.reserve( svgPaths.size() );
   323   for ( 
int i = 0; i < svgPaths.size(); i++ )
   325     QDir dir( svgPaths.at( i ) );
   326     QStandardItem *baseGroup = 
nullptr;
   330       baseGroup = 
new QStandardItem( tr( 
"App Symbols" ) );
   334       baseGroup = 
new QStandardItem( tr( 
"User Symbols" ) );
   338       baseGroup = 
new QStandardItem( dir.dirName() );
   340     baseGroup->setData( QVariant( svgPaths.at( i ) ) );
   341     baseGroup->setEditable( 
false );
   342     baseGroup->setCheckable( 
false );
   344     baseGroup->setToolTip( dir.path() );
   345     parentItem->appendRow( baseGroup );
   346     parentPaths << svgPaths.at( i );
   347     mPathItemHash.insert( svgPaths.at( i ), baseGroup );
   348     QgsDebugMsg( QStringLiteral( 
"SVG base path %1: %2" ).arg( i ).arg( baseGroup->data().toString() ) );
   350   mLoader->setParentPaths( parentPaths );
   351   connect( mLoader, &QgsSvgGroupLoader::foundPath, 
this, &QgsSvgSelectorGroupsModel::addPath );
   360 void QgsSvgSelectorGroupsModel::addPath( 
const QString &parentPath, 
const QString &item )
   362   QStandardItem *parentGroup = mPathItemHash.value( parentPath );
   366   QString fullPath = parentPath + 
'/' + item;
   367   QStandardItem *group = 
new QStandardItem( item );
   368   group->setData( QVariant( fullPath ) );
   369   group->setEditable( 
false );
   370   group->setCheckable( 
false );
   371   group->setToolTip( fullPath );
   373   parentGroup->appendRow( group );
   374   mPathItemHash.insert( fullPath, group );
   388 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)   389   mIconSize = std::max( 30, static_cast< int >( std::round( 
Qgis::UI_SCALE_FACTOR * fontMetrics().width( 
'X' ) * 3 ) ) );
   391   mIconSize = std::max( 30, static_cast< int >( std::round( 
Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 
'X' ) * 3 ) ) );
   393   mImagesListView->setGridSize( QSize( mIconSize * 1.2, mIconSize * 1.2 ) );
   395   mGroupsTreeView->setHeaderHidden( 
true );
   398   connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged,
   399            this, &QgsSvgSelectorWidget::svgSelectionChanged );
   400   connect( mGroupsTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
   401            this, &QgsSvgSelectorWidget::populateIcons );
   406   mCurrentSvgPath = svgPath;
   410   mImagesListView->selectionModel()->blockSignals( 
true );
   411   QAbstractItemModel *m = mImagesListView->model();
   412   QItemSelectionModel *selModel = mImagesListView->selectionModel();
   413   for ( 
int i = 0; i < m->rowCount(); i++ )
   415     QModelIndex idx( m->index( i, 0 ) );
   416     if ( m->data( idx ).toString() == svgPath )
   418       selModel->select( idx, QItemSelectionModel::SelectCurrent );
   419       selModel->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent );
   420       mImagesListView->scrollTo( idx );
   424   mImagesListView->selectionModel()->blockSignals( 
false );
   429   return mCurrentSvgPath;
   432 void QgsSvgSelectorWidget::updateCurrentSvgPath( 
const QString &svgPath )
   434   mCurrentSvgPath = svgPath;
   438 void QgsSvgSelectorWidget::svgSelectionChanged( 
const QModelIndex &idx )
   440   QString filePath = idx.data( Qt::UserRole ).toString();
   442   updateCurrentSvgPath( filePath );
   445 void QgsSvgSelectorWidget::populateIcons( 
const QModelIndex &idx )
   447   QString path = idx.data( Qt::UserRole + 1 ).toString();
   449   QAbstractItemModel *oldModel = mImagesListView->model();
   451   mImagesListView->setModel( m );
   454   connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged,
   455            this, &QgsSvgSelectorWidget::svgSelectionChanged );
   458 void QgsSvgSelectorWidget::svgSourceChanged( 
const QString &text )
   461   bool validSVG = !resolvedPath.isNull();
   463   updateCurrentSvgPath( validSVG ? resolvedPath : text );
   469   mGroupsTreeView->setModel( g );
   471   int rows = g->rowCount( g->indexFromItem( g->invisibleRootItem() ) );
   472   for ( 
int i = 0; i < rows; i++ )
   474     mGroupsTreeView->setExpanded( g->indexFromItem( g->item( i ) ), 
true );
   478   QAbstractItemModel *oldModel = mImagesListView->model();
   480   mImagesListView->setModel( m );
   487     QDialogButtonBox::StandardButtons buttons,
   488     Qt::Orientation orientation )
   489   : QDialog( parent, fl )
   492   Q_UNUSED( orientation )
   495   mButtonBox = 
new QDialogButtonBox( buttons, orientation, 
this );
   496   connect( 
mButtonBox, &QDialogButtonBox::accepted, 
this, &QDialog::accept );
   497   connect( 
mButtonBox, &QDialogButtonBox::rejected, 
this, &QDialog::reject );
   499   setMinimumSize( 480, 320 );
 QgsSvgSelectorWidget * mSvgSelector
 
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. 
 
A model for displaying SVG files with a preview icon. 
 
static const double UI_SCALE_FACTOR
UI scaling factor. 
 
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)
Gets SVG as QImage. 
 
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. 
 
void sourceChanged(const QString &source)
Emitted whenever the file source is changed in the widget. 
 
QgsSvgSelectorGroupsModel(QObject *parent)
 
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon. 
 
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
 
int rowCount(const QModelIndex &parent=QModelIndex()) const override
 
QDialogButtonBox * mButtonBox
 
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons. 
 
static QString pkgDataPath()
Returns the common root path of all application data directories. 
 
~QgsSvgSelectorGroupsModel() override
 
A model for displaying SVG search paths. 
 
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object. 
 
QgsSvgSelectorListModel(QObject *parent, int iconSize=30)
Constructor for QgsSvgSelectorListModel. 
 
static QgsProject * instance()
Returns the QgsProject singleton instance. 
 
QgsSvgSelectorDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags, QDialogButtonBox::StandardButtons buttons=QDialogButtonBox::Close|QDialogButtonBox::Ok, Qt::Orientation orientation=Qt::Horizontal)
Constructor for QgsSvgSelectorDialog. 
 
static QStringList svgPaths()
Returns the paths to svg directories. 
 
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol's path from its name.