Integration of embedded widgets into layer tree + config GUI

This commit is contained in:
Martin Dobias 2016-06-01 12:40:00 +02:00
parent 2b1560c5be
commit ea2395dd92
14 changed files with 547 additions and 1 deletions

View File

@ -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
{

View File

@ -2815,6 +2815,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 );

View File

@ -237,6 +237,9 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu()
duplicateLayersAction->setEnabled( false );
}
QAction* embeddedAction = menu->addAction( tr( "Embedded widgets..."), this, SLOT( editEmbeddedWidgets() ) );
embeddedAction->setProperty( "layerId", layer->id() );
addCustomLayerActions( menu, layer );
if ( layer && QgsProject::instance()->layerIsEmbedded( layer->id() ).isEmpty() )
@ -555,3 +558,26 @@ void QgsAppLayerTreeViewMenuProvider::setSymbolLegendNodeColor( const QColor &co
newSymbol->setColor( color );
node->setSymbol( newSymbol );
}
#include "qgslayertreeembeddedconfigwidget.h"
void QgsAppLayerTreeViewMenuProvider::editEmbeddedWidgets()
{
QAction* action = qobject_cast< QAction*>( sender() );
if ( !action )
return;
QString layerId = action->property( "layerId" ).toString();
QgsLayerTreeLayer* nodeLayer = QgsProject::instance()->layerTreeRoot()->findLayer( layerId );
if ( !nodeLayer )
return;
QDialog d;
QgsLayerTreeEmbeddedConfigWidget* widget = new QgsLayerTreeEmbeddedConfigWidget(nodeLayer, &d);
QLayout* l = new QVBoxLayout(&d);
l->addWidget(widget);
d.setLayout(l);
d.exec();
mView->layerTreeModel()->refreshLayerLegend( nodeLayer );
}

View File

@ -69,6 +69,7 @@ class QgsAppLayerTreeViewMenuProvider : public QObject, public QgsLayerTreeViewM
void setVectorSymbolColor( const QColor &color );
void editSymbolLegendNodeSymbol();
void setSymbolLegendNodeColor( const QColor &color );
void editEmbeddedWidgets();
};
#endif // QGSAPPLAYERTREEVIEWMENUPROVIDER_H

View File

@ -1149,6 +1149,21 @@ void QgsLayerTreeModel::removeLegendFromLayer( QgsLayerTreeLayer* nodeLayer )
}
}
class EmbeddedWidgetLegendNode : public QgsLayerTreeModelLegendNode
{
public:
EmbeddedWidgetLegendNode( QgsLayerTreeLayer* nodeL )
: QgsLayerTreeModelLegendNode( nodeL )
{
}
virtual QVariant data( int role ) const override
{
Q_UNUSED( role );
return QVariant();
}
};
void QgsLayerTreeModel::addLegendToLayer( QgsLayerTreeLayer* nodeL )
{
@ -1169,6 +1184,16 @@ 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 ) )
{
int widgetsCount = nodeL->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();

View File

@ -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

View File

@ -58,6 +58,7 @@ class QDomElement;
* - "embedded_project" - path to the external project (embedded root node only)
* - "legend/..." - properties for legend appearance customization
* - "expandedLegendNodes" - list of layer's legend nodes' rules in expanded state
* - "embeddedWidgets/..." - properties for embedded widgets (layer node only)
*
* @see also QgsLayerTree, QgsLayerTreeLayer, QgsLayerTreeGroup
* @note added in 2.4

View File

@ -153,6 +153,8 @@ SET(QGIS_GUI_SRCS
editorwidgets/qgsvaluerelationwidgetfactory.cpp
layertree/qgscustomlayerorderwidget.cpp
layertree/qgslayertreeembeddedconfigwidget.cpp
layertree/qgslayertreeembeddedwidgetregistry.cpp
layertree/qgslayertreemapcanvasbridge.cpp
layertree/qgslayertreeview.cpp
layertree/qgslayertreeviewdefaultactions.cpp
@ -574,6 +576,8 @@ SET(QGIS_GUI_MOC_HDRS
editorwidgets/qgsvaluerelationwidgetwrapper.h
layertree/qgscustomlayerorderwidget.h
layertree/qgslayertreeembeddedconfigwidget.h
layertree/qgslayertreeembeddedwidgetregistry.h
layertree/qgslayertreemapcanvasbridge.h
layertree/qgslayertreeview.h
layertree/qgslayertreeviewdefaultactions.h
@ -669,6 +673,9 @@ SET(QGIS_GUI_HDRS
editorwidgets/qgsvaluemapwidgetfactory.h
editorwidgets/qgsvaluerelationwidgetfactory.h
layertree/qgslayertreeembeddedconfigwidget.h
layertree/qgslayertreeembeddedwidgetregistry.h
raster/qgsrasterrendererwidget.h
)

View File

@ -0,0 +1,87 @@
#include "qgslayertreeembeddedconfigwidget.h"
#include "qgslayertree.h"
#include "qgslayertreeembeddedwidgetregistry.h"
#include <QStringListModel>
QgsLayerTreeEmbeddedConfigWidget::QgsLayerTreeEmbeddedConfigWidget( QgsLayerTreeLayer* nodeLayer, QWidget* parent )
: QWidget( parent )
, mNodeLayer( nodeLayer )
{
setupUi( this );
connect( mBtnAdd, SIGNAL(clicked(bool)), this, SLOT(onAddClicked()));
connect( mBtnRemove, SIGNAL(clicked(bool)), this, SLOT(onRemoveClicked()));
// populate available
QStringList lst;
Q_FOREACH ( const QString& providerId, QgsLayerTreeEmbeddedWidgetRegistry::instance()->providers() )
{
QgsLayerTreeEmbeddedWidgetProvider* provider = QgsLayerTreeEmbeddedWidgetRegistry::instance()->provider( providerId );
lst << provider->id();
}
mListAvailable->setModel( new QStringListModel( lst, this ) );
// populate used
QStringList lstUsed;
int widgetsCount = nodeLayer->customProperty( "embeddedWidgets/count", 0 ).toInt();
for ( int i = 0; i < widgetsCount; ++i )
{
QString providerId = nodeLayer->customProperty( QString( "embeddedWidgets/%1/id" ).arg( i ) ).toString();
if ( /*QgsLayerTreeEmbeddedWidgetProvider* provider =*/ QgsLayerTreeEmbeddedWidgetRegistry::instance()->provider( providerId ) )
{
lstUsed << providerId;
}
}
mListUsed->setModel( new QStringListModel( lstUsed, this ) );
}
void QgsLayerTreeEmbeddedConfigWidget::onAddClicked()
{
if (!mListAvailable->currentIndex().isValid())
return;
QString providerId = mListAvailable->model()->data( mListAvailable->currentIndex() ).toString();
if (QStringListModel* model = qobject_cast<QStringListModel*>(mListUsed->model()))
{
int row = model->rowCount();
model->insertRow( row );
model->setData( model->index(row, 0), providerId );
//QStringList lst = model->stringList();
//lst << providerId;
//model->setStringList( lst );
}
updateCustomProperties();
}
void QgsLayerTreeEmbeddedConfigWidget::onRemoveClicked()
{
if (!mListUsed->currentIndex().isValid())
return;
int row = mListUsed->currentIndex().row();
mListUsed->model()->removeRow( row );
updateCustomProperties();
}
void QgsLayerTreeEmbeddedConfigWidget::updateCustomProperties()
{
// clear old properties
int widgetsCount = mNodeLayer->customProperty( "embeddedWidgets/count", 0 ).toInt();
for ( int i = 0; i < widgetsCount; ++i )
{
mNodeLayer->removeCustomProperty( QString( "embeddedWidgets/%1/id" ).arg( i ) );
}
// setup new properties
int newCount = mListUsed->model()->rowCount();
mNodeLayer->setCustomProperty( "embeddedWidgets/count", newCount );
for ( int i = 0; i < newCount; ++i )
{
QString providerId = mListUsed->model()->data( mListUsed->model()->index(i, 0) ).toString();
mNodeLayer->setCustomProperty( QString( "embeddedWidgets/%1/id" ).arg( i ), providerId );
}
}

View File

@ -0,0 +1,25 @@
#ifndef QGSLAYERTREEEMBEDDEDCONFIGWIDGET_H
#define QGSLAYERTREEEMBEDDEDCONFIGWIDGET_H
#include "ui_qgslayertreeembeddedconfigwidget.h"
class QgsLayerTreeLayer;
class GUI_EXPORT QgsLayerTreeEmbeddedConfigWidget : public QWidget, protected Ui::QgsLayerTreeEmbeddedConfigWidget
{
Q_OBJECT
public:
QgsLayerTreeEmbeddedConfigWidget( QgsLayerTreeLayer* nodeLayer, QWidget* parent = nullptr );
private slots:
void onAddClicked();
void onRemoveClicked();
private:
void updateCustomProperties();
private:
QgsLayerTreeLayer* mNodeLayer;
};
#endif // QGSLAYERTREEEMBEDDEDCONFIGWIDGET_H

View File

@ -0,0 +1,155 @@
/***************************************************************************
qgslayertreeembeddedwidgetregistry.cpp
--------------------------------------
Date : May 2016
Copyright : (C) 2016 by Martin Dobias
Email : wonder dot sk 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 "qgslayertreeembeddedwidgetregistry.h"
#include "qgsmaplayer.h"
class TransparencyWidgetProvider : public QgsLayerTreeEmbeddedWidgetProvider
{
public:
virtual QString id() const override
{
return "transparency";
}
virtual TransparencyWidget* createWidget( QgsMapLayer* layer, QMap<QString, QString> properties ) override
{
Q_UNUSED( properties );
return new TransparencyWidget( layer );
}
virtual bool supportsLayer( QgsMapLayer *layer ) override
{
return layer->type() == QgsMapLayer::VectorLayer || layer->type() == QgsMapLayer::RasterLayer;
}
};
/////
QgsLayerTreeEmbeddedWidgetRegistry*QgsLayerTreeEmbeddedWidgetRegistry::instance()
{
static QgsLayerTreeEmbeddedWidgetRegistry* sInstance( new QgsLayerTreeEmbeddedWidgetRegistry() );
return sInstance;
}
QgsLayerTreeEmbeddedWidgetRegistry::~QgsLayerTreeEmbeddedWidgetRegistry()
{
Q_FOREACH ( QgsLayerTreeEmbeddedWidgetProvider* provider, mProviders )
{
removeProvider( provider->id() );
}
}
QStringList QgsLayerTreeEmbeddedWidgetRegistry::providers() const
{
return mProviders.keys();
}
QgsLayerTreeEmbeddedWidgetProvider*QgsLayerTreeEmbeddedWidgetRegistry::provider( const QString& providerId ) const
{
return mProviders.value( providerId );
}
bool QgsLayerTreeEmbeddedWidgetRegistry::addProvider( QgsLayerTreeEmbeddedWidgetProvider* provider )
{
if ( mProviders.contains( provider->id() ) )
return false;
mProviders.insert( provider->id(), provider );
return true;
}
bool QgsLayerTreeEmbeddedWidgetRegistry::removeProvider( const QString& providerId )
{
if ( !mProviders.contains( providerId ) )
return false;
delete mProviders.take( providerId );
return true;
}
QgsLayerTreeEmbeddedWidgetRegistry::QgsLayerTreeEmbeddedWidgetRegistry()
{
addProvider( new TransparencyWidgetProvider() );
}
/////
#include <QLabel>
#include <QSlider>
#include <QHBoxLayout>
#include "qgsrasterlayer.h"
#include "qgsrasterrenderer.h"
#include "qgsvectorlayer.h"
TransparencyWidget::TransparencyWidget( QgsMapLayer* layer )
: mLayer( layer )
{
setAutoFillBackground( true ); // override the content from model
QLabel* l = new QLabel( "Transparency", this );
mSlider = new QSlider( Qt::Horizontal, this );
mSlider->setRange( 0, 100 );
QHBoxLayout* lay = new QHBoxLayout();
lay->addWidget( l );
lay->addWidget( mSlider );
setLayout( lay );
connect( mSlider, SIGNAL( valueChanged( int ) ), this, SLOT( sliderValueChanged( int ) ) );
// init from layer
if ( mLayer->type() == QgsMapLayer::VectorLayer )
{
mSlider->setValue( qobject_cast<QgsVectorLayer*>( mLayer )->layerTransparency() );
connect( mLayer, SIGNAL( layerTransparencyChanged( int ) ), this, SLOT( layerTrChanged() ) );
}
else if ( mLayer->type() == QgsMapLayer::RasterLayer )
{
mSlider->setValue( 100 - qobject_cast<QgsRasterLayer*>( mLayer )->renderer()->opacity() * 100 );
// TODO: there is no signal for raster layers
}
}
QSize TransparencyWidget::sizeHint() const
{
return QWidget::sizeHint();
//return QSize(200,200); // horizontal seems ignored, vertical is used for spacing
}
void TransparencyWidget::sliderValueChanged( int value )
{
if ( mLayer->type() == QgsMapLayer::VectorLayer )
{
qobject_cast<QgsVectorLayer*>( mLayer )->setLayerTransparency( value );
}
else if ( mLayer->type() == QgsMapLayer::RasterLayer )
{
qobject_cast<QgsRasterLayer*>( mLayer )->renderer()->setOpacity( 1 - value / 100. );
}
mLayer->triggerRepaint();
}
void TransparencyWidget::layerTrChanged()
{
mSlider->blockSignals( true );
mSlider->setValue( qobject_cast<QgsVectorLayer*>( mLayer )->layerTransparency() );
mSlider->blockSignals( false );
}

View File

@ -0,0 +1,108 @@
/***************************************************************************
qgslayertreeembeddedwidgetregistry.h
--------------------------------------
Date : May 2016
Copyright : (C) 2016 by Martin Dobias
Email : wonder dot sk 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. *
* *
***************************************************************************/
#ifndef QGSLAYERTREEEMBEDDEDWIDGETREGISTRY_H
#define QGSLAYERTREEEMBEDDEDWIDGETREGISTRY_H
#include <QMap>
#include <QWidget>
#if 0
/** Base class for embedded widgets */
class QgsLayerTreeEmbeddedWidget : public QWidget
{
public:
};
#endif
class QgsMapLayer;
/** Provider interface to be implemented */
class GUI_EXPORT QgsLayerTreeEmbeddedWidgetProvider
{
public:
virtual ~QgsLayerTreeEmbeddedWidgetProvider() {}
//! unique name of the provider (among other providers)
virtual QString id() const = 0;
//! factory to create widgets
virtual QWidget* createWidget( QgsMapLayer* layer, QMap<QString, QString> properties ) = 0;
//! whether it makes sense to use this widget for a particular layer
virtual bool supportsLayer( QgsMapLayer* layer ) = 0;
};
/** Singleton registry */
class GUI_EXPORT QgsLayerTreeEmbeddedWidgetRegistry
{
public:
/** Means of accessing canonical single instance */
static QgsLayerTreeEmbeddedWidgetRegistry* instance();
~QgsLayerTreeEmbeddedWidgetRegistry();
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 );
/** 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:
QgsLayerTreeEmbeddedWidgetRegistry();
//! storage of all the providers
QMap<QString, QgsLayerTreeEmbeddedWidgetProvider*> mProviders;
};
////
// TMP
class QSlider;
class QgsMapLayer;
#include <QWidget>
class TransparencyWidget : public QWidget
{
Q_OBJECT
public:
TransparencyWidget( QgsMapLayer* layer );
virtual QSize sizeHint() const override;
public slots:
void sliderValueChanged( int value );
void layerTrChanged();
private:
QgsMapLayer* mLayer;
QSlider* mSlider;
};
#endif // QGSLAYERTREEEMBEDDEDWIDGETREGISTRY_H

View File

@ -16,6 +16,7 @@
#include "qgslayertreeview.h"
#include "qgslayertree.h"
#include "qgslayertreeembeddedwidgetregistry.h"
#include "qgslayertreemodel.h"
#include "qgslayertreemodellegendnode.h"
#include "qgslayertreeviewdefaultactions.h"
@ -24,6 +25,7 @@
#include <QMenu>
#include <QContextMenuEvent>
QgsLayerTreeView::QgsLayerTreeView( QWidget *parent )
: QTreeView( parent )
, mDefaultActions( nullptr )
@ -129,6 +131,28 @@ void QgsLayerTreeView::modelRowsInserted( const QModelIndex& index, int start, i
if ( !parentNode )
return;
// EMBEDDED WIDGETS
if ( layerTreeModel()->testFlag( QgsLayerTreeModel::UseEmbeddedWidgets ) && QgsLayerTree::isLayer( parentNode ) )
{
QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( parentNode );
if ( QgsMapLayer* layer = nodeLayer->layer() )
{
int widgetsCount = nodeLayer->customProperty( "embeddedWidgets/count", 0 ).toInt();
QList<QgsLayerTreeModelLegendNode*> legendNodes = layerTreeModel()->layerLegendNodes( nodeLayer );
for ( int i = 0; i < widgetsCount; ++i )
{
// TODO: check that the legend nodes are actually the placeholders for embedded widgets
QString providerId = nodeLayer->customProperty( QString( "embeddedWidgets/%1/id" ).arg( i ) ).toString();
if ( QgsLayerTreeEmbeddedWidgetProvider* provider = QgsLayerTreeEmbeddedWidgetRegistry::instance()->provider( providerId ) )
{
QModelIndex index = layerTreeModel()->legendNode2index( legendNodes[i] );
setIndexWidget( index, provider->createWidget( layer, QMap<QString, QString>() ) );
}
}
}
}
if ( QgsLayerTree::isLayer( parentNode ) )
{
// if ShowLegendAsTree flag is enabled in model, we may need to expand some legend nodes

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsLayerTreeEmbeddedConfigWidget</class>
<widget class="QWidget" name="QgsLayerTreeEmbeddedConfigWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>580</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Available widgets</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Used widgets</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QListView" name="mListAvailable"/>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="mBtnAdd">
<property name="text">
<string>-&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mBtnRemove">
<property name="text">
<string>&lt;-</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="2">
<widget class="QListView" name="mListUsed"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>