mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
[FEATURE] Allow exploration of QGS project file contents directly
within browser Allows QGIS project file items inside the browser to be expanded, showing the full layer tree (including groups) contained within that project. Layers are shown as normal layer items, allowing them to be easily added to the current project via drag and drop or double click. Additionally, because they are treated just the same as any other layer items in the browser, they can be drag and dropped within the browser to e.g. directly copy the layer to a geopackage file! TODO: apply layer symbology from project file when adding a layer from a different project to the current project
This commit is contained in:
parent
f0436df618
commit
09c2daa1c1
@ -1161,6 +1161,8 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
|
||||
registerCustomDropHandler( new QgsPyDropHandler() );
|
||||
#endif
|
||||
|
||||
QgsApplication::dataItemProviderRegistry()->addProvider( new QgsProjectDataItemProvider() );
|
||||
|
||||
// Create the plugin registry and load plugins
|
||||
// load any plugins that were running in the last session
|
||||
mSplash->showMessage( tr( "Restoring loaded plugins" ), Qt::AlignHCenter | Qt::AlignBottom );
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include "qgisapp.h"
|
||||
#include "qgsstyleexportimportdialog.h"
|
||||
#include "qgsstyle.h"
|
||||
#include "qgslayertreenode.h"
|
||||
#include "qgslayertree.h"
|
||||
#include <QDesktopServices>
|
||||
|
||||
//
|
||||
@ -399,3 +401,127 @@ bool QgsStyleXmlDropHandler::handleFileDrop( const QString &file )
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// QgsProjectRootDataItem
|
||||
//
|
||||
|
||||
QgsProjectRootDataItem::QgsProjectRootDataItem( QgsDataItem *parent, const QString &path )
|
||||
: QgsProjectItem( parent, QFileInfo( path ).completeBaseName(), path )
|
||||
{
|
||||
mCapabilities = Collapse | Fertile; // collapse by default to avoid costly population on startup
|
||||
setState( NotPopulated );
|
||||
}
|
||||
|
||||
|
||||
QVector<QgsDataItem *> QgsProjectRootDataItem::createChildren()
|
||||
{
|
||||
QVector<QgsDataItem *> childItems;
|
||||
|
||||
QgsProject p;
|
||||
if ( !p.read( mPath ) )
|
||||
{
|
||||
childItems.append( new QgsErrorItem( nullptr, p.error(), mPath + "/error" ) );
|
||||
return childItems;
|
||||
}
|
||||
|
||||
// recursively create groups and layer items for project's layer tree
|
||||
std::function<void( QgsDataItem *parentItem, QgsLayerTreeGroup *group )> addNodes;
|
||||
addNodes = [this, &addNodes, &childItems]( QgsDataItem * parentItem, QgsLayerTreeGroup * group )
|
||||
{
|
||||
const QList< QgsLayerTreeNode * > children = group->children();
|
||||
for ( QgsLayerTreeNode *child : children )
|
||||
{
|
||||
switch ( child->nodeType() )
|
||||
{
|
||||
case QgsLayerTreeNode::NodeLayer:
|
||||
{
|
||||
if ( QgsLayerTreeLayer *layerNode = qobject_cast< QgsLayerTreeLayer * >( child ) )
|
||||
{
|
||||
QgsMapLayer *layer = layerNode->layer();
|
||||
#if 0 // TODO
|
||||
QString style;
|
||||
if ( layer )
|
||||
{
|
||||
QString errorMsg;
|
||||
QDomDocument doc( QStringLiteral( "qgis" ) );
|
||||
QgsReadWriteContext context;
|
||||
context.setPathResolver( p.pathResolver() );
|
||||
layer->exportNamedStyle( doc, errorMsg, context );
|
||||
style = doc.toString();
|
||||
}
|
||||
#endif
|
||||
|
||||
QgsLayerItem *layerItem = new QgsLayerItem( nullptr, layerNode->name(),
|
||||
layer ? layer->source() : QString(),
|
||||
layer ? layer->source() : QString(),
|
||||
layer ? QgsLayerItem::typeFromMapLayer( layer ) : QgsLayerItem::NoType,
|
||||
layer ? layer->dataProvider()->name() : QString() );
|
||||
layerItem->setState( Populated ); // children are not expected
|
||||
layerItem->setToolTip( layer ? layer->source() : QString() );
|
||||
if ( parentItem == this )
|
||||
childItems << layerItem;
|
||||
else
|
||||
parentItem->addChildItem( layerItem, true );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case QgsLayerTreeNode::NodeGroup:
|
||||
{
|
||||
if ( QgsLayerTreeGroup *groupNode = qobject_cast< QgsLayerTreeGroup * >( child ) )
|
||||
{
|
||||
QgsProjectLayerTreeGroupItem *groupItem = new QgsProjectLayerTreeGroupItem( nullptr, groupNode->name() );
|
||||
addNodes( groupItem, groupNode );
|
||||
groupItem->setState( Populated );
|
||||
if ( parentItem == this )
|
||||
childItems << groupItem;
|
||||
else
|
||||
parentItem->addChildItem( groupItem, true );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
addNodes( this, p.layerTreeRoot() );
|
||||
return childItems;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsProjectLayerTreeGroupItem
|
||||
//
|
||||
|
||||
QgsProjectLayerTreeGroupItem::QgsProjectLayerTreeGroupItem( QgsDataItem *parent, const QString &name )
|
||||
: QgsDataCollectionItem( parent, name )
|
||||
{
|
||||
mIconName = QStringLiteral( "mActionFolder.svg" );
|
||||
mCapabilities = NoCapabilities;
|
||||
setToolTip( name );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsProjectDataItemProvider
|
||||
//
|
||||
|
||||
QString QgsProjectDataItemProvider::name()
|
||||
{
|
||||
return QStringLiteral( "project_item" );
|
||||
}
|
||||
|
||||
int QgsProjectDataItemProvider::capabilities()
|
||||
{
|
||||
return QgsDataProvider::File;
|
||||
}
|
||||
|
||||
QgsDataItem *QgsProjectDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem )
|
||||
{
|
||||
QFileInfo fileInfo( path );
|
||||
if ( fileInfo.suffix().compare( QLatin1String( "qgs" ), Qt::CaseInsensitive ) == 0 || fileInfo.suffix().compare( QLatin1String( "qgz" ), Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
return new QgsProjectRootDataItem( parentItem, path );
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#ifndef QGSAPPBROWSERPROVIDERS_H
|
||||
#define QGSAPPBROWSERPROVIDERS_H
|
||||
|
||||
#include "qgis_app.h"
|
||||
#include "qgsdataitemprovider.h"
|
||||
#include "qgsdataprovider.h"
|
||||
#include "qgscustomdrophandler.h"
|
||||
@ -189,4 +190,48 @@ class QgsStyleXmlDropHandler : public QgsCustomDropHandler
|
||||
bool handleFileDrop( const QString &file ) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom data item for qgs/qgz QGIS project files, with more functionality than default browser project
|
||||
* file handling. Specifically allows browsing of the project's layer structure within the browser
|
||||
*/
|
||||
class APP_EXPORT QgsProjectRootDataItem : public QgsProjectItem
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsProjectRootDataItem, with the specified
|
||||
* project \a path.
|
||||
*/
|
||||
QgsProjectRootDataItem( QgsDataItem *parent, const QString &path );
|
||||
QVector<QgsDataItem *> createChildren() override;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a layer tree group node within a QGIS project file.
|
||||
*/
|
||||
class APP_EXPORT QgsProjectLayerTreeGroupItem : public QgsDataCollectionItem
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsProjectLayerTreeGroupItem, with the specified group \a name.
|
||||
*/
|
||||
QgsProjectLayerTreeGroupItem( QgsDataItem *parent, const QString &name );
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom data item provider for showing qgs/qgz QGIS project files within the browser,
|
||||
* including the ability to browser the whole project's layer tree structure directly
|
||||
* within the browser.
|
||||
*/
|
||||
class APP_EXPORT QgsProjectDataItemProvider : public QgsDataItemProvider
|
||||
{
|
||||
public:
|
||||
QString name() override;
|
||||
int capabilities() override;
|
||||
QgsDataItem *createDataItem( const QString &path, QgsDataItem *parentItem ) override;
|
||||
};
|
||||
|
||||
#endif // QGSAPPBROWSERPROVIDERS_H
|
||||
|
@ -175,7 +175,7 @@ void QgsBrowserDockWidget::itemDoubleClicked( const QModelIndex &index )
|
||||
return;
|
||||
else
|
||||
{
|
||||
// double click not handled by browser model, so use as default view expand behavior
|
||||
// double-click not handled by browser model, so use as default view expand behavior
|
||||
if ( mBrowserView->isExpanded( index ) )
|
||||
mBrowserView->collapse( index );
|
||||
else
|
||||
|
@ -93,6 +93,7 @@ IF (WITH_BINDINGS)
|
||||
ADD_QGIS_TEST(apppythontest testqgisapppython.cpp)
|
||||
ENDIF ()
|
||||
ADD_QGIS_TEST(qgisapp testqgisapp.cpp)
|
||||
ADD_QGIS_TEST(appbrowserproviders testqgsappbrowserproviders.cpp)
|
||||
ADD_QGIS_TEST(qgisappclipboard testqgisappclipboard.cpp)
|
||||
ADD_QGIS_TEST(attributetabletest testqgsattributetable.cpp)
|
||||
ADD_QGIS_TEST(applocatorfilters testqgsapplocatorfilters.cpp)
|
||||
|
137
tests/src/app/testqgsappbrowserproviders.cpp
Normal file
137
tests/src/app/testqgsappbrowserproviders.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
/***************************************************************************
|
||||
testqgsappbrowserproviders.cpp
|
||||
--------------------------------------
|
||||
Date : October 30 2018
|
||||
Copyright : (C) 2018 Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgstest.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
//qgis includes...
|
||||
#include "qgsdataitem.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsdataitemprovider.h"
|
||||
#include "qgsdataitemproviderregistry.h"
|
||||
#include "qgssettings.h"
|
||||
#include "qgsappbrowserproviders.h"
|
||||
|
||||
class TestQgsAppBrowserProviders : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TestQgsAppBrowserProviders();
|
||||
|
||||
private slots:
|
||||
void initTestCase();// will be called before the first testfunction is executed.
|
||||
void cleanupTestCase();// will be called after the last testfunction was executed.
|
||||
void init() {} // will be called before each testfunction is executed.
|
||||
void cleanup() {} // will be called after every testfunction.
|
||||
|
||||
void testProjectItemCreation();
|
||||
|
||||
private:
|
||||
QgsDirectoryItem *mDirItem = nullptr;
|
||||
QString mScanItemsSetting;
|
||||
QString mTestDataDir;
|
||||
};
|
||||
|
||||
TestQgsAppBrowserProviders::TestQgsAppBrowserProviders() = default;
|
||||
|
||||
void TestQgsAppBrowserProviders::initTestCase()
|
||||
{
|
||||
//
|
||||
// Runs once before any tests are run
|
||||
//
|
||||
// init QGIS's paths - true means that all path will be inited from prefix
|
||||
QgsApplication::init();
|
||||
QgsApplication::initQgis();
|
||||
QgsApplication::showSettings();
|
||||
|
||||
QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
|
||||
mTestDataDir = dataDir + '/';
|
||||
|
||||
// Set up the QgsSettings environment
|
||||
QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
|
||||
QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
|
||||
QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
|
||||
// save current scanItemsSetting value
|
||||
QgsSettings settings;
|
||||
mScanItemsSetting = settings.value( QStringLiteral( "/qgis/scanItemsInBrowser2" ), QVariant( "" ) ).toString();
|
||||
|
||||
//create a directory item that will be used in all tests...
|
||||
mDirItem = new QgsDirectoryItem( nullptr, QStringLiteral( "Test" ), TEST_DATA_DIR );
|
||||
}
|
||||
|
||||
void TestQgsAppBrowserProviders::cleanupTestCase()
|
||||
{
|
||||
// restore scanItemsSetting
|
||||
QgsSettings settings;
|
||||
settings.setValue( QStringLiteral( "/qgis/scanItemsInBrowser2" ), mScanItemsSetting );
|
||||
if ( mDirItem )
|
||||
delete mDirItem;
|
||||
|
||||
QgsApplication::exitQgis();
|
||||
}
|
||||
|
||||
|
||||
void TestQgsAppBrowserProviders::testProjectItemCreation()
|
||||
{
|
||||
QgsDirectoryItem *dirItem = new QgsDirectoryItem( nullptr, QStringLiteral( "Test" ), mTestDataDir + QStringLiteral( "qgis_server/" ) );
|
||||
QVector<QgsDataItem *> children = dirItem->createChildren();
|
||||
|
||||
// now, add a specific provider which handles project files
|
||||
QgsApplication::dataItemProviderRegistry()->addProvider( new QgsProjectDataItemProvider() );
|
||||
|
||||
dirItem = new QgsDirectoryItem( nullptr, QStringLiteral( "Test" ), mTestDataDir + QStringLiteral( "qgis_server/" ) );
|
||||
children = dirItem->createChildren();
|
||||
|
||||
for ( QgsDataItem *child : children )
|
||||
{
|
||||
if ( child->type() == QgsDataItem::Project && child->path() == mTestDataDir + QStringLiteral( "qgis_server/test_project.qgs" ) )
|
||||
{
|
||||
child->populate( true );
|
||||
|
||||
QCOMPARE( child->children().count(), 4 );
|
||||
QVERIFY( dynamic_cast< QgsProjectLayerTreeGroupItem * >( child->children().at( 0 ) ) );
|
||||
QCOMPARE( child->children().at( 0 )->name(), QStringLiteral( "groupwithoutshortname" ) );
|
||||
|
||||
QCOMPARE( child->children().at( 0 )->children().count(), 1 );
|
||||
QVERIFY( dynamic_cast< QgsLayerItem * >( child->children().at( 0 )->children().at( 0 ) ) );
|
||||
QCOMPARE( child->children().at( 0 )->children().at( 0 )->name(), QStringLiteral( "testlayer3" ) );
|
||||
|
||||
QVERIFY( dynamic_cast< QgsProjectLayerTreeGroupItem * >( child->children().at( 1 ) ) );
|
||||
QCOMPARE( child->children().at( 1 )->name(), QStringLiteral( "groupwithshortname" ) );
|
||||
|
||||
QCOMPARE( child->children().at( 1 )->children().count(), 1 );
|
||||
QVERIFY( dynamic_cast< QgsLayerItem * >( child->children().at( 1 )->children().at( 0 ) ) );
|
||||
QCOMPARE( child->children().at( 1 )->children().at( 0 )->name(), QStringLiteral( "testlayer2" ) );
|
||||
|
||||
QVERIFY( dynamic_cast< QgsLayerItem * >( child->children().at( 2 ) ) );
|
||||
QCOMPARE( child->children().at( 2 )->name(), QStringLiteral( "testlayer" ) );
|
||||
|
||||
QVERIFY( dynamic_cast< QgsLayerItem * >( child->children().at( 3 ) ) );
|
||||
QCOMPARE( child->children().at( 3 )->name(), QStringLiteral( "testlayer \u00E8\u00E9" ) );
|
||||
|
||||
delete dirItem;
|
||||
return;
|
||||
}
|
||||
}
|
||||
delete dirItem;
|
||||
QVERIFY( false ); // should not be reached
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsAppBrowserProviders )
|
||||
#include "testqgsappbrowserproviders.moc"
|
Loading…
x
Reference in New Issue
Block a user