27 #include <QAbstractListModel> 30 #include <QFileDialog> 31 #include <QModelIndex> 32 #include <QPixmapCache> 40 QgsSvgSelectorLoader::QgsSvgSelectorLoader( QObject *parent )
45 QgsSvgSelectorLoader::~QgsSvgSelectorLoader()
50 void QgsSvgSelectorLoader::run()
54 mTraversedPaths.clear();
62 if ( !mQueuedSvgs.isEmpty() )
65 emit foundSvgs( mQueuedSvgs );
70 void QgsSvgSelectorLoader::stop()
73 while ( isRunning() ) {}
76 void QgsSvgSelectorLoader::loadPath(
const QString &path )
86 const auto constSvgPaths = svgPaths;
87 for (
const QString &svgPath : constSvgPaths )
92 if ( !svgPath.isEmpty() )
103 QString canonicalPath = dir.canonicalPath();
104 if ( mTraversedPaths.contains( canonicalPath ) )
107 mTraversedPaths.insert( canonicalPath );
111 const auto constEntryList = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot );
112 for (
const QString &item : constEntryList )
117 QString newPath = dir.path() +
'/' + item;
124 void QgsSvgSelectorLoader::loadImages(
const QString &path )
127 const auto constEntryList = dir.entryList( QStringList(
"*.svg" ), QDir::Files );
128 for (
const QString &item : constEntryList )
134 QString svgPath = dir.path() +
'/' + item;
138 mQueuedSvgs << svgPath;
142 if ( mTimer.elapsed() > mTimerThreshold && !mQueuedSvgs.isEmpty() )
144 emit foundSvgs( mQueuedSvgs );
150 if ( mTimerThreshold < 1000 )
151 mTimerThreshold *= 2;
162 QgsSvgGroupLoader::QgsSvgGroupLoader( QObject *parent )
168 QgsSvgGroupLoader::~QgsSvgGroupLoader()
173 void QgsSvgGroupLoader::run()
176 mTraversedPaths.clear();
178 while ( !mCanceled && !mParentPaths.isEmpty() )
180 QString parentPath = mParentPaths.takeFirst();
181 loadGroup( parentPath );
185 void QgsSvgGroupLoader::stop()
188 while ( isRunning() ) {}
191 void QgsSvgGroupLoader::loadGroup(
const QString &parentPath )
193 QDir parentDir( parentPath );
196 QString canonicalPath = parentDir.canonicalPath();
197 if ( mTraversedPaths.contains( canonicalPath ) )
200 mTraversedPaths.insert( canonicalPath );
202 const auto constEntryList = parentDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot );
203 for (
const QString &item : constEntryList )
208 emit foundPath( parentPath, item );
209 mParentPaths.append( parentDir.path() +
'/' + item );
220 : QAbstractListModel( parent )
221 , mSvgLoader( new QgsSvgSelectorLoader( this ) )
222 , mIconSize( iconSize )
224 mSvgLoader->setPath( QString() );
225 connect( mSvgLoader, &QgsSvgSelectorLoader::foundSvgs,
this, &QgsSvgSelectorListModel::addSvgs );
230 : QAbstractListModel( parent )
231 , mSvgLoader( new QgsSvgSelectorLoader( this ) )
232 , mIconSize( iconSize )
234 mSvgLoader->setPath( path );
235 connect( mSvgLoader, &QgsSvgSelectorLoader::foundSvgs,
this, &QgsSvgSelectorListModel::addSvgs );
245 QPixmap QgsSvgSelectorListModel::createPreview(
const QString &entry )
const 249 double strokeWidth, fillOpacity, strokeOpacity;
250 bool fillParam, fillOpacityParam, strokeParam, strokeWidthParam, strokeOpacityParam;
251 bool hasDefaultFillColor =
false, hasDefaultFillOpacity =
false, hasDefaultStrokeColor =
false,
252 hasDefaultStrokeWidth =
false, hasDefaultStrokeOpacity =
false;
254 fillOpacityParam, hasDefaultFillOpacity, fillOpacity,
255 strokeParam, hasDefaultStrokeColor, stroke,
256 strokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
257 strokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
260 if ( !hasDefaultFillColor )
261 fill = QColor( 200, 200, 200 );
262 fill.setAlphaF( hasDefaultFillOpacity ? fillOpacity : 1.0 );
263 if ( !hasDefaultStrokeColor )
265 stroke.setAlphaF( hasDefaultStrokeOpacity ? strokeOpacity : 1.0 );
266 if ( !hasDefaultStrokeWidth )
271 return QPixmap::fromImage( img );
276 QString entry =
mSvgFiles.at( index.row() );
278 if ( role == Qt::DecorationRole )
281 if ( !QPixmapCache::find( entry, pixmap ) )
283 pixmap = createPreview( entry );
284 QPixmapCache::insert( entry, pixmap );
289 else if ( role == Qt::UserRole || role == Qt::ToolTipRole )
297 void QgsSvgSelectorListModel::addSvgs(
const QStringList &svgs )
299 beginInsertRows( QModelIndex(),
mSvgFiles.count(),
mSvgFiles.count() + svgs.size() - 1 );
311 : QStandardItemModel( parent )
312 , mLoader( new QgsSvgGroupLoader( this ) )
315 QStandardItem *parentItem = invisibleRootItem();
316 QStringList parentPaths;
317 parentPaths.reserve( svgPaths.size() );
319 for (
int i = 0; i < svgPaths.size(); i++ )
321 QDir dir( svgPaths.at( i ) );
322 QStandardItem *baseGroup =
nullptr;
326 baseGroup =
new QStandardItem( tr(
"App Symbols" ) );
330 baseGroup =
new QStandardItem( tr(
"User Symbols" ) );
334 baseGroup =
new QStandardItem( dir.dirName() );
336 baseGroup->setData( QVariant( svgPaths.at( i ) ) );
337 baseGroup->setEditable(
false );
338 baseGroup->setCheckable(
false );
340 baseGroup->setToolTip( dir.path() );
341 parentItem->appendRow( baseGroup );
342 parentPaths << svgPaths.at( i );
343 mPathItemHash.insert( svgPaths.at( i ), baseGroup );
344 QgsDebugMsg( QStringLiteral(
"SVG base path %1: %2" ).arg( i ).arg( baseGroup->data().toString() ) );
346 mLoader->setParentPaths( parentPaths );
347 connect( mLoader, &QgsSvgGroupLoader::foundPath,
this, &QgsSvgSelectorGroupsModel::addPath );
356 void QgsSvgSelectorGroupsModel::addPath(
const QString &parentPath,
const QString &item )
358 QStandardItem *parentGroup = mPathItemHash.value( parentPath );
362 QString fullPath = parentPath +
'/' + item;
363 QStandardItem *group =
new QStandardItem( item );
364 group->setData( QVariant( fullPath ) );
365 group->setEditable(
false );
366 group->setCheckable(
false );
367 group->setToolTip( fullPath );
369 parentGroup->appendRow( group );
370 mPathItemHash.insert( fullPath, group );
384 mIconSize = std::max( 30, static_cast< int >( std::round(
Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral(
"XXXX" ) ) ) ) );
385 mImagesListView->setGridSize( QSize( mIconSize * 1.2, mIconSize * 1.2 ) );
387 mGroupsTreeView->setHeaderHidden(
true );
390 connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged,
391 this, &QgsSvgSelectorWidget::svgSelectionChanged );
392 connect( mGroupsTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
393 this, &QgsSvgSelectorWidget::populateIcons );
398 mCurrentSvgPath = svgPath;
402 mImagesListView->selectionModel()->blockSignals(
true );
403 QAbstractItemModel *m = mImagesListView->model();
404 QItemSelectionModel *selModel = mImagesListView->selectionModel();
405 for (
int i = 0; i < m->rowCount(); i++ )
407 QModelIndex idx( m->index( i, 0 ) );
408 if ( m->data( idx ).toString() == svgPath )
410 selModel->select( idx, QItemSelectionModel::SelectCurrent );
411 selModel->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent );
412 mImagesListView->scrollTo( idx );
416 mImagesListView->selectionModel()->blockSignals(
false );
421 return mCurrentSvgPath;
424 void QgsSvgSelectorWidget::updateCurrentSvgPath(
const QString &svgPath )
426 mCurrentSvgPath = svgPath;
430 void QgsSvgSelectorWidget::svgSelectionChanged(
const QModelIndex &idx )
432 QString filePath = idx.data( Qt::UserRole ).toString();
434 updateCurrentSvgPath( filePath );
437 void QgsSvgSelectorWidget::populateIcons(
const QModelIndex &idx )
439 QString path = idx.data( Qt::UserRole + 1 ).toString();
441 QAbstractItemModel *oldModel = mImagesListView->model();
443 mImagesListView->setModel( m );
446 connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged,
447 this, &QgsSvgSelectorWidget::svgSelectionChanged );
450 void QgsSvgSelectorWidget::svgSourceChanged(
const QString &text )
453 bool validSVG = !resolvedPath.isNull();
455 updateCurrentSvgPath( validSVG ? resolvedPath : text );
461 mGroupsTreeView->setModel( g );
463 int rows = g->rowCount( g->indexFromItem( g->invisibleRootItem() ) );
464 for (
int i = 0; i < rows; i++ )
466 mGroupsTreeView->setExpanded( g->indexFromItem( g->item( i ) ),
true );
470 QAbstractItemModel *oldModel = mImagesListView->model();
472 mImagesListView->setModel( m );
479 QDialogButtonBox::StandardButtons buttons,
480 Qt::Orientation orientation )
481 : QDialog( parent, fl )
484 Q_UNUSED( orientation )
487 mButtonBox =
new QDialogButtonBox( buttons, orientation,
this );
488 connect(
mButtonBox, &QDialogButtonBox::accepted,
this, &QDialog::accept );
489 connect(
mButtonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
491 setMinimumSize( 480, 320 );
502 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.
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
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
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.
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.
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.
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.