Skip to content

Commit

Permalink
[FEATURE] Layer tree embedded widgets
Browse files Browse the repository at this point in the history
This allows definition of widgets embedded into layer tree for individual layers in the layer properties dialog (in new Legend tab). The idea is to have a way to quickly access to some actions that are often used with a layer.

The implementation comes with transparency widget, in the future there may be more standard widgets coming, e.g. to setup filtering, selection, style or other stuff. The API allows plugins to register their own widgets, which will be useful for various domain specific plugins to assign custom widgets to layers they manage.
  • Loading branch information
wonder-sk committed Jun 3, 2016
2 parents 45989f7 + 19f83ae commit 9c7dbb9
Show file tree
Hide file tree
Showing 21 changed files with 1,144 additions and 84 deletions.
2 changes: 1 addition & 1 deletion python/core/qgsdataitem.sip
Expand Up @@ -75,7 +75,7 @@ class QgsDataItem : QObject

/** Create children. Children are not expected to have parent set.
* This method MUST BE THREAD SAFE. */
virtual QVector<QgsDataItem*> createChildren();
virtual QVector<QgsDataItem*> createChildren() /Factory/;

enum State
{
Expand Down
2 changes: 2 additions & 0 deletions python/gui/gui.sip
Expand Up @@ -198,6 +198,8 @@
%Include auth/qgsauthtrustedcasdialog.sip

%Include layertree/qgscustomlayerorderwidget.sip
%Include layertree/qgslayertreeembeddedconfigwidget.sip
%Include layertree/qgslayertreeembeddedwidgetregistry.sip
%Include layertree/qgslayertreemapcanvasbridge.sip
%Include layertree/qgslayertreeview.sip
%Include layertree/qgslayertreeviewdefaultactions.sip
Expand Down
22 changes: 22 additions & 0 deletions python/gui/layertree/qgslayertreeembeddedconfigwidget.sip
@@ -0,0 +1,22 @@

/** \ingroup gui
* \class QgsLayerTreeEmbeddedConfigWidget
* A widget to configure layer tree embedded widgets for a particular map layer.
* @note introduced in QGIS 2.16
*/
class QgsLayerTreeEmbeddedConfigWidget : public QWidget
{
%TypeHeaderCode
#include <qgslayertreeembeddedconfigwidget.h>
%End

public:
QgsLayerTreeEmbeddedConfigWidget( QWidget* parent /TransferThis/ = nullptr );

//! Initialize widget with a map layer
void setLayer( QgsMapLayer* layer );

//! Store changes made in the widget to the layer
void applyToLayer();

};
72 changes: 72 additions & 0 deletions python/gui/layertree/qgslayertreeembeddedwidgetregistry.sip
@@ -0,0 +1,72 @@

/** \ingroup gui
* \class QgsLayerTreeEmbeddedWidgetProvider
* Provider interface to be implemented in order to introduce new kinds of embedded widgets for use in layer tree.
* Embedded widgets are assigned per individual map layers and they are shown before any legend entries.
* @see QgsLayerTreeEmbeddedWidgetRegistry
* @note introduced in QGIS 2.16
*/
class QgsLayerTreeEmbeddedWidgetProvider
{
%TypeHeaderCode
#include <qgslayertreeembeddedwidgetregistry.h>
%End

public:
virtual ~QgsLayerTreeEmbeddedWidgetProvider();

//! Unique name of the provider (among other providers)
virtual QString id() const = 0;

//! Human readable name - may be translatable with tr()
virtual QString name() const = 0;

//! Factory to create widgets. The returned widget is owned by the caller.
//! The widgetIndex argument may be used to identify which widget is being
//! created (useful when using multiple widgets from the same provider for one layer).
virtual QWidget* createWidget( QgsMapLayer* layer, int widgetIndex ) = 0 /Factory/;

//! Whether it makes sense to use this widget for a particular layer
virtual bool supportsLayer( QgsMapLayer* layer ) = 0;
};

/** \ingroup gui
* \class QgsLayerTreeEmbeddedWidgetRegistry
* Registry of widgets that may be embedded into layer tree view.
* Embedded widgets are assigned per individual map layers and they are shown before any legend entries.
* Layer tree must have UseEmbeddedWidgets flag enabled in order to show assigned widgets.
*
* @see QgsLayerTreeEmbeddedWidgetRegistry
* @note introduced in QGIS 2.16
*/
class QgsLayerTreeEmbeddedWidgetRegistry
{
%TypeHeaderCode
#include <qgslayertreeembeddedwidgetregistry.h>
%End

public:

/** Means of accessing canonical single instance */
static QgsLayerTreeEmbeddedWidgetRegistry* instance();

~QgsLayerTreeEmbeddedWidgetRegistry();

/** Return list of all registered providers */
QStringList providers() const;

/** Get provider object from the provider's ID */
QgsLayerTreeEmbeddedWidgetProvider* provider( const QString& providerId ) const;

/** Register a provider, takes ownership of the object.
* Returns true on success, false if the provider is already registered. */
bool addProvider( QgsLayerTreeEmbeddedWidgetProvider* provider /Transfer/ );

/** Unregister a provider, the provider object is deleted.
* Returns true on success, false if the provider was not registered. */
bool removeProvider( const QString& providerId );

protected:
//! Protected constructor - use instance() to access the registry.
QgsLayerTreeEmbeddedWidgetRegistry();
};
1 change: 1 addition & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -2938,6 +2938,7 @@ void QgisApp::initLayerTreeView()
model->setFlag( QgsLayerTreeModel::AllowNodeRename );
model->setFlag( QgsLayerTreeModel::AllowNodeChangeVisibility );
model->setFlag( QgsLayerTreeModel::ShowLegendAsTree );
model->setFlag( QgsLayerTreeModel::UseEmbeddedWidgets );
model->setAutoCollapseLegendNodes( 10 );

mLayerTreeView->setModel( model );
Expand Down
11 changes: 11 additions & 0 deletions src/app/qgsrasterlayerproperties.cpp
Expand Up @@ -797,6 +797,12 @@ void QgsRasterLayerProperties::sync()

mLayerLegendUrlLineEdit->setText( mRasterLayer->legendUrl() );
mLayerLegendUrlFormatComboBox->setCurrentIndex( mLayerLegendUrlFormatComboBox->findText( mRasterLayer->legendUrlFormat() ) );

/*
* Legend Tab
*/
mLegendConfigEmbeddedWidget->setLayer( mRasterLayer );

} // QgsRasterLayerProperties::sync()

/*
Expand All @@ -806,6 +812,11 @@ void QgsRasterLayerProperties::sync()
*/
void QgsRasterLayerProperties::apply()
{
/*
* Legend Tab
*/
mLegendConfigEmbeddedWidget->applyToLayer();

QgsDebugMsg( "apply processing symbology tab" );
/*
* Symbology Tab
Expand Down
6 changes: 6 additions & 0 deletions src/app/qgsvectorlayerproperties.cpp
Expand Up @@ -251,6 +251,9 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
diagLayout->addWidget( diagramPropertiesDialog );
mDiagramFrame->setLayout( diagLayout );

// Legend tab
mLegendConfigEmbeddedWidget->setLayer( mLayer );

// WMS Name as layer short name
mLayerShortNameLineEdit->setText( mLayer->shortName() );
// WMS Name validator
Expand Down Expand Up @@ -535,6 +538,9 @@ void QgsVectorLayerProperties::apply()
labelingDialog->writeSettingsToLayer();
}

// apply legend settings
mLegendConfigEmbeddedWidget->applyToLayer();

//
// Set up sql subset query if applicable
//
Expand Down
67 changes: 58 additions & 9 deletions src/core/layertree/qgslayertreemodel.cpp
Expand Up @@ -33,6 +33,27 @@
#include "qgsvectorlayer.h"


/** In order to support embedded widgets in layer tree view, the model
* generates one placeholder legend node for each embedded widget.
* The placeholder will be replaced by an embedded widget in QgsLayerTreeView
*/
class EmbeddedWidgetLegendNode : public QgsLayerTreeModelLegendNode

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Jun 3, 2016

Member

Some bindings are missing apparently.

https://travis-ci.org/qgis/QGIS/jobs/135002563#L2022-L2025

This comment has been minimized.

Copy link
@wonder-sk

wonder-sk Jun 3, 2016

Author Member

All of those are private classes, not exported outside of the library... why are they reported?

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Jun 3, 2016

Member

Not sure...

ping @nyalldawson

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Jun 3, 2016

Member

IIRC

//! @note not available in python bindings

will remove it from the bindings check

/// @cond

and

/// @endcond

will remove it from the API docs and I think also from the bindings check

{
public:
EmbeddedWidgetLegendNode( QgsLayerTreeLayer* nodeL )
: QgsLayerTreeModelLegendNode( nodeL )
{
}

virtual QVariant data( int role ) const override
{
Q_UNUSED( role );
return QVariant();
}
};



QgsLayerTreeModel::QgsLayerTreeModel( QgsLayerTreeGroup* rootNode, QObject *parent )
: QAbstractItemModel( parent )
, mRootNode( rootNode )
Expand Down Expand Up @@ -1169,9 +1190,20 @@ void QgsLayerTreeModel::addLegendToLayer( QgsLayerTreeLayer* nodeL )
// apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
QgsMapLayerLegendUtils::applyLayerNodeProperties( nodeL, lstNew );

if ( testFlag( UseEmbeddedWidgets ) )
{
// generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
int widgetsCount = ml->customProperty( "embeddedWidgets/count", 0 ).toInt();
while ( widgetsCount > 0 )
{
lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
--widgetsCount;
}
}

QList<QgsLayerTreeModelLegendNode*> filteredLstNew = filterLegendNodes( lstNew );

bool isEmbedded = filteredLstNew.count() == 1 && filteredLstNew[0]->isEmbeddedInParent();
bool hasOnlyEmbedded = filteredLstNew.count() == 1 && filteredLstNew[0]->isEmbeddedInParent();

Q_FOREACH ( QgsLayerTreeModelLegendNode* n, lstNew )
{
Expand All @@ -1190,11 +1222,11 @@ void QgsLayerTreeModel::addLegendToLayer( QgsLayerTreeLayer* nodeL )

int count = data.tree ? data.tree->children[nullptr].count() : filteredLstNew.count();

if ( ! isEmbedded ) beginInsertRows( node2index( nodeL ), 0, count - 1 );
if ( ! hasOnlyEmbedded ) beginInsertRows( node2index( nodeL ), 0, count - 1 );

mLegend[nodeL] = data;

if ( ! isEmbedded ) endInsertRows();
if ( ! hasOnlyEmbedded ) endInsertRows();

if ( hasStyleOverride )
ml->styleManager()->restoreOverrideStyle();
Expand Down Expand Up @@ -1291,17 +1323,19 @@ int QgsLayerTreeModel::legendNodeRowCount( QgsLayerTreeModelLegendNode* node ) c

int QgsLayerTreeModel::legendRootRowCount( QgsLayerTreeLayer* nL ) const
{
if ( legendEmbeddedInParent( nL ) )
return 0;

if ( !mLegend.contains( nL ) )
return 0;

const LayerLegendData& data = mLegend[nL];
if ( data.tree )
return data.tree->children[nullptr].count();

return data.activeNodes.count();
int count = data.activeNodes.count();

if ( legendEmbeddedInParent( nL ) )
count--; // one item less -- it is embedded in parent

return count;
}


Expand Down Expand Up @@ -1365,14 +1399,29 @@ Qt::ItemFlags QgsLayerTreeModel::legendNodeFlags( QgsLayerTreeModelLegendNode* n

bool QgsLayerTreeModel::legendEmbeddedInParent( QgsLayerTreeLayer* nodeLayer ) const
{
return legendNodeEmbeddedInParent( nodeLayer );
}

QgsLayerTreeModelLegendNode* QgsLayerTreeModel::legendNodeEmbeddedInParent( QgsLayerTreeLayer* nodeLayer ) const
{
// legend node embedded in parent does not have to be the first one...
// there could be extra legend nodes generated for embedded widgets
const LayerLegendData& data = mLegend[nodeLayer];
return data.activeNodes.count() == 1 && data.activeNodes[0]->isEmbeddedInParent();
Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, data.activeNodes )
{
if ( legendNode->isEmbeddedInParent() )
return legendNode;
}
return nullptr;
}


QIcon QgsLayerTreeModel::legendIconEmbeddedInParent( QgsLayerTreeLayer* nodeLayer ) const
{
return QIcon( qvariant_cast<QPixmap>( mLegend[nodeLayer].activeNodes[0]->data( Qt::DecorationRole ) ) );
QgsLayerTreeModelLegendNode* legendNode = legendNodeEmbeddedInParent( nodeLayer );
if ( !legendNode )
return QIcon();
return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
}


Expand Down
3 changes: 3 additions & 0 deletions src/core/layertree/qgslayertreemodel.h
Expand Up @@ -80,6 +80,7 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel
ShowRasterPreviewIcon = 0x0002, //!< Will use real preview of raster layer as icon (may be slow)
ShowLegendAsTree = 0x0004, //!< For legends that support it, will show them in a tree instead of a list (needs also ShowLegend). Added in 2.8
DeferredLegendInvalidation = 0x0008, //!< defer legend model invalidation
UseEmbeddedWidgets = 0x0010, //!< Layer nodes may optionally include extra embedded widgets (if used in QgsLayerTreeView). Added in 2.16

// behavioral flags
AllowNodeReorder = 0x1000, //!< Allow reordering with drag'n'drop
Expand Down Expand Up @@ -276,6 +277,8 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel
QVariant legendNodeData( QgsLayerTreeModelLegendNode* node, int role ) const;
Qt::ItemFlags legendNodeFlags( QgsLayerTreeModelLegendNode* node ) const;
bool legendEmbeddedInParent( QgsLayerTreeLayer* nodeLayer ) const;
/** Return legend node that may be embbeded in parent (i.e. its icon will be used for layer's icon). */
QgsLayerTreeModelLegendNode* legendNodeEmbeddedInParent( QgsLayerTreeLayer* nodeLayer ) const;
QIcon legendIconEmbeddedInParent( QgsLayerTreeLayer* nodeLayer ) const;
void legendCleanup();
void legendInvalidateMapBasedData();
Expand Down
8 changes: 8 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -154,6 +154,9 @@ SET(QGIS_GUI_SRCS
editorwidgets/qgsvaluerelationwidgetfactory.cpp

layertree/qgscustomlayerorderwidget.cpp
layertree/qgslayertreeembeddedconfigwidget.cpp
layertree/qgslayertreeembeddedwidgetregistry.cpp
layertree/qgslayertreeembeddedwidgetsimpl.cpp
layertree/qgslayertreemapcanvasbridge.cpp
layertree/qgslayertreeview.cpp
layertree/qgslayertreeviewdefaultactions.cpp
Expand Down Expand Up @@ -584,6 +587,8 @@ SET(QGIS_GUI_MOC_HDRS
editorwidgets/qgsvaluerelationwidgetwrapper.h

layertree/qgscustomlayerorderwidget.h
layertree/qgslayertreeembeddedconfigwidget.h
layertree/qgslayertreeembeddedwidgetsimpl.h
layertree/qgslayertreemapcanvasbridge.h
layertree/qgslayertreeview.h
layertree/qgslayertreeviewdefaultactions.h
Expand Down Expand Up @@ -679,6 +684,9 @@ SET(QGIS_GUI_HDRS
editorwidgets/qgsvaluemapwidgetfactory.h
editorwidgets/qgsvaluerelationwidgetfactory.h

layertree/qgslayertreeembeddedconfigwidget.h
layertree/qgslayertreeembeddedwidgetregistry.h

raster/qgsrasterrendererwidget.h
)

Expand Down

3 comments on commit 9c7dbb9

@nirvn
Copy link
Contributor

@nirvn nirvn commented on 9c7dbb9 Jun 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wonder-sk , nice! is there any way we can get the style dock transparency slider to synchronize with the legend transparency slider?

@wonder-sk
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will have a look...

@timlinux
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yay! 👍

Please sign in to comment.