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 )
282 if ( !QPixmapCache::find( entry, pixmap ) )
284 pixmap = createPreview( entry );
285 QPixmapCache::insert( entry, pixmap );
290 else if ( role == Qt::UserRole || role == Qt::ToolTipRole )
298 void QgsSvgSelectorListModel::addSvgs(
const QStringList &svgs )
300 beginInsertRows( QModelIndex(),
mSvgFiles.count(),
mSvgFiles.count() + svgs.size() - 1 );
312 : QStandardItemModel( parent )
313 , mLoader( new QgsSvgGroupLoader( this ) )
316 QStandardItem *parentItem = invisibleRootItem();
317 QStringList parentPaths;
318 parentPaths.reserve( svgPaths.size() );
320 for (
int i = 0; i < svgPaths.size(); i++ )
322 QDir dir( svgPaths.at( i ) );
323 QStandardItem *baseGroup =
nullptr;
327 baseGroup =
new QStandardItem( tr(
"App Symbols" ) );
331 baseGroup =
new QStandardItem( tr(
"User Symbols" ) );
335 baseGroup =
new QStandardItem( dir.dirName() );
337 baseGroup->setData( QVariant( svgPaths.at( i ) ) );
338 baseGroup->setEditable(
false );
339 baseGroup->setCheckable(
false );
341 baseGroup->setToolTip( dir.path() );
342 parentItem->appendRow( baseGroup );
343 parentPaths << svgPaths.at( i );
344 mPathItemHash.insert( svgPaths.at( i ), baseGroup );
345 QgsDebugMsg( QStringLiteral(
"SVG base path %1: %2" ).arg( i ).arg( baseGroup->data().toString() ) );
347 mLoader->setParentPaths( parentPaths );
348 connect( mLoader, &QgsSvgGroupLoader::foundPath,
this, &QgsSvgSelectorGroupsModel::addPath );
357 void QgsSvgSelectorGroupsModel::addPath(
const QString &parentPath,
const QString &item )
359 QStandardItem *parentGroup = mPathItemHash.value( parentPath );
363 QString fullPath = parentPath +
'/' + item;
364 QStandardItem *group =
new QStandardItem( item );
365 group->setData( QVariant( fullPath ) );
366 group->setEditable(
false );
367 group->setCheckable(
false );
368 group->setToolTip( fullPath );
370 parentGroup->appendRow( group );
371 mPathItemHash.insert( fullPath, group );
385 mIconSize = std::max( 30, static_cast< int >( std::round(
Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral(
"XXXX" ) ) ) ) );
386 mImagesListView->setGridSize( QSize( mIconSize * 1.2, mIconSize * 1.2 ) );
388 mGroupsTreeView->setHeaderHidden(
true );
391 connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged,
392 this, &QgsSvgSelectorWidget::svgSelectionChanged );
393 connect( mGroupsTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
394 this, &QgsSvgSelectorWidget::populateIcons );
399 mCurrentSvgPath = svgPath;
403 mImagesListView->selectionModel()->blockSignals(
true );
404 QAbstractItemModel *m = mImagesListView->model();
405 QItemSelectionModel *selModel = mImagesListView->selectionModel();
406 for (
int i = 0; i < m->rowCount(); i++ )
408 QModelIndex idx( m->index( i, 0 ) );
409 if ( m->data( idx ).toString() == svgPath )
411 selModel->select( idx, QItemSelectionModel::SelectCurrent );
412 selModel->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent );
413 mImagesListView->scrollTo( idx );
417 mImagesListView->selectionModel()->blockSignals(
false );
422 return mCurrentSvgPath;
425 void QgsSvgSelectorWidget::updateCurrentSvgPath(
const QString &svgPath )
427 mCurrentSvgPath = svgPath;
431 void QgsSvgSelectorWidget::svgSelectionChanged(
const QModelIndex &idx )
433 QString filePath = idx.data( Qt::UserRole ).toString();
435 updateCurrentSvgPath( filePath );
438 void QgsSvgSelectorWidget::populateIcons(
const QModelIndex &idx )
440 QString path = idx.data( Qt::UserRole + 1 ).toString();
442 QAbstractItemModel *oldModel = mImagesListView->model();
444 mImagesListView->setModel( m );
447 connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged,
448 this, &QgsSvgSelectorWidget::svgSelectionChanged );
451 void QgsSvgSelectorWidget::svgSourceChanged(
const QString &text )
454 bool validSVG = !resolvedPath.isNull();
456 updateCurrentSvgPath( validSVG ? resolvedPath : text );
462 mGroupsTreeView->setModel( g );
464 int rows = g->rowCount( g->indexFromItem( g->invisibleRootItem() ) );
465 for (
int i = 0; i < rows; i++ )
467 mGroupsTreeView->setExpanded( g->indexFromItem( g->item( i ) ),
true );
471 QAbstractItemModel *oldModel = mImagesListView->model();
473 mImagesListView->setModel( m );
480 QDialogButtonBox::StandardButtons buttons,
481 Qt::Orientation orientation )
482 : QDialog( parent, fl )
485 Q_UNUSED( orientation )
488 mButtonBox =
new QDialogButtonBox( buttons, orientation,
this );
489 connect(
mButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
490 connect(
mButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
492 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.