Fix SVG preview blocks QGIS (fix #14255)

Now SVG preview loading occurs in a background thread so that
dialogs can open instantly
This commit is contained in:
Nyall Dawson 2016-10-04 12:26:34 +10:00
parent d3f8763bd9
commit c60c4f7f0c
5 changed files with 420 additions and 216 deletions

View File

@ -715,7 +715,7 @@ QStringList QgsApplication::svgPaths()
}
myPathList << ABISYM( mDefaultSvgPaths );
return myPathList;
return myPathList.toSet().toList();
}
/*!

View File

@ -32,20 +32,183 @@
#include <QStyle>
#include <QTime>
// QgsSvgSelectorLoader
//--- QgsSvgSelectorListModel
///@cond PRIVATE
QgsSvgSelectorLoader::QgsSvgSelectorLoader( QObject* parent )
: QThread( parent )
, mCancelled( false )
, mTimerThreshold( 0 )
{
}
QgsSvgSelectorLoader::~QgsSvgSelectorLoader()
{
stop();
}
void QgsSvgSelectorLoader::run()
{
mCancelled = false;
mQueuedSvgs.clear();
// start with a small initial timeout (ms)
mTimerThreshold = 10;
mTimer.start();
loadPath( mPath );
if ( !mQueuedSvgs.isEmpty() )
{
// make sure we notify model of any remaining queued svgs (ie svgs added since last foundSvgs() signal was emitted)
emit foundSvgs( mQueuedSvgs );
}
mQueuedSvgs.clear();
}
void QgsSvgSelectorLoader::stop()
{
mCancelled = true;
while ( isRunning() ) {}
}
void QgsSvgSelectorLoader::loadPath( const QString& path )
{
if ( mCancelled )
return;
// QgsDebugMsg( QString( "loading path: %1" ).arg( path ) );
if ( path.isEmpty() )
{
QStringList svgPaths = QgsApplication::svgPaths();
Q_FOREACH ( const QString& svgPath, svgPaths )
{
if ( mCancelled )
return;
loadPath( svgPath );
}
}
else
{
loadImages( path );
QDir dir( path );
Q_FOREACH ( const QString& item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
{
if ( mCancelled )
return;
QString newPath = dir.path() + '/' + item;
loadPath( newPath );
// QgsDebugMsg( QString( "added path: %1" ).arg( newPath ) );
}
}
}
void QgsSvgSelectorLoader::loadImages( const QString& path )
{
QDir dir( path );
Q_FOREACH ( const QString& item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
{
if ( mCancelled )
return;
// TODO test if it is correct SVG
QString svgPath = dir.path() + '/' + item;
// QgsDebugMsg( QString( "adding svg: %1" ).arg( svgPath ) );
// add it to the list of queued SVGs
mQueuedSvgs << svgPath;
// we need to avoid spamming the model with notifications about new svgs, so foundSvgs
// is only emitted for blocks of SVGs (otherwise the view goes all flickery)
if ( mTimer.elapsed() > mTimerThreshold )
{
emit foundSvgs( mQueuedSvgs );
mQueuedSvgs.clear();
// increase the timer threshold - this ensures that the first lots of svgs loaded are added
// to the view quickly, but as the list grows new svgs are added at a slower rate.
// ie, good for initial responsiveness but avoid being spammy as the list grows.
if ( mTimerThreshold < 1000 )
mTimerThreshold *= 2;
mTimer.restart();
}
}
}
//
// QgsSvgGroupLoader
//
QgsSvgGroupLoader::QgsSvgGroupLoader( QObject* parent )
: QThread( parent )
, mCancelled( false )
{
}
QgsSvgGroupLoader::~QgsSvgGroupLoader()
{
stop();
}
void QgsSvgGroupLoader::run()
{
mCancelled = false;
while ( !mCancelled && !mParentPaths.isEmpty() )
{
QString parentPath = mParentPaths.takeFirst();
loadGroup( parentPath );
}
}
void QgsSvgGroupLoader::stop()
{
mCancelled = true;
while ( isRunning() ) {}
}
void QgsSvgGroupLoader::loadGroup( const QString& parentPath )
{
QDir parentDir( parentPath );
Q_FOREACH ( const QString& item, parentDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
{
if ( mCancelled )
return;
emit foundPath( parentPath, item );
mParentPaths.append( parentDir.path() + '/' + item );
}
}
///@endcond
//,
// QgsSvgSelectorListModel
//
QgsSvgSelectorListModel::QgsSvgSelectorListModel( QObject* parent )
: QAbstractListModel( parent )
, mSvgLoader( new QgsSvgSelectorLoader( this ) )
{
mSvgFiles = QgsSymbolLayerUtils::listSvgFiles();
mSvgLoader->setPath( QString() );
connect( mSvgLoader, SIGNAL( foundSvgs( QStringList ) ), this, SLOT( addSvgs( QStringList ) ) );
mSvgLoader->start();
}
// Constructor to create model for icons in a specific path
QgsSvgSelectorListModel::QgsSvgSelectorListModel( QObject* parent, const QString& path )
: QAbstractListModel( parent )
, mSvgLoader( new QgsSvgSelectorLoader( this ) )
{
mSvgFiles = QgsSymbolLayerUtils::listSvgFilesAt( path );
mSvgLoader->setPath( path );
connect( mSvgLoader, SIGNAL( foundSvgs( QStringList ) ), this, SLOT( addSvgs( QStringList ) ) );
mSvgLoader->start();
}
int QgsSvgSelectorListModel::rowCount( const QModelIndex& parent ) const
@ -54,14 +217,7 @@ int QgsSvgSelectorListModel::rowCount( const QModelIndex & parent ) const
return mSvgFiles.count();
}
QVariant QgsSvgSelectorListModel::data( const QModelIndex & index, int role ) const
{
QString entry = mSvgFiles.at( index.row() );
if ( role == Qt::DecorationRole ) // icon
{
QPixmap pixmap;
if ( !QPixmapCache::find( entry, pixmap ) )
QPixmap QgsSvgSelectorListModel::createPreview( const QString& entry ) const
{
// render SVG file
QColor fill, outline;
@ -87,7 +243,19 @@ QVariant QgsSvgSelectorListModel::data( const QModelIndex & index, int role ) co
bool fitsInCache; // should always fit in cache at these sizes (i.e. under 559 px ^ 2, or half cache size)
const QImage& img = QgsSvgCache::instance()->svgAsImage( entry, 30.0, fill, outline, outlineWidth, 3.5 /*appr. 88 dpi*/, 1.0, fitsInCache );
pixmap = QPixmap::fromImage( img );
return QPixmap::fromImage( img );
}
QVariant QgsSvgSelectorListModel::data( const QModelIndex& index, int role ) const
{
QString entry = mSvgFiles.at( index.row() );
if ( role == Qt::DecorationRole ) // icon
{
QPixmap pixmap;
if ( !QPixmapCache::find( entry, pixmap ) )
{
pixmap = createPreview( entry );
QPixmapCache::insert( entry, pixmap );
}
@ -101,18 +269,30 @@ QVariant QgsSvgSelectorListModel::data( const QModelIndex & index, int role ) co
return QVariant();
}
void QgsSvgSelectorListModel::addSvgs( const QStringList& svgs )
{
beginInsertRows( QModelIndex(), mSvgFiles.count(), mSvgFiles.count() + svgs.size() - 1 );
mSvgFiles.append( svgs );
endInsertRows();
}
//--- QgsSvgSelectorGroupsModel
QgsSvgSelectorGroupsModel::QgsSvgSelectorGroupsModel( QObject* parent )
: QStandardItemModel( parent )
, mLoader( new QgsSvgGroupLoader( this ) )
{
QStringList svgPaths = QgsApplication::svgPaths();
QStandardItem *parentItem = invisibleRootItem();
QStringList parentPaths;
for ( int i = 0; i < svgPaths.size(); i++ )
{
QDir dir( svgPaths[i] );
QDir dir( svgPaths.at( i ) );
QStandardItem *baseGroup;
if ( dir.path().contains( QgsApplication::pkgDataPath() ) )
@ -127,31 +307,41 @@ QgsSvgSelectorGroupsModel::QgsSvgSelectorGroupsModel( QObject* parent )
{
baseGroup = new QStandardItem( dir.dirName() );
}
baseGroup->setData( QVariant( svgPaths[i] ) );
baseGroup->setData( QVariant( svgPaths.at( i ) ) );
baseGroup->setEditable( false );
baseGroup->setCheckable( false );
baseGroup->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) );
baseGroup->setToolTip( dir.path() );
parentItem->appendRow( baseGroup );
createTree( baseGroup );
parentPaths << svgPaths.at( i );
mPathItemHash.insert( svgPaths.at( i ), baseGroup );
QgsDebugMsg( QString( "SVG base path %1: %2" ).arg( i ).arg( baseGroup->data().toString() ) );
}
mLoader->setParentPaths( parentPaths );
connect( mLoader, SIGNAL( foundPath( QString, QString ) ), this, SLOT( addPath( QString, QString ) ) );
mLoader->start();
}
void QgsSvgSelectorGroupsModel::createTree( QStandardItem* &parentGroup )
QgsSvgSelectorGroupsModel::~QgsSvgSelectorGroupsModel()
{
QDir parentDir( parentGroup->data().toString() );
Q_FOREACH ( const QString& item, parentDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
mLoader->stop();
}
void QgsSvgSelectorGroupsModel::addPath( const QString& parentPath, const QString& item )
{
QStandardItem* parentGroup = mPathItemHash.value( parentPath );
if ( !parentGroup )
return;
QString fullPath = parentPath + '/' + item;
QStandardItem* group = new QStandardItem( item );
group->setData( QVariant( parentDir.path() + '/' + item ) );
group->setData( QVariant( fullPath ) );
group->setEditable( false );
group->setCheckable( false );
group->setToolTip( parentDir.path() + '/' + item );
group->setToolTip( fullPath );
group->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) );
parentGroup->appendRow( group );
createTree( group );
}
mPathItemHash.insert( fullPath, group );
}
@ -250,11 +440,14 @@ void QgsSvgSelectorWidget::populateIcons( const QModelIndex& idx )
{
QString path = idx.data( Qt::UserRole + 1 ).toString();
QAbstractItemModel* oldModel = mImagesListView->model();
QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( mImagesListView, path );
mImagesListView->setModel( m );
delete oldModel; //explicitly delete old model to force any background threads to stop
connect( mImagesListView->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ),
this, SLOT( svgSelectionChanged( const QModelIndex& ) ) );
}
void QgsSvgSelectorWidget::on_mFilePushButton_clicked()
@ -319,8 +512,10 @@ void QgsSvgSelectorWidget::populateList()
}
// Initally load the icons in the List view without any grouping
QAbstractItemModel* oldModel = mImagesListView->model();
QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( mImagesListView );
mImagesListView->setModel( m );
delete oldModel; //explicitly delete old model to force any background threads to stop
}
//-- QgsSvgSelectorDialog
@ -357,3 +552,4 @@ QgsSvgSelectorDialog::~QgsSvgSelectorDialog()
QSettings settings;
settings.setValue( "/Windows/SvgSelectorDialog/geometry", saveGeometry() );
}

View File

@ -20,13 +20,14 @@
#include "ui_widget_svgselector.h"
#include "qgisgui.h"
#include <QAbstractListModel>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLayout>
#include <QStandardItemModel>
#include <QWidget>
#include <QThread>
#include <QElapsedTimer>
class QCheckBox;
class QLayout;
@ -35,29 +36,172 @@ class QListView;
class QPushButton;
class QTreeView;
///@cond PRIVATE
/** \ingroup gui
* \class QgsSvgSelectorLoader
* Recursively loads SVG images from a path in a background thread.
* \note added in QGIS 2.18
*/
class GUI_EXPORT QgsSvgSelectorLoader : public QThread
{
Q_OBJECT
public:
/** Constructor for QgsSvgSelectorLoader
* @param parent parent object
*/
QgsSvgSelectorLoader( QObject* parent = nullptr );
~QgsSvgSelectorLoader();
/** Starts the loader finding and generating previews for SVG images. foundSvgs() will be
* emitted as the loader encounters SVG images.
* @brief run
*/
virtual void run() override;
/** Cancels the current loading operation. Waits until the thread has finished operation
* before returning.
*/
virtual void stop();
/** Sets the root path containing SVG images to load. If no path is set, the default SVG
* search paths will be used instead.
*/
void setPath( const QString& path )
{
mPath = path;
}
signals:
/** Emitted when the loader has found a block of SVG images. This signal is emitted with blocks
* of SVG images to prevent spamming any connected model.
* @param svgs list of SVGs and preview images found.
*/
void foundSvgs( QStringList svgs );
private:
QString mPath;
bool mCancelled;
QStringList mQueuedSvgs;
QElapsedTimer mTimer;
int mTimerThreshold;
void loadPath( const QString& path );
void loadImages( const QString& path );
};
/** \ingroup gui
* \class QgsSvgGroupLoader
* Recursively loads SVG paths in a background thread.
* \note added in QGIS 2.18
*/
class GUI_EXPORT QgsSvgGroupLoader : public QThread
{
Q_OBJECT
public:
/** Constructor for QgsSvgGroupLoader
* @param parent parent object
*/
QgsSvgGroupLoader( QObject* parent = nullptr );
~QgsSvgGroupLoader();
/** Starts the loader finding folders for SVG images.
* @brief run
*/
virtual void run() override;
/** Cancels the current loading operation. Waits until the thread has finished operation
* before returning.
*/
virtual void stop();
/** Sets the root path containing child paths to find. If no path is set, the default SVG
* search paths will be used instead.
*/
void setParentPaths( const QStringList& parentPaths )
{
mParentPaths = parentPaths;
}
signals:
/** Emitted when the loader has found a block of SVG images. This signal is emitted with blocks
* of SVG images to prevent spamming any connected model.
* @param svgs list of SVGs and preview images found.
*/
void foundPath( const QString& parentPath, const QString& path );
private:
QStringList mParentPaths;
bool mCancelled;
void loadGroup( const QString& parentPath );
};
///@endcond
///
/** \ingroup gui
* \class QgsSvgSelectorListModel
* A model for displaying SVG files with a preview icon. Population of the model is performed in
* a background thread to ensure that initial creation of the model is responsive and does
* not block the GUI.
*/
class GUI_EXPORT QgsSvgSelectorListModel : public QAbstractListModel
{
Q_OBJECT
public:
/** Constructor for QgsSvgSelectorListModel. All SVGs in folders from the application SVG
* search paths will be shown.
* @param parent parent object
*/
QgsSvgSelectorListModel( QObject* parent );
// Constructor to create model for icons in a specific path
/** Constructor for creating a model for SVG files in a specific path.
* @param parent parent object
* @param path initial path, which is recursively searched
*/
QgsSvgSelectorListModel( QObject* parent, const QString& path );
int rowCount( const QModelIndex & parent = QModelIndex() ) const override;
QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const override;
protected:
QStringList mSvgFiles;
private:
QPixmap createPreview( const QString& entry ) const;
QgsSvgSelectorLoader* mSvgLoader;
private slots:
/** Called to add SVG files to the model.
* @param svgs list of SVG files to add to model.
*/
void addSvgs( const QStringList& svgs );
};
/** \ingroup gui
* \class QgsSvgSelectorGroupsModel
* A model for displaying SVG search paths. Population of the model is performed in
* a background thread to ensure that initial creation of the model is responsive and does
* not block the GUI.
*/
class GUI_EXPORT QgsSvgSelectorGroupsModel : public QStandardItemModel
{
@ -65,9 +209,15 @@ class GUI_EXPORT QgsSvgSelectorGroupsModel : public QStandardItemModel
public:
QgsSvgSelectorGroupsModel( QObject* parent );
~QgsSvgSelectorGroupsModel();
private:
void createTree( QStandardItem* &parentGroup );
QgsSvgGroupLoader* mLoader;
QHash< QString, QStandardItem* > mPathItemHash;
private slots:
void addPath( const QString& parentPath, const QString& path );
};
/** \ingroup gui
@ -114,6 +264,7 @@ class GUI_EXPORT QgsSvgSelectorWidget : public QWidget, private Ui::WidgetSvgSel
private:
QString mCurrentSvgPath; // always stored as absolute path
};
/** \ingroup gui

View File

@ -34,6 +34,7 @@
#include "qgsmapcanvas.h"
#include "qgsapplication.h"
#include "qgsvectorlayer.h"
#include "qgssvgselectorwidget.h"
#include "qgslogger.h"
#include "qgssizescalewidget.h"
@ -1802,8 +1803,11 @@ QgsSvgMarkerSymbolLayerWidget::~QgsSvgMarkerSymbolLayerWidget()
void QgsSvgMarkerSymbolLayerWidget::populateList()
{
QgsSvgGroupsModel* g = new QgsSvgGroupsModel( viewGroups );
QAbstractItemModel* oldModel = viewGroups->model();
QgsSvgSelectorGroupsModel* g = new QgsSvgSelectorGroupsModel( viewGroups );
viewGroups->setModel( g );
delete oldModel;
// Set the tree expanded at the first level
int rows = g->rowCount( g->indexFromItem( g->invisibleRootItem() ) );
for ( int i = 0; i < rows; i++ )
@ -1812,19 +1816,22 @@ void QgsSvgMarkerSymbolLayerWidget::populateList()
}
// Initally load the icons in the List view without any grouping
QgsSvgListModel* m = new QgsSvgListModel( viewImages );
oldModel = viewImages->model();
QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( viewImages );
viewImages->setModel( m );
delete oldModel;
}
void QgsSvgMarkerSymbolLayerWidget::populateIcons( const QModelIndex& idx )
{
QString path = idx.data( Qt::UserRole + 1 ).toString();
QgsSvgListModel* m = new QgsSvgListModel( viewImages, path );
QAbstractItemModel* oldModel = viewImages->model();
QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( viewImages, path );
viewImages->setModel( m );
delete oldModel;
connect( viewImages->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( setName( const QModelIndex& ) ) );
emit changed();
}
void QgsSvgMarkerSymbolLayerWidget::setGuiForSvg( const QgsSvgMarkerSymbolLayer* layer )
@ -2285,8 +2292,11 @@ void QgsSVGFillSymbolLayerWidget::setFile( const QModelIndex& item )
void QgsSVGFillSymbolLayerWidget::insertIcons()
{
QgsSvgGroupsModel* g = new QgsSvgGroupsModel( mSvgTreeView );
QAbstractItemModel* oldModel = mSvgTreeView->model();
QgsSvgSelectorGroupsModel* g = new QgsSvgSelectorGroupsModel( mSvgTreeView );
mSvgTreeView->setModel( g );
delete oldModel;
// Set the tree expanded at the first level
int rows = g->rowCount( g->indexFromItem( g->invisibleRootItem() ) );
for ( int i = 0; i < rows; i++ )
@ -2294,19 +2304,22 @@ void QgsSVGFillSymbolLayerWidget::insertIcons()
mSvgTreeView->setExpanded( g->indexFromItem( g->item( i ) ), true );
}
QgsSvgListModel* m = new QgsSvgListModel( mSvgListView );
oldModel = mSvgListView->model();
QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( mSvgListView );
mSvgListView->setModel( m );
delete oldModel;
}
void QgsSVGFillSymbolLayerWidget::populateIcons( const QModelIndex& idx )
{
QString path = idx.data( Qt::UserRole + 1 ).toString();
QgsSvgListModel* m = new QgsSvgListModel( mSvgListView, path );
QAbstractItemModel* oldModel = mSvgListView->model();
QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( mSvgListView, path );
mSvgListView->setModel( m );
delete oldModel;
connect( mSvgListView->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( setFile( const QModelIndex& ) ) );
emit changed();
}
@ -3203,129 +3216,6 @@ void QgsRasterFillSymbolLayerWidget::updatePreviewImage()
}
/// @cond PRIVATE
QgsSvgListModel::QgsSvgListModel( QObject* parent ) : QAbstractListModel( parent )
{
mSvgFiles = QgsSymbolLayerUtils::listSvgFiles();
}
QgsSvgListModel::QgsSvgListModel( QObject* parent, const QString& path ) : QAbstractListModel( parent )
{
mSvgFiles = QgsSymbolLayerUtils::listSvgFilesAt( path );
}
int QgsSvgListModel::rowCount( const QModelIndex& parent ) const
{
Q_UNUSED( parent );
return mSvgFiles.count();
}
QVariant QgsSvgListModel::data( const QModelIndex& index, int role ) const
{
QString entry = mSvgFiles.at( index.row() );
if ( role == Qt::DecorationRole ) // icon
{
QPixmap pixmap;
if ( !QPixmapCache::find( entry, pixmap ) )
{
// render SVG file
QColor fill, outline;
double outlineWidth, fillOpacity, outlineOpacity;
bool fillParam, fillOpacityParam, outlineParam, outlineWidthParam, outlineOpacityParam;
bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultOutlineColor = false,
hasDefaultOutlineWidth = false, hasDefaultOutlineOpacity = false;
QgsSvgCache::instance()->containsParams( entry, fillParam, hasDefaultFillColor, fill,
fillOpacityParam, hasDefaultFillOpacity, fillOpacity,
outlineParam, hasDefaultOutlineColor, outline,
outlineWidthParam, hasDefaultOutlineWidth, outlineWidth,
outlineOpacityParam, hasDefaultOutlineOpacity, outlineOpacity );
//if defaults not set in symbol, use these values
if ( !hasDefaultFillColor )
fill = QColor( 200, 200, 200 );
fill.setAlphaF( hasDefaultFillOpacity ? fillOpacity : 1.0 );
if ( !hasDefaultOutlineColor )
outline = Qt::black;
outline.setAlphaF( hasDefaultOutlineOpacity ? outlineOpacity : 1.0 );
if ( !hasDefaultOutlineWidth )
outlineWidth = 0.6;
bool fitsInCache; // should always fit in cache at these sizes (i.e. under 559 px ^ 2, or half cache size)
const QImage& img = QgsSvgCache::instance()->svgAsImage( entry, 30.0, fill, outline, outlineWidth, 3.5 /*appr. 88 dpi*/, 1.0, fitsInCache );
pixmap = QPixmap::fromImage( img );
QPixmapCache::insert( entry, pixmap );
}
return pixmap;
}
else if ( role == Qt::UserRole || role == Qt::ToolTipRole )
{
return entry;
}
return QVariant();
}
QgsSvgGroupsModel::QgsSvgGroupsModel( QObject* parent ) : QStandardItemModel( parent )
{
QStringList svgPaths = QgsApplication::svgPaths();
QStandardItem *parentItem = invisibleRootItem();
for ( int i = 0; i < svgPaths.size(); i++ )
{
QDir dir( svgPaths[i] );
QStandardItem *baseGroup;
if ( dir.path().contains( QgsApplication::pkgDataPath() ) )
{
baseGroup = new QStandardItem( QString( "App Symbols" ) );
}
else if ( dir.path().contains( QgsApplication::qgisSettingsDirPath() ) )
{
baseGroup = new QStandardItem( QString( "User Symbols" ) );
}
else
{
baseGroup = new QStandardItem( dir.dirName() );
}
baseGroup->setData( QVariant( svgPaths[i] ) );
baseGroup->setEditable( false );
baseGroup->setCheckable( false );
baseGroup->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) );
baseGroup->setToolTip( dir.path() );
parentItem->appendRow( baseGroup );
createTree( baseGroup );
QgsDebugMsg( QString( "SVG base path %1: %2" ).arg( i ).arg( baseGroup->data().toString() ) );
}
}
void QgsSvgGroupsModel::createTree( QStandardItem*& parentGroup )
{
QDir parentDir( parentGroup->data().toString() );
Q_FOREACH ( const QString& item, parentDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
{
QStandardItem* group = new QStandardItem( item );
group->setData( QVariant( parentDir.path() + '/' + item ) );
group->setEditable( false );
group->setCheckable( false );
group->setToolTip( parentDir.path() + '/' + item );
group->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) );
parentGroup->appendRow( group );
createTree( group );
}
}
/// @endcond
QgsGeometryGeneratorSymbolLayerWidget::QgsGeometryGeneratorSymbolLayerWidget( const QgsVectorLayer* vl, QWidget* parent )
: QgsSymbolLayerWidget( parent, vl )
, mLayer( nullptr )

View File

@ -707,39 +707,6 @@ class GUI_EXPORT QgsCentroidFillSymbolLayerWidget : public QgsSymbolLayerWidget,
};
///@cond PRIVATE
class QgsSvgListModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit QgsSvgListModel( QObject* parent );
// Constructor to create model for icons in a specific path
QgsSvgListModel( QObject* parent, const QString& path );
int rowCount( const QModelIndex & parent = QModelIndex() ) const override;
QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const override;
protected:
QStringList mSvgFiles;
};
class QgsSvgGroupsModel : public QStandardItemModel
{
Q_OBJECT
public:
explicit QgsSvgGroupsModel( QObject* parent );
private:
void createTree( QStandardItem* &parentGroup );
};
///@endcond
#include "ui_qgsgeometrygeneratorwidgetbase.h"
class QgsGeometryGeneratorSymbolLayer;