Skip to content

Commit 9c7dbb9

Browse files
committedJun 3, 2016
[FEATURE] Layer tree embedded widgets
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.
2 parents 45989f7 + 19f83ae commit 9c7dbb9

21 files changed

+1144
-84
lines changed
 

‎python/core/qgsdataitem.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class QgsDataItem : QObject
7575

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

8080
enum State
8181
{

‎python/gui/gui.sip

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@
198198
%Include auth/qgsauthtrustedcasdialog.sip
199199

200200
%Include layertree/qgscustomlayerorderwidget.sip
201+
%Include layertree/qgslayertreeembeddedconfigwidget.sip
202+
%Include layertree/qgslayertreeembeddedwidgetregistry.sip
201203
%Include layertree/qgslayertreemapcanvasbridge.sip
202204
%Include layertree/qgslayertreeview.sip
203205
%Include layertree/qgslayertreeviewdefaultactions.sip
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
/** \ingroup gui
3+
* \class QgsLayerTreeEmbeddedConfigWidget
4+
* A widget to configure layer tree embedded widgets for a particular map layer.
5+
* @note introduced in QGIS 2.16
6+
*/
7+
class QgsLayerTreeEmbeddedConfigWidget : public QWidget
8+
{
9+
%TypeHeaderCode
10+
#include <qgslayertreeembeddedconfigwidget.h>
11+
%End
12+
13+
public:
14+
QgsLayerTreeEmbeddedConfigWidget( QWidget* parent /TransferThis/ = nullptr );
15+
16+
//! Initialize widget with a map layer
17+
void setLayer( QgsMapLayer* layer );
18+
19+
//! Store changes made in the widget to the layer
20+
void applyToLayer();
21+
22+
};
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
2+
/** \ingroup gui
3+
* \class QgsLayerTreeEmbeddedWidgetProvider
4+
* Provider interface to be implemented in order to introduce new kinds of embedded widgets for use in layer tree.
5+
* Embedded widgets are assigned per individual map layers and they are shown before any legend entries.
6+
* @see QgsLayerTreeEmbeddedWidgetRegistry
7+
* @note introduced in QGIS 2.16
8+
*/
9+
class QgsLayerTreeEmbeddedWidgetProvider
10+
{
11+
%TypeHeaderCode
12+
#include <qgslayertreeembeddedwidgetregistry.h>
13+
%End
14+
15+
public:
16+
virtual ~QgsLayerTreeEmbeddedWidgetProvider();
17+
18+
//! Unique name of the provider (among other providers)
19+
virtual QString id() const = 0;
20+
21+
//! Human readable name - may be translatable with tr()
22+
virtual QString name() const = 0;
23+
24+
//! Factory to create widgets. The returned widget is owned by the caller.
25+
//! The widgetIndex argument may be used to identify which widget is being
26+
//! created (useful when using multiple widgets from the same provider for one layer).
27+
virtual QWidget* createWidget( QgsMapLayer* layer, int widgetIndex ) = 0 /Factory/;
28+
29+
//! Whether it makes sense to use this widget for a particular layer
30+
virtual bool supportsLayer( QgsMapLayer* layer ) = 0;
31+
};
32+
33+
/** \ingroup gui
34+
* \class QgsLayerTreeEmbeddedWidgetRegistry
35+
* Registry of widgets that may be embedded into layer tree view.
36+
* Embedded widgets are assigned per individual map layers and they are shown before any legend entries.
37+
* Layer tree must have UseEmbeddedWidgets flag enabled in order to show assigned widgets.
38+
*
39+
* @see QgsLayerTreeEmbeddedWidgetRegistry
40+
* @note introduced in QGIS 2.16
41+
*/
42+
class QgsLayerTreeEmbeddedWidgetRegistry
43+
{
44+
%TypeHeaderCode
45+
#include <qgslayertreeembeddedwidgetregistry.h>
46+
%End
47+
48+
public:
49+
50+
/** Means of accessing canonical single instance */
51+
static QgsLayerTreeEmbeddedWidgetRegistry* instance();
52+
53+
~QgsLayerTreeEmbeddedWidgetRegistry();
54+
55+
/** Return list of all registered providers */
56+
QStringList providers() const;
57+
58+
/** Get provider object from the provider's ID */
59+
QgsLayerTreeEmbeddedWidgetProvider* provider( const QString& providerId ) const;
60+
61+
/** Register a provider, takes ownership of the object.
62+
* Returns true on success, false if the provider is already registered. */
63+
bool addProvider( QgsLayerTreeEmbeddedWidgetProvider* provider /Transfer/ );
64+
65+
/** Unregister a provider, the provider object is deleted.
66+
* Returns true on success, false if the provider was not registered. */
67+
bool removeProvider( const QString& providerId );
68+
69+
protected:
70+
//! Protected constructor - use instance() to access the registry.
71+
QgsLayerTreeEmbeddedWidgetRegistry();
72+
};

‎src/app/qgisapp.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2938,6 +2938,7 @@ void QgisApp::initLayerTreeView()
29382938
model->setFlag( QgsLayerTreeModel::AllowNodeRename );
29392939
model->setFlag( QgsLayerTreeModel::AllowNodeChangeVisibility );
29402940
model->setFlag( QgsLayerTreeModel::ShowLegendAsTree );
2941+
model->setFlag( QgsLayerTreeModel::UseEmbeddedWidgets );
29412942
model->setAutoCollapseLegendNodes( 10 );
29422943

29432944
mLayerTreeView->setModel( model );

‎src/app/qgsrasterlayerproperties.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,12 @@ void QgsRasterLayerProperties::sync()
797797

798798
mLayerLegendUrlLineEdit->setText( mRasterLayer->legendUrl() );
799799
mLayerLegendUrlFormatComboBox->setCurrentIndex( mLayerLegendUrlFormatComboBox->findText( mRasterLayer->legendUrlFormat() ) );
800+
801+
/*
802+
* Legend Tab
803+
*/
804+
mLegendConfigEmbeddedWidget->setLayer( mRasterLayer );
805+
800806
} // QgsRasterLayerProperties::sync()
801807

802808
/*
@@ -806,6 +812,11 @@ void QgsRasterLayerProperties::sync()
806812
*/
807813
void QgsRasterLayerProperties::apply()
808814
{
815+
/*
816+
* Legend Tab
817+
*/
818+
mLegendConfigEmbeddedWidget->applyToLayer();
819+
809820
QgsDebugMsg( "apply processing symbology tab" );
810821
/*
811822
* Symbology Tab

‎src/app/qgsvectorlayerproperties.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
251251
diagLayout->addWidget( diagramPropertiesDialog );
252252
mDiagramFrame->setLayout( diagLayout );
253253

254+
// Legend tab
255+
mLegendConfigEmbeddedWidget->setLayer( mLayer );
256+
254257
// WMS Name as layer short name
255258
mLayerShortNameLineEdit->setText( mLayer->shortName() );
256259
// WMS Name validator
@@ -535,6 +538,9 @@ void QgsVectorLayerProperties::apply()
535538
labelingDialog->writeSettingsToLayer();
536539
}
537540

541+
// apply legend settings
542+
mLegendConfigEmbeddedWidget->applyToLayer();
543+
538544
//
539545
// Set up sql subset query if applicable
540546
//

‎src/core/layertree/qgslayertreemodel.cpp

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,27 @@
3333
#include "qgsvectorlayer.h"
3434

3535

36+
/** In order to support embedded widgets in layer tree view, the model
37+
* generates one placeholder legend node for each embedded widget.
38+
* The placeholder will be replaced by an embedded widget in QgsLayerTreeView
39+
*/
40+
class EmbeddedWidgetLegendNode : public QgsLayerTreeModelLegendNode
Code has comments. Press enter to view.
41+
{
42+
public:
43+
EmbeddedWidgetLegendNode( QgsLayerTreeLayer* nodeL )
44+
: QgsLayerTreeModelLegendNode( nodeL )
45+
{
46+
}
47+
48+
virtual QVariant data( int role ) const override
49+
{
50+
Q_UNUSED( role );
51+
return QVariant();
52+
}
53+
};
54+
55+
56+
3657
QgsLayerTreeModel::QgsLayerTreeModel( QgsLayerTreeGroup* rootNode, QObject *parent )
3758
: QAbstractItemModel( parent )
3859
, mRootNode( rootNode )
@@ -1169,9 +1190,20 @@ void QgsLayerTreeModel::addLegendToLayer( QgsLayerTreeLayer* nodeL )
11691190
// apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
11701191
QgsMapLayerLegendUtils::applyLayerNodeProperties( nodeL, lstNew );
11711192

1193+
if ( testFlag( UseEmbeddedWidgets ) )
1194+
{
1195+
// generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1196+
int widgetsCount = ml->customProperty( "embeddedWidgets/count", 0 ).toInt();
1197+
while ( widgetsCount > 0 )
1198+
{
1199+
lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1200+
--widgetsCount;
1201+
}
1202+
}
1203+
11721204
QList<QgsLayerTreeModelLegendNode*> filteredLstNew = filterLegendNodes( lstNew );
11731205

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

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

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

1193-
if ( ! isEmbedded ) beginInsertRows( node2index( nodeL ), 0, count - 1 );
1225+
if ( ! hasOnlyEmbedded ) beginInsertRows( node2index( nodeL ), 0, count - 1 );
11941226

11951227
mLegend[nodeL] = data;
11961228

1197-
if ( ! isEmbedded ) endInsertRows();
1229+
if ( ! hasOnlyEmbedded ) endInsertRows();
11981230

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

12921324
int QgsLayerTreeModel::legendRootRowCount( QgsLayerTreeLayer* nL ) const
12931325
{
1294-
if ( legendEmbeddedInParent( nL ) )
1295-
return 0;
1296-
12971326
if ( !mLegend.contains( nL ) )
12981327
return 0;
12991328

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

1304-
return data.activeNodes.count();
1333+
int count = data.activeNodes.count();
1334+
1335+
if ( legendEmbeddedInParent( nL ) )
1336+
count--; // one item less -- it is embedded in parent
1337+
1338+
return count;
13051339
}
13061340

13071341

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

13661400
bool QgsLayerTreeModel::legendEmbeddedInParent( QgsLayerTreeLayer* nodeLayer ) const
13671401
{
1402+
return legendNodeEmbeddedInParent( nodeLayer );
1403+
}
1404+
1405+
QgsLayerTreeModelLegendNode* QgsLayerTreeModel::legendNodeEmbeddedInParent( QgsLayerTreeLayer* nodeLayer ) const
1406+
{
1407+
// legend node embedded in parent does not have to be the first one...
1408+
// there could be extra legend nodes generated for embedded widgets
13681409
const LayerLegendData& data = mLegend[nodeLayer];
1369-
return data.activeNodes.count() == 1 && data.activeNodes[0]->isEmbeddedInParent();
1410+
Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, data.activeNodes )
1411+
{
1412+
if ( legendNode->isEmbeddedInParent() )
1413+
return legendNode;
1414+
}
1415+
return nullptr;
13701416
}
13711417

13721418

13731419
QIcon QgsLayerTreeModel::legendIconEmbeddedInParent( QgsLayerTreeLayer* nodeLayer ) const
13741420
{
1375-
return QIcon( qvariant_cast<QPixmap>( mLegend[nodeLayer].activeNodes[0]->data( Qt::DecorationRole ) ) );
1421+
QgsLayerTreeModelLegendNode* legendNode = legendNodeEmbeddedInParent( nodeLayer );
1422+
if ( !legendNode )
1423+
return QIcon();
1424+
return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
13761425
}
13771426

13781427

‎src/core/layertree/qgslayertreemodel.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel
8080
ShowRasterPreviewIcon = 0x0002, //!< Will use real preview of raster layer as icon (may be slow)
8181
ShowLegendAsTree = 0x0004, //!< For legends that support it, will show them in a tree instead of a list (needs also ShowLegend). Added in 2.8
8282
DeferredLegendInvalidation = 0x0008, //!< defer legend model invalidation
83+
UseEmbeddedWidgets = 0x0010, //!< Layer nodes may optionally include extra embedded widgets (if used in QgsLayerTreeView). Added in 2.16
8384

8485
// behavioral flags
8586
AllowNodeReorder = 0x1000, //!< Allow reordering with drag'n'drop
@@ -276,6 +277,8 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel
276277
QVariant legendNodeData( QgsLayerTreeModelLegendNode* node, int role ) const;
277278
Qt::ItemFlags legendNodeFlags( QgsLayerTreeModelLegendNode* node ) const;
278279
bool legendEmbeddedInParent( QgsLayerTreeLayer* nodeLayer ) const;
280+
/** Return legend node that may be embbeded in parent (i.e. its icon will be used for layer's icon). */
281+
QgsLayerTreeModelLegendNode* legendNodeEmbeddedInParent( QgsLayerTreeLayer* nodeLayer ) const;
279282
QIcon legendIconEmbeddedInParent( QgsLayerTreeLayer* nodeLayer ) const;
280283
void legendCleanup();
281284
void legendInvalidateMapBasedData();

‎src/gui/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ SET(QGIS_GUI_SRCS
154154
editorwidgets/qgsvaluerelationwidgetfactory.cpp
155155

156156
layertree/qgscustomlayerorderwidget.cpp
157+
layertree/qgslayertreeembeddedconfigwidget.cpp
158+
layertree/qgslayertreeembeddedwidgetregistry.cpp
159+
layertree/qgslayertreeembeddedwidgetsimpl.cpp
157160
layertree/qgslayertreemapcanvasbridge.cpp
158161
layertree/qgslayertreeview.cpp
159162
layertree/qgslayertreeviewdefaultactions.cpp
@@ -584,6 +587,8 @@ SET(QGIS_GUI_MOC_HDRS
584587
editorwidgets/qgsvaluerelationwidgetwrapper.h
585588

586589
layertree/qgscustomlayerorderwidget.h
590+
layertree/qgslayertreeembeddedconfigwidget.h
591+
layertree/qgslayertreeembeddedwidgetsimpl.h
587592
layertree/qgslayertreemapcanvasbridge.h
588593
layertree/qgslayertreeview.h
589594
layertree/qgslayertreeviewdefaultactions.h
@@ -679,6 +684,9 @@ SET(QGIS_GUI_HDRS
679684
editorwidgets/qgsvaluemapwidgetfactory.h
680685
editorwidgets/qgsvaluerelationwidgetfactory.h
681686

687+
layertree/qgslayertreeembeddedconfigwidget.h
688+
layertree/qgslayertreeembeddedwidgetregistry.h
689+
682690
raster/qgsrasterrendererwidget.h
683691
)
684692

3 commit comments

Comments
 (3)

nirvn commented on Jun 3, 2016

@nirvn
Contributor

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

wonder-sk commented on Jun 3, 2016

@wonder-sk
MemberAuthor

I will have a look...

timlinux commented on Jun 7, 2016

@timlinux
Member

Yay! 👍

Please sign in to comment.