Skip to content
Permalink
Browse files
[browser] By default do not monitor directories on drives we know
are slow

Effectively this means that the browser no longer defaults to watching
network and remote drives (on Windows) for changes. This is expensive
to do and can result in large hangs in the QGIS application.

Users can still manually opt-in to monitoring of these locations
through the context menu of the directory in the browser panel.
  • Loading branch information
nyalldawson committed May 31, 2021
1 parent 75838d3 commit 8ff0efd2301cfe6d154fd9540786f7c4296af6ba
@@ -97,5 +97,7 @@
#define PDAL_VERSION_MINOR "${PDAL_VERSION_MINOR}"
#define PDAL_VERSION_MICRO "${PDAL_VERSION_MICRO}"

#cmakedefine ENABLE_TESTS

#endif

@@ -142,8 +142,9 @@ 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``.
If no explicit setting is in place for a parent directory, then a check will be made to determine
whether the path resides on a known slow drive. If so, monitoring is disabled by default and
``False`` will be returned. Otherwise paths are monitored by default and the function will return ``True``.

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

@@ -23,6 +23,7 @@
#include "qgsdataprovider.h"
#include "qgszipitem.h"
#include "qgsprojectitem.h"
#include "qgsfileutils.h"
#include <QFileSystemWatcher>
#include <QDir>
#include <QMouseEvent>
@@ -449,6 +450,11 @@ bool QgsDirectoryItem::pathShouldByMonitoredByDefault( const QString &path )
}
}

// else if we know that the path is on a slow device, we don't monitor by default
// as this can be very expensive and slow down QGIS
if ( QgsFileUtils::pathIsSlowDevice( path ) )
return false;

// paths are monitored by default if no explicit setting is in place
return true;
}
@@ -150,8 +150,9 @@ class CORE_EXPORT QgsDirectoryItem : public QgsDataCollectionItem
* 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.
* If no explicit setting is in place for a parent directory, then a check will be made to determine
* whether the path resides on a known slow drive. If so, monitoring is disabled by default and
* FALSE will be returned. Otherwise paths are monitored by default and the function will return TRUE.
*
* \see isMonitored()
* \see setMonitoring()
@@ -15,6 +15,7 @@
#include "qgsfileutils.h"
#include "qgis.h"
#include "qgsexception.h"
#include "qgsconfig.h"
#include <QObject>
#include <QRegularExpression>
#include <QFileInfo>
@@ -337,6 +338,11 @@ Qgis::DriveType QgsFileUtils::driveType( const QString &path )

bool QgsFileUtils::pathIsSlowDevice( const QString &path )
{
#ifdef ENABLE_TESTS
if ( path.contains( QLatin1String( "fake_slow_path_for_unit_tests" ) ) )
return true;
#endif

try
{
const Qgis::DriveType type = driveType( path );
@@ -53,6 +53,7 @@ class TestQgsDataItem : public QObject
void testDirItem();
void testDirItemChildren();
void testDirItemMonitoring();
void testDirItemMonitoringSlowDrive();
void testLayerItemType();
void testProjectItemCreation();

@@ -341,6 +342,57 @@ void TestQgsDataItem::testDirItemMonitoring()
QVERIFY( !childItem3->mFileSystemWatcher );
}

void TestQgsDataItem::testDirItemMonitoringSlowDrive()
{
QTemporaryDir dir;

// fake a directory on a slow drive -- this is a special hardcoded path which always reports to be on a slow drive. See QgsFileUtils::pathIsSlowDevice
const QString parentDir = dir.path() + QStringLiteral( "/fake_slow_path_for_unit_tests" );
const QString child1 = parentDir + QStringLiteral( "/child1" );
QVERIFY( QDir().mkpath( child1 ) );
const QString child2 = parentDir + QStringLiteral( "/child2" );
QVERIFY( QDir().mkpath( child2 ) );
const QString child2child = child2 + QStringLiteral( "/child" );
QVERIFY( QDir().mkpath( child2child ) );

QVERIFY( !QgsDirectoryItem::pathShouldByMonitoredByDefault( parentDir ) );
QVERIFY( !QgsDirectoryItem::pathShouldByMonitoredByDefault( child1 ) );
QVERIFY( !QgsDirectoryItem::pathShouldByMonitoredByDefault( child2 ) );
QVERIFY( !QgsDirectoryItem::pathShouldByMonitoredByDefault( child2child ) );

std::unique_ptr< QgsDirectoryItem > dirItem = std::make_unique< QgsDirectoryItem >( nullptr, QStringLiteral( "parent name" ), parentDir, QStringLiteral( "/" ) + parentDir );
// user has not explicitly set the path to be monitored or not, so Default should be returned here:
QCOMPARE( dirItem->monitoring(), Qgis::BrowserDirectoryMonitoring::Default );
// but directory should NOT be monitored
QVERIFY( !dirItem->isMonitored() );

dirItem->populate( true );
QVERIFY( !dirItem->mFileSystemWatcher );

QCOMPARE( dirItem->rowCount(), 2 );
QPointer< QgsDirectoryItem > childItem1( qobject_cast< QgsDirectoryItem * >( dirItem->children().at( 0 ) ) );
QPointer< QgsDirectoryItem > childItem2( qobject_cast< QgsDirectoryItem * >( dirItem->children().at( 1 ) ) );
QVERIFY( childItem1 );
QVERIFY( childItem2 );
// neither of these should be monitored either!
QVERIFY( !childItem1->isMonitored() );
QVERIFY( !childItem2->isMonitored() );
childItem1->populate( true );
childItem2->populate( true );
QVERIFY( !childItem1->mFileSystemWatcher );
QVERIFY( !childItem2->mFileSystemWatcher );

// explicitly opt in to monitoring a directory on a slow drive
childItem2->setMonitoring( Qgis::BrowserDirectoryMonitoring::AlwaysMonitor );
QVERIFY( childItem2->mFileSystemWatcher );

// this means that subdirectories of childItem2 should now return true to being monitored by default
QVERIFY( !QgsDirectoryItem::pathShouldByMonitoredByDefault( parentDir ) );
QVERIFY( !QgsDirectoryItem::pathShouldByMonitoredByDefault( child1 ) );
QVERIFY( !QgsDirectoryItem::pathShouldByMonitoredByDefault( child2 ) );
QVERIFY( QgsDirectoryItem::pathShouldByMonitoredByDefault( child2child ) );
}

void TestQgsDataItem::testLayerItemType()
{
std::unique_ptr< QgsMapLayer > layer = std::make_unique< QgsVectorLayer >( mTestDataDir + "polys.shp",

0 comments on commit 8ff0efd

Please sign in to comment.