Skip to content
Permalink
Browse files
[browser] Add api to QgsDirectoryItem to control whether directory
items should be automatically monitored for changes

This monitoring can be expensive for some locations (eg network
folders or cloud based directories), so in these cases it is
desirable to allow it to be disabled to avoid hangs in the QGIS
application.
  • Loading branch information
nyalldawson committed May 31, 2021
1 parent 63c4f4f commit af1900f539edcb55d1913580c4d5c92fea5aab8b
@@ -198,6 +198,13 @@
Qgis.BrowserLayerType.__doc__ = 'Browser item layer types\n\n.. versionadded:: 3.20\n\n' + '* ``NoType``: ' + Qgis.BrowserLayerType.NoType.__doc__ + '\n' + '* ``Vector``: ' + Qgis.BrowserLayerType.Vector.__doc__ + '\n' + '* ``Raster``: ' + Qgis.BrowserLayerType.Raster.__doc__ + '\n' + '* ``Point``: ' + Qgis.BrowserLayerType.Point.__doc__ + '\n' + '* ``Line``: ' + Qgis.BrowserLayerType.Line.__doc__ + '\n' + '* ``Polygon``: ' + Qgis.BrowserLayerType.Polygon.__doc__ + '\n' + '* ``TableLayer``: ' + Qgis.BrowserLayerType.TableLayer.__doc__ + '\n' + '* ``Database``: ' + Qgis.BrowserLayerType.Database.__doc__ + '\n' + '* ``Table``: ' + Qgis.BrowserLayerType.Table.__doc__ + '\n' + '* ``Plugin``: ' + Qgis.BrowserLayerType.Plugin.__doc__ + '\n' + '* ``Mesh``: ' + Qgis.BrowserLayerType.Mesh.__doc__ + '\n' + '* ``VectorTile``: ' + Qgis.BrowserLayerType.VectorTile.__doc__ + '\n' + '* ``PointCloud``: ' + Qgis.BrowserLayerType.PointCloud.__doc__
# --
Qgis.BrowserLayerType.baseClass = Qgis
# monkey patching scoped based enum
Qgis.BrowserDirectoryMonitoring.Default.__doc__ = "Use default logic to determine whether directory should be monitored"
Qgis.BrowserDirectoryMonitoring.NeverMonitor.__doc__ = "Never monitor the directory, regardless of the default logic"
Qgis.BrowserDirectoryMonitoring.AlwaysMonitor.__doc__ = "Always monitor the directory, regardless of the default logic"
Qgis.BrowserDirectoryMonitoring.__doc__ = 'Browser directory item monitoring switches.\n\n.. versionadded:: 3.20\n\n' + '* ``Default``: ' + Qgis.BrowserDirectoryMonitoring.Default.__doc__ + '\n' + '* ``NeverMonitor``: ' + Qgis.BrowserDirectoryMonitoring.NeverMonitor.__doc__ + '\n' + '* ``AlwaysMonitor``: ' + Qgis.BrowserDirectoryMonitoring.AlwaysMonitor.__doc__
# --
Qgis.BrowserDirectoryMonitoring.baseClass = Qgis
QgsVectorLayerExporter.ExportError = Qgis.VectorExportResult
# monkey patching scoped based enum
QgsVectorLayerExporter.NoError = Qgis.VectorExportResult.Success
@@ -116,6 +116,68 @@ If ``color`` is an invalid color then the default icon color will be used.
static bool hiddenPath( const QString &path );
%Docstring
Check if the given path is hidden from the browser model
%End

static Qgis::BrowserDirectoryMonitoring monitoringForPath( const QString &path );
%Docstring
Returns the monitoring setting for a directory ``path``.

This method returns the monitoring setting for ``path`` only. If no explicit monitoring setting
is in place for the path then Qgis.BrowserDirectoryMonitoring.Default is returned.

This method does not consider the monitoring setting of parent directories.

.. versionadded:: 3.20
%End

static bool pathShouldByMonitoredByDefault( const QString &path );
%Docstring
Returns ``True`` if a directory ``path`` should be monitored by default.

In the absence of any other settings this will dictate whether the directory is monitored. This method
does not consider an explicit monitoring setting set for the path, which can be determined by
calling :py:func:`~QgsDirectoryItem.monitoringForPath`.

All parent directories will be checked to determine whether they have monitoring
manually enabled or disabled. As soon as a parent directory is found which has monitoring
manually enabled or disabled then the corresponding value will be returned.

Paths are monitored by default, so if no explicit setting is in place for a parent directory then
the function will return ``True``.

.. seealso:: :py:func:`isMonitored`

.. seealso:: :py:func:`setMonitoring`

.. versionadded:: 3.20
%End

bool isMonitored() const;
%Docstring
Returns ``True`` if the directory is currently being monitored for changes and the item auto-refreshed
when these occur.

.. versionadded:: 3.20
%End

Qgis::BrowserDirectoryMonitoring monitoring() const;
%Docstring
Returns the monitoring setting for this directory item.

.. seealso:: :py:func:`setMonitoring`

.. versionadded:: 3.20
%End

void setMonitoring( Qgis::BrowserDirectoryMonitoring monitoring );
%Docstring
Sets the ``monitoring`` setting for this directory.

This is a persistent setting, which is saved in QSettings.

.. seealso:: :py:func:`monitoring`

.. versionadded:: 3.20
%End

public slots:
@@ -126,6 +188,8 @@ Check if the given path is hidden from the browser model
protected:
void init();



};


@@ -202,6 +202,13 @@ The development version
PointCloud
};

enum class BrowserDirectoryMonitoring
{
Default,
NeverMonitor,
AlwaysMonitor,
};

enum class VectorExportResult
{
Success,
@@ -51,6 +51,21 @@ QgsDirectoryItem::QgsDirectoryItem( QgsDataItem *parent, const QString &name,
, mRefreshLater( false )
{
QgsSettings settings;

mMonitoring = monitoringForPath( mDirPath );
switch ( mMonitoring )
{
case Qgis::BrowserDirectoryMonitoring::Default:
mMonitored = pathShouldByMonitoredByDefault( mDirPath );
break;
case Qgis::BrowserDirectoryMonitoring::NeverMonitor:
mMonitored = false;
break;
case Qgis::BrowserDirectoryMonitoring::AlwaysMonitor:
mMonitored = true;
break;
}

settings.beginGroup( QStringLiteral( "qgis/browserPathColors" ) );
QString settingKey = mDirPath;
settingKey.replace( '/', QStringLiteral( "|||" ) );
@@ -70,6 +85,47 @@ void QgsDirectoryItem::init()
setToolTip( QDir::toNativeSeparators( mDirPath ) );
}

void QgsDirectoryItem::reevaluateMonitoring()
{
mMonitoring = monitoringForPath( mDirPath );
switch ( mMonitoring )
{
case Qgis::BrowserDirectoryMonitoring::Default:
mMonitored = pathShouldByMonitoredByDefault( mDirPath );
break;
case Qgis::BrowserDirectoryMonitoring::NeverMonitor:
mMonitored = false;
break;
case Qgis::BrowserDirectoryMonitoring::AlwaysMonitor:
mMonitored = true;
break;
}

const QVector<QgsDataItem *> childItems = children();
for ( QgsDataItem *child : childItems )
{
if ( QgsDirectoryItem *dirItem = qobject_cast< QgsDirectoryItem *>( child ) )
dirItem->reevaluateMonitoring();
}

createOrDestroyFileSystemWatcher();
}

void QgsDirectoryItem::createOrDestroyFileSystemWatcher()
{
if ( !mMonitored && mFileSystemWatcher )
{
mFileSystemWatcher->deleteLater();
mFileSystemWatcher = nullptr;
}
else if ( mMonitored && state() == Qgis::BrowserItemState::Populated && !mFileSystemWatcher )
{
mFileSystemWatcher = new QFileSystemWatcher( this );
mFileSystemWatcher->addPath( mDirPath );
connect( mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &QgsDirectoryItem::directoryChanged );
}
}

QColor QgsDirectoryItem::iconColor() const
{
return mIconColor;
@@ -123,6 +179,74 @@ QIcon QgsDirectoryItem::icon()
return iconDir( mIconColor, mIconColor.darker() );
}

Qgis::BrowserDirectoryMonitoring QgsDirectoryItem::monitoring() const
{
return mMonitoring;
}

void QgsDirectoryItem::setMonitoring( Qgis::BrowserDirectoryMonitoring monitoring )
{
mMonitoring = monitoring;

QgsSettings settings;
QStringList noMonitorDirs = settings.value( QStringLiteral( "qgis/disableMonitorItemUris" ), QStringList() ).toStringList();
QStringList alwaysMonitorDirs = settings.value( QStringLiteral( "qgis/alwaysMonitorItemUris" ), QStringList() ).toStringList();

switch ( mMonitoring )
{
case Qgis::BrowserDirectoryMonitoring::Default:
{
// remove disable/always setting for this path, so that default behavior is used
noMonitorDirs.removeAll( mDirPath );
settings.setValue( QStringLiteral( "qgis/disableMonitorItemUris" ), noMonitorDirs );

alwaysMonitorDirs.removeAll( mDirPath );
settings.setValue( QStringLiteral( "qgis/alwaysMonitorItemUris" ), alwaysMonitorDirs );

mMonitored = pathShouldByMonitoredByDefault( mDirPath );
break;
}

case Qgis::BrowserDirectoryMonitoring::NeverMonitor:
{
if ( !noMonitorDirs.contains( mDirPath ) )
{
noMonitorDirs.append( mDirPath );
settings.setValue( QStringLiteral( "qgis/disableMonitorItemUris" ), noMonitorDirs );
}

alwaysMonitorDirs.removeAll( mDirPath );
settings.setValue( QStringLiteral( "qgis/alwaysMonitorItemUris" ), alwaysMonitorDirs );

mMonitored = false;
break;
}

case Qgis::BrowserDirectoryMonitoring::AlwaysMonitor:
{
noMonitorDirs.removeAll( mDirPath );
settings.setValue( QStringLiteral( "qgis/disableMonitorItemUris" ), noMonitorDirs );

if ( !alwaysMonitorDirs.contains( mDirPath ) )
{
alwaysMonitorDirs.append( mDirPath );
settings.setValue( QStringLiteral( "qgis/alwaysMonitorItemUris" ), alwaysMonitorDirs );
}

mMonitored = true;
break;
}
}

const QVector<QgsDataItem *> childItems = children();
for ( QgsDataItem *child : childItems )
{
if ( QgsDirectoryItem *dirItem = qobject_cast< QgsDirectoryItem *>( child ) )
dirItem->reevaluateMonitoring();
}

createOrDestroyFileSystemWatcher();
}

QVector<QgsDataItem *> QgsDirectoryItem::createChildren()
{
@@ -235,7 +359,7 @@ void QgsDirectoryItem::setState( Qgis::BrowserItemState state )
{
QgsDataCollectionItem::setState( state );

if ( state == Qgis::BrowserItemState::Populated )
if ( state == Qgis::BrowserItemState::Populated && mMonitored )
{
if ( !mFileSystemWatcher )
{
@@ -292,6 +416,43 @@ bool QgsDirectoryItem::hiddenPath( const QString &path )
return ( idx > -1 );
}

Qgis::BrowserDirectoryMonitoring QgsDirectoryItem::monitoringForPath( const QString &path )
{
QgsSettings settings;
if ( settings.value( QStringLiteral( "qgis/disableMonitorItemUris" ), QStringList() ).toStringList().contains( path ) )
return Qgis::BrowserDirectoryMonitoring::NeverMonitor;
else if ( settings.value( QStringLiteral( "qgis/alwaysMonitorItemUris" ), QStringList() ).toStringList().contains( path ) )
return Qgis::BrowserDirectoryMonitoring::AlwaysMonitor;
return Qgis::BrowserDirectoryMonitoring::Default;
}

bool QgsDirectoryItem::pathShouldByMonitoredByDefault( const QString &path )
{
// check through path's parent directories, to see if any have an explicit
// always/never monitor setting. If so, this path will inherit that setting
const QString originalPath = QDir::cleanPath( path );
QString currentPath = originalPath;
QString prevPath;
while ( currentPath != prevPath )
{
prevPath = currentPath;
currentPath = QFileInfo( currentPath ).path();

switch ( monitoringForPath( currentPath ) )
{
case Qgis::BrowserDirectoryMonitoring::NeverMonitor:
return false;
case Qgis::BrowserDirectoryMonitoring::AlwaysMonitor:
return true;
case Qgis::BrowserDirectoryMonitoring::Default:
break;
}
}

// paths are monitored by default if no explicit setting is in place
return true;
}

void QgsDirectoryItem::childrenCreated()
{
QgsDebugMsgLevel( QStringLiteral( "mRefreshLater = %1" ).arg( mRefreshLater ), 3 );
@@ -308,7 +469,8 @@ void QgsDirectoryItem::childrenCreated()
QgsDataCollectionItem::childrenCreated();
}
// Re-connect the file watcher after all children have been created
connect( mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &QgsDirectoryItem::directoryChanged );
if ( mFileSystemWatcher && mMonitored )
connect( mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &QgsDirectoryItem::directoryChanged );
}

bool QgsDirectoryItem::equal( const QgsDataItem *other )

0 comments on commit af1900f

Please sign in to comment.