27 #include <QAbstractListModel> 30 #include <QFileDialog> 31 #include <QModelIndex> 32 #include <QPixmapCache> 39 QgsSvgSelectorLoader::QgsSvgSelectorLoader( QObject *parent )
44 QgsSvgSelectorLoader::~QgsSvgSelectorLoader()
49 void QgsSvgSelectorLoader::run()
53 mTraversedPaths.clear();
61 if ( !mQueuedSvgs.isEmpty() )
64 emit foundSvgs( mQueuedSvgs );
69 void QgsSvgSelectorLoader::stop()
72 while ( isRunning() ) {}
75 void QgsSvgSelectorLoader::loadPath(
const QString &path )
85 Q_FOREACH (
const QString &svgPath, svgPaths )
90 if ( !svgPath.isEmpty() )
101 QString canonicalPath = dir.canonicalPath();
102 if ( mTraversedPaths.contains( canonicalPath ) )
105 mTraversedPaths.insert( canonicalPath );
109 Q_FOREACH (
const QString &item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
114 QString newPath = dir.path() +
'/' + item;
121 void QgsSvgSelectorLoader::loadImages(
const QString &path )
124 Q_FOREACH (
const QString &item, dir.entryList( QStringList(
"*.svg" ), QDir::Files ) )
130 QString svgPath = dir.path() +
'/' + item;
134 mQueuedSvgs << svgPath;
138 if ( mTimer.elapsed() > mTimerThreshold && !mQueuedSvgs.isEmpty() )
140 emit foundSvgs( mQueuedSvgs );
146 if ( mTimerThreshold < 1000 )
147 mTimerThreshold *= 2;
158 QgsSvgGroupLoader::QgsSvgGroupLoader( QObject *parent )
164 QgsSvgGroupLoader::~QgsSvgGroupLoader()
169 void QgsSvgGroupLoader::run()
172 mTraversedPaths.clear();
174 while ( !mCanceled && !mParentPaths.isEmpty() )
176 QString parentPath = mParentPaths.takeFirst();
177 loadGroup( parentPath );
181 void QgsSvgGroupLoader::stop()
184 while ( isRunning() ) {}
187 void QgsSvgGroupLoader::loadGroup(
const QString &parentPath )
189 QDir parentDir( parentPath );
192 QString canonicalPath = parentDir.canonicalPath();
193 if ( mTraversedPaths.contains( canonicalPath ) )
196 mTraversedPaths.insert( canonicalPath );
198 Q_FOREACH (
const QString &item, parentDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
203 emit foundPath( parentPath, item );
204 mParentPaths.append( parentDir.path() +
'/' + item );
215 : QAbstractListModel( parent )
216 , mSvgLoader( new QgsSvgSelectorLoader( this ) )
217 , mIconSize( iconSize )
219 mSvgLoader->setPath( QString() );
220 connect( mSvgLoader, &QgsSvgSelectorLoader::foundSvgs,
this, &QgsSvgSelectorListModel::addSvgs );
225 : QAbstractListModel( parent )
226 , mSvgLoader( new QgsSvgSelectorLoader( this ) )
227 , mIconSize( iconSize )
229 mSvgLoader->setPath( path );
230 connect( mSvgLoader, &QgsSvgSelectorLoader::foundSvgs,
this, &QgsSvgSelectorListModel::addSvgs );
240 QPixmap QgsSvgSelectorListModel::createPreview(
const QString &entry )
const 244 double strokeWidth, fillOpacity, strokeOpacity;
245 bool fillParam, fillOpacityParam, strokeParam, strokeWidthParam, strokeOpacityParam;
246 bool hasDefaultFillColor =
false, hasDefaultFillOpacity =
false, hasDefaultStrokeColor =
false,
247 hasDefaultStrokeWidth =
false, hasDefaultStrokeOpacity =
false;
249 fillOpacityParam, hasDefaultFillOpacity, fillOpacity,
250 strokeParam, hasDefaultStrokeColor, stroke,
251 strokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
252 strokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
255 if ( !hasDefaultFillColor )
256 fill = QColor( 200, 200, 200 );
257 fill.setAlphaF( hasDefaultFillOpacity ? fillOpacity : 1.0 );
258 if ( !hasDefaultStrokeColor )
260 stroke.setAlphaF( hasDefaultStrokeOpacity ? strokeOpacity : 1.0 );
261 if ( !hasDefaultStrokeWidth )
266 return QPixmap::fromImage( img );
271 QString entry =
mSvgFiles.at( index.row() );
273 if ( role == Qt::DecorationRole )
276 if ( !QPixmapCache::find( entry, pixmap ) )
278 pixmap = createPreview( entry );
279 QPixmapCache::insert( entry, pixmap );
284 else if ( role == Qt::UserRole || role == Qt::ToolTipRole )
292 void QgsSvgSelectorListModel::addSvgs(
const QStringList &svgs )
294 beginInsertRows( QModelIndex(),
mSvgFiles.count(),
mSvgFiles.count() + svgs.size() - 1 );
306 : QStandardItemModel( parent )
307 , mLoader( new QgsSvgGroupLoader( this ) )
310 QStandardItem *parentItem = invisibleRootItem();
311 QStringList parentPaths;
312 parentPaths.reserve( svgPaths.size() );
314 for (
int i = 0; i < svgPaths.size(); i++ )
316 QDir dir( svgPaths.at( i ) );
317 QStandardItem *baseGroup =
nullptr;
321 baseGroup =
new QStandardItem( tr(
"App Symbols" ) );
325 baseGroup =
new QStandardItem( tr(
"User Symbols" ) );
329 baseGroup =
new QStandardItem( dir.dirName() );
331 baseGroup->setData( QVariant( svgPaths.at( i ) ) );
332 baseGroup->setEditable(
false );
333 baseGroup->setCheckable(
false );
334 baseGroup->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) );
335 baseGroup->setToolTip( dir.path() );
336 parentItem->appendRow( baseGroup );
337 parentPaths << svgPaths.at( i );
338 mPathItemHash.insert( svgPaths.at( i ), baseGroup );
339 QgsDebugMsg( QString(
"SVG base path %1: %2" ).arg( i ).arg( baseGroup->data().toString() ) );
341 mLoader->setParentPaths( parentPaths );
342 connect( mLoader, &QgsSvgGroupLoader::foundPath,
this, &QgsSvgSelectorGroupsModel::addPath );
351 void QgsSvgSelectorGroupsModel::addPath(
const QString &parentPath,
const QString &item )
353 QStandardItem *parentGroup = mPathItemHash.value( parentPath );
357 QString fullPath = parentPath +
'/' + item;
358 QStandardItem *group =
new QStandardItem( item );
359 group->setData( QVariant( fullPath ) );
360 group->setEditable(
false );
361 group->setCheckable(
false );
362 group->setToolTip( fullPath );
363 group->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) );
364 parentGroup->appendRow( group );
365 mPathItemHash.insert( fullPath, group );
376 connect( mFilePushButton, &QPushButton::clicked,
this, &QgsSvgSelectorWidget::mFilePushButton_clicked );
377 connect( mFileLineEdit, &QLineEdit::textChanged,
this, &QgsSvgSelectorWidget::mFileLineEdit_textChanged );
379 mIconSize = std::max( 30, static_cast< int >( std::round(
Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral(
"XXXX" ) ) ) ) );
380 mImagesListView->setGridSize( QSize( mIconSize * 1.2, mIconSize * 1.2 ) );
382 mGroupsTreeView->setHeaderHidden(
true );
385 connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged,
386 this, &QgsSvgSelectorWidget::svgSelectionChanged );
387 connect( mGroupsTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
388 this, &QgsSvgSelectorWidget::populateIcons );
393 mCurrentSvgPath = svgPath;
395 mFileLineEdit->blockSignals(
true );
396 mFileLineEdit->setText( svgPath );
397 mFileLineEdit->blockSignals(
false );
399 mImagesListView->selectionModel()->blockSignals(
true );
400 QAbstractItemModel *m = mImagesListView->model();
401 QItemSelectionModel *selModel = mImagesListView->selectionModel();
402 for (
int i = 0; i < m->rowCount(); i++ )
404 QModelIndex idx( m->index( i, 0 ) );
405 if ( m->data( idx ).toString() == svgPath )
407 selModel->select( idx, QItemSelectionModel::SelectCurrent );
408 selModel->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent );
409 mImagesListView->scrollTo( idx );
413 mImagesListView->selectionModel()->blockSignals(
false );
418 return mCurrentSvgPath;
421 void QgsSvgSelectorWidget::updateCurrentSvgPath(
const QString &svgPath )
423 mCurrentSvgPath = svgPath;
427 void QgsSvgSelectorWidget::svgSelectionChanged(
const QModelIndex &idx )
429 QString filePath = idx.data( Qt::UserRole ).toString();
430 mFileLineEdit->setText( filePath );
431 updateCurrentSvgPath( filePath );
434 void QgsSvgSelectorWidget::populateIcons(
const QModelIndex &idx )
436 QString path = idx.data( Qt::UserRole + 1 ).toString();
438 QAbstractItemModel *oldModel = mImagesListView->model();
440 mImagesListView->setModel( m );
443 connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged,
444 this, &QgsSvgSelectorWidget::svgSelectionChanged );
448 void QgsSvgSelectorWidget::mFilePushButton_clicked()
451 QString openDir = settings.
value( QStringLiteral(
"UI/lastSVGMarkerDir" ), QDir::homePath() ).toString();
453 QString lineEditText = mFileLineEdit->text();
454 if ( !lineEditText.isEmpty() )
456 QFileInfo openDirFileInfo( lineEditText );
457 openDir = openDirFileInfo.path();
460 QString file = QFileDialog::getOpenFileName(
nullptr,
461 tr(
"Select SVG file" ),
463 tr(
"SVG files" ) +
" (*.svg *.SVG)" );
470 QFileInfo fi( file );
471 if ( !fi.exists() || !fi.isReadable() )
473 updateCurrentSvgPath( QString() );
474 updateLineEditFeedback(
false );
477 settings.
setValue( QStringLiteral(
"UI/lastSVGMarkerDir" ), fi.absolutePath() );
478 mFileLineEdit->setText( file );
479 updateCurrentSvgPath( file );
482 void QgsSvgSelectorWidget::updateLineEditFeedback(
bool ok,
const QString &tip )
485 mFileLineEdit->setStyleSheet( QString( !ok ?
"QLineEdit{ color: rgb(225, 0, 0); }" :
"" ) );
486 mFileLineEdit->setToolTip( !ok ? tr(
"File not found" ) : tip );
489 void QgsSvgSelectorWidget::mFileLineEdit_textChanged(
const QString &text )
492 bool validSVG = !resolvedPath.isNull();
494 updateLineEditFeedback( validSVG, resolvedPath );
495 updateCurrentSvgPath( validSVG ? resolvedPath : QString() );
501 mGroupsTreeView->setModel( g );
503 int rows = g->rowCount( g->indexFromItem( g->invisibleRootItem() ) );
504 for (
int i = 0; i < rows; i++ )
506 mGroupsTreeView->setExpanded( g->indexFromItem( g->item( i ) ),
true );
510 QAbstractItemModel *oldModel = mImagesListView->model();
512 mImagesListView->setModel( m );
519 QDialogButtonBox::StandardButtons buttons,
520 Qt::Orientation orientation )
521 : QDialog( parent, fl )
524 Q_UNUSED( orientation );
527 mButtonBox =
new QDialogButtonBox( buttons, orientation,
this );
528 connect(
mButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
529 connect(
mButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
531 setMinimumSize( 480, 320 );
542 restoreGeometry( settings.
value( QStringLiteral(
"Windows/SvgSelectorDialog/geometry" ) ).toByteArray() );
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.
This class is a composition of two QSettings instances:
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0)
Gets SVG as QImage.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
QgsSvgSelectorGroupsModel(QObject *parent)
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
void saveGeometry(QWidget *widget, const QString &keyName)
Save the wigget geometry into settings.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
~QgsSvgSelectorDialog() override
QDialogButtonBox * mButtonBox
static QString pkgDataPath()
Returns the common root path of all application data directories.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth) const
Tests if an svg file contains parameters for fill, stroke color, stroke width.
~QgsSvgSelectorGroupsModel() override
A model for displaying SVG search paths.
QgsSvgSelectorListModel(QObject *parent, int iconSize=30)
Constructor for QgsSvgSelectorListModel.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
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.