Skip to content

Commit

Permalink
[browser] React when a custom data item provider is added/removed
Browse files Browse the repository at this point in the history
Until now, if a plugin adds a custom data item provider that adds
a root item to the browser model, the new root data item would not
get added and a restart of QGIS was necessary.
  • Loading branch information
wonder-sk authored and nyalldawson committed Apr 20, 2020
1 parent ea9613e commit 0633989
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 18 deletions.
1 change: 1 addition & 0 deletions python/core/auto_generated/qgsbrowsermodel.sip.in
Expand Up @@ -9,6 +9,7 @@




class QgsBrowserModel : QAbstractItemModel
{
%Docstring
Expand Down
18 changes: 17 additions & 1 deletion python/core/auto_generated/qgsdataitemproviderregistry.sip.in
Expand Up @@ -11,7 +11,7 @@



class QgsDataItemProviderRegistry
class QgsDataItemProviderRegistry : QObject
{
%Docstring
This class keeps a list of data item providers that may add items to the browser tree.
Expand Down Expand Up @@ -63,6 +63,22 @@ Returns the (possibly blank) data provider key for a given data item provider na

:param dataItemProviderName: name of the data item provider

.. versionadded:: 3.14
%End

signals:

void providerAdded( QgsDataItemProvider *provider );
%Docstring
Emitted when a new data item provider has been added.

.. versionadded:: 3.14
%End

void providerWillBeRemoved( QgsDataItemProvider *provider );
%Docstring
Emitted when a data item provider is about to be removed

.. versionadded:: 3.14
%End

Expand Down
69 changes: 53 additions & 16 deletions src/core/qgsbrowsermodel.cpp
Expand Up @@ -49,6 +49,10 @@ QgsBrowserModel::QgsBrowserModel( QObject *parent )
: QAbstractItemModel( parent )

{
connect( QgsApplication::dataItemProviderRegistry(), &QgsDataItemProviderRegistry::providerAdded,
this, &QgsBrowserModel::dataItemProviderAdded );
connect( QgsApplication::dataItemProviderRegistry(), &QgsDataItemProviderRegistry::providerWillBeRemoved,
this, &QgsBrowserModel::dataItemProviderWillBeRemoved );
}

QgsBrowserModel::~QgsBrowserModel()
Expand Down Expand Up @@ -132,23 +136,9 @@ void QgsBrowserModel::addRootItems()
const auto constProviders = QgsApplication::dataItemProviderRegistry()->providers();
for ( QgsDataItemProvider *pr : constProviders )
{
int capabilities = pr->capabilities();
if ( capabilities == QgsDataProvider::NoDataCapabilities )
if ( QgsDataItem *item = addProviderRootItem( pr ) )
{
QgsDebugMsgLevel( pr->name() + " does not have any dataCapabilities", 4 );
continue;
}

QgsDataItem *item = pr->createDataItem( QString(), nullptr ); // empty path -> top level
if ( item )
{
// make sure the top level key is set always
item->setProviderKey( pr->name() );
// Forward the signal from the root items to the model (and then to the app)
connect( item, &QgsDataItem::connectionsChanged, this, &QgsBrowserModel::connectionsChanged );
QgsDebugMsgLevel( "Add new top level item : " + item->name(), 4 );
setupItemConnections( item );
providerMap.insertMulti( capabilities, item );
providerMap.insertMulti( pr->capabilities(), item );
}
}

Expand Down Expand Up @@ -182,6 +172,32 @@ void QgsBrowserModel::removeRootItems()
mDriveItems.clear();
}

void QgsBrowserModel::dataItemProviderAdded( QgsDataItemProvider *provider )
{
if ( !mInitialized )
return;

if ( QgsDataItem *item = addProviderRootItem( provider ) )
{
beginInsertRows( QModelIndex(), rowCount(), rowCount() );
mRootItems << item;
endInsertRows();
}
}

void QgsBrowserModel::dataItemProviderWillBeRemoved( QgsDataItemProvider *provider )
{
const auto constMRootItems = mRootItems;
for ( QgsDataItem *item : constMRootItems )
{
if ( item->providerKey() == provider->name() )
{
removeRootItem( item );
break; // assuming there is max. 1 root item per provider
}
}
}

QMap<QString, QgsDirectoryItem *> QgsBrowserModel::driveItems() const
{
return mDriveItems;
Expand Down Expand Up @@ -733,3 +749,24 @@ void QgsBrowserModel::removeRootItem( QgsDataItem *item )
endRemoveRows();
}

QgsDataItem *QgsBrowserModel::addProviderRootItem( QgsDataItemProvider *pr )
{
int capabilities = pr->capabilities();
if ( capabilities == QgsDataProvider::NoDataCapabilities )
{
QgsDebugMsgLevel( pr->name() + " does not have any dataCapabilities", 4 );
return nullptr;
}

QgsDataItem *item = pr->createDataItem( QString(), nullptr ); // empty path -> top level
if ( item )
{
// make sure the top level key is set always
item->setProviderKey( pr->name() );
// Forward the signal from the root items to the model (and then to the app)
connect( item, &QgsDataItem::connectionsChanged, this, &QgsBrowserModel::connectionsChanged );
QgsDebugMsgLevel( "Add new top level item : " + item->name(), 4 );
setupItemConnections( item );
}
return item;
}
8 changes: 8 additions & 0 deletions src/core/qgsbrowsermodel.h
Expand Up @@ -25,6 +25,8 @@

#include "qgsdataitem.h"

class QgsDataItemProvider;

/**
* \ingroup core
* \class QgsBrowserWatcher
Expand Down Expand Up @@ -252,6 +254,10 @@ class CORE_EXPORT QgsBrowserModel : public QAbstractItemModel
QgsFavoritesItem *mFavorites = nullptr;
QgsDirectoryItem *mProjectHome = nullptr;

private slots:
void dataItemProviderAdded( QgsDataItemProvider *provider );
void dataItemProviderWillBeRemoved( QgsDataItemProvider *provider );

private:
bool mInitialized = false;
QMap< QString, QgsDirectoryItem * > mDriveItems;
Expand All @@ -260,6 +266,8 @@ class CORE_EXPORT QgsBrowserModel : public QAbstractItemModel

void removeRootItem( QgsDataItem *item );

QgsDataItem *addProviderRootItem( QgsDataItemProvider *provider );

friend class TestQgsBrowserModel;
friend class TestQgsBrowserProxyModel;
};
Expand Down
4 changes: 4 additions & 0 deletions src/core/qgsdataitemproviderregistry.cpp
Expand Up @@ -66,13 +66,17 @@ void QgsDataItemProviderRegistry::addProvider( QgsDataItemProvider *provider )
mDataItemProviderOrigin[ provider->name() ] = provider->dataProviderKey();
}
mProviders.append( provider );
emit providerAdded( provider );
}

void QgsDataItemProviderRegistry::removeProvider( QgsDataItemProvider *provider )
{
int index = mProviders.indexOf( provider );
if ( index >= 0 )
{
emit providerWillBeRemoved( provider );
delete mProviders.takeAt( index );
}
}

QString QgsDataItemProviderRegistry::dataProviderKey( const QString &dataItemProviderName )
Expand Down
18 changes: 17 additions & 1 deletion src/core/qgsdataitemproviderregistry.h
Expand Up @@ -18,6 +18,7 @@

#include <QList>
#include <QMap>
#include <QObject>

#include "qgis_sip.h"

Expand All @@ -35,8 +36,9 @@ class QgsDataItemProvider;
*
* \since QGIS 2.10
*/
class CORE_EXPORT QgsDataItemProviderRegistry
class CORE_EXPORT QgsDataItemProviderRegistry : public QObject
{
Q_OBJECT
public:

QgsDataItemProviderRegistry();
Expand Down Expand Up @@ -79,6 +81,20 @@ class CORE_EXPORT QgsDataItemProviderRegistry
*/
QString dataProviderKey( const QString &dataItemProviderName );

signals:

/**
* Emitted when a new data item provider has been added.
* \since QGIS 3.14
*/
void providerAdded( QgsDataItemProvider *provider );

/**
* Emitted when a data item provider is about to be removed
* \since QGIS 3.14
*/
void providerWillBeRemoved( QgsDataItemProvider *provider );

private:
#ifdef SIP_RUN
QgsDataItemProviderRegistry( const QgsDataItemProviderRegistry &rh );
Expand Down
47 changes: 47 additions & 0 deletions tests/src/core/testqgsbrowsermodel.cpp
Expand Up @@ -25,6 +25,8 @@
#include "qgslogger.h"
#include "qgssettings.h"
#include "qgsbrowsermodel.h"
#include "qgsdataitemprovider.h"
#include "qgsdataitemproviderregistry.h"

class TestQgsBrowserModel : public QObject
{
Expand All @@ -38,6 +40,7 @@ class TestQgsBrowserModel : public QObject

void testModel();
void driveItems();
void updatesToDataItemProviderRegistry();

};

Expand Down Expand Up @@ -179,5 +182,49 @@ void TestQgsBrowserModel::driveItems()
QCOMPARE( rootItem->path(), QStringLiteral( "/" ) );
}


class TestDataItemProvider : public QgsDataItemProvider
{
public:
QString name() override { return QStringLiteral( "test" ); }
int capabilities() const override { return QgsDataProvider::Net; }
QgsDataItem *createDataItem( const QString &path, QgsDataItem *parentItem ) override
{
if ( path.isEmpty() )
return new QgsDataItem( QgsDataItem::Custom, parentItem, QStringLiteral( "test-root-item" ), path );
return nullptr;
}
};

static int testRootItemCount( QgsBrowserModel &model )
{
int count = 0;
for ( int i = 0; i < model.rowCount(); ++i )
{
if ( model.data( model.index( i, 0 ) ).toString() == QStringLiteral( "test-root-item" ) )
++count;
}
return count;
}

void TestQgsBrowserModel::updatesToDataItemProviderRegistry()
{
QgsBrowserModel model;
model.initialize();

QCOMPARE( testRootItemCount( model ), 0 );

QgsDataItemProvider *provider = new TestDataItemProvider;
QgsApplication::dataItemProviderRegistry()->addProvider( provider );

// browser should react to providerAdded() signal from the registry
QCOMPARE( testRootItemCount( model ), 1 );

QgsApplication::dataItemProviderRegistry()->removeProvider( provider );

// browser should react to providerWillBeRemoved() signal from the registry
QCOMPARE( testRootItemCount( model ), 0 );
}

QGSTEST_MAIN( TestQgsBrowserModel )
#include "testqgsbrowsermodel.moc"

0 comments on commit 0633989

Please sign in to comment.