Skip to content
Permalink
Browse files
[feature] When a group is selected in the layer tree, the layer
styling dock now shows an option to control the rendering
appearance of the entire group

By selecting "Render Layers as a Group", the whole set of layers
present in the group will be rendered as a flattened object during
map renders. This means that the opacity and blend mode assigned
to the group will apply to the flattened version of all the
child layers, instead of applying to each layer one-by-one.

This allows for styling results such as groups of layers which
obscure each other, but overall are rendered as semi-opaque.
  • Loading branch information
nyalldawson committed Nov 25, 2021
1 parent 027928a commit f91ace8fd75538ec70623001354b7a193e30d02a
@@ -45,6 +45,7 @@ set(QGIS_APP_SRCS
qgsfeatureaction.cpp
qgslayercapabilitiesmodel.cpp
qgslayernotesmanager.cpp
qgslayertreegrouppropertieswidget.cpp
qgslayertreeviewindicatorprovider.cpp
qgslayertreeviewembeddedindicator.cpp
qgslayertreeviewfilterindicator.cpp
@@ -252,6 +252,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgsdiagramproperties.h"
#include "qgslayerdefinition.h"
#include "qgslayertree.h"
#include "qgslayertreegrouppropertieswidget.h"
#include "qgslayertreemapcanvasbridge.h"
#include "qgslayertreemodel.h"
#include "qgslayertreemodellegendnode.h"
@@ -1428,6 +1429,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
#endif
registerMapLayerPropertiesFactory( new QgsPointCloudElevationPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsAnnotationItemPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsLayerTreeGroupPropertiesWidgetFactory( this ) );

activateDeactivateLayerRelatedActions( nullptr ); // after members were created

@@ -0,0 +1,154 @@
/***************************************************************************
qgslayertreegrouppropertieswidget.cpp
---------------------
begin : Nobember 2021
copyright : (C) 2021 by 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 "qgslayertreegrouppropertieswidget.h"
#include "qgsstyle.h"
#include "qgsapplication.h"
#include "qgsmaplayer.h"
#include "qgsgui.h"
#include "qgspainteffect.h"
#include "qgsmapcanvas.h"

#include <QStackedWidget>
#include <QHBoxLayout>
#include <QLabel>

QgsLayerTreeGroupPropertiesWidget::QgsLayerTreeGroupPropertiesWidget( QgsMapCanvas *canvas, QWidget *parent )
: QgsMapLayerConfigWidget( nullptr, canvas, parent )
{
setupUi( this );

connect( mOpacityWidget, &QgsOpacityWidget::opacityChanged, this, &QgsLayerTreeGroupPropertiesWidget::onLayerPropertyChanged );
connect( mBlendModeComboBox, qOverload< int >( &QgsBlendModeComboBox::currentIndexChanged ), this, &QgsLayerTreeGroupPropertiesWidget::onLayerPropertyChanged );
// connect( mEffectWidget, &QgsEffectStackCompactWidget::changed, this, &QgsLayerTreeGroupPropertiesWidget::onLayerPropertyChanged );
connect( mRenderAsGroupCheckBox, &QGroupBox::toggled, this, &QgsLayerTreeGroupPropertiesWidget::onLayerPropertyChanged );

setDockMode( true );
}

QgsLayerTreeGroupPropertiesWidget::~QgsLayerTreeGroupPropertiesWidget() = default;

void QgsLayerTreeGroupPropertiesWidget::syncToLayer( QgsMapLayer * )
{
}

void QgsLayerTreeGroupPropertiesWidget::setMapLayerConfigWidgetContext( const QgsMapLayerConfigWidgetContext &context )
{
QgsMapLayerConfigWidget::setMapLayerConfigWidgetContext( context );
mLayerTreeGroup = context.layerTreeGroup();

mBlockLayerUpdates = true;
if ( QgsGroupLayer *groupLayer = mLayerTreeGroup->groupLayer() )
{
mRenderAsGroupCheckBox->setChecked( true );
mBlendModeComboBox->setBlendMode( groupLayer->blendMode() );
mOpacityWidget->setOpacity( groupLayer->opacity() );
if ( groupLayer->paintEffect() )
{
mPaintEffect.reset( groupLayer->paintEffect()->clone() );
//mEffectWidget->setPaintEffect( mPaintEffect.get() );
}
}
else
{
mRenderAsGroupCheckBox->setChecked( false );
}
mBlockLayerUpdates = false;
}

void QgsLayerTreeGroupPropertiesWidget::setDockMode( bool dockMode )
{
QgsMapLayerConfigWidget::setDockMode( dockMode );
}

void QgsLayerTreeGroupPropertiesWidget::apply()
{
if ( !mLayerTreeGroup )
return;

QgsGroupLayer *groupLayer = mLayerTreeGroup->groupLayer();
if ( groupLayer && !mRenderAsGroupCheckBox->isChecked() )
{
mLayerTreeGroup->setGroupLayer( nullptr );
QgsProject::instance()->removeMapLayer( groupLayer );
groupLayer = nullptr;
}
else if ( !groupLayer && mRenderAsGroupCheckBox->isChecked() )
{
QgsGroupLayer::LayerOptions options( QgsProject::instance()->transformContext() );
groupLayer = mLayerTreeGroup->convertToGroupLayer( options );
QgsProject::instance()->addMapLayer( groupLayer, false );
}

if ( groupLayer )
{
// set the blend mode and opacity for the layer
groupLayer->setBlendMode( mBlendModeComboBox->blendMode() );
groupLayer->setOpacity( mOpacityWidget->opacity() );
if ( mPaintEffect )
groupLayer->setPaintEffect( mPaintEffect->clone() );
groupLayer->triggerRepaint();
}
else if ( mMapLayerConfigWidgetContext.mapCanvas() )
{
mMapLayerConfigWidgetContext.mapCanvas()->refresh();
}
}

void QgsLayerTreeGroupPropertiesWidget::onLayerPropertyChanged()
{
if ( mBlockLayerUpdates )
return;

emit widgetChanged();
}


//
// QgsLayerTreeGroupPropertiesWidgetFactory
//

QgsLayerTreeGroupPropertiesWidgetFactory::QgsLayerTreeGroupPropertiesWidgetFactory( QObject *parent )
: QObject( parent )
{
setIcon( QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/symbology.svg" ) ) );
setTitle( tr( "Group" ) );
}

QgsMapLayerConfigWidget *QgsLayerTreeGroupPropertiesWidgetFactory::createWidget( QgsMapLayer *, QgsMapCanvas *canvas, bool, QWidget *parent ) const
{
return new QgsLayerTreeGroupPropertiesWidget( canvas, parent );
}

bool QgsLayerTreeGroupPropertiesWidgetFactory::supportLayerPropertiesDialog() const
{
return false;
}

bool QgsLayerTreeGroupPropertiesWidgetFactory::supportsStyleDock() const
{
return true;
}

bool QgsLayerTreeGroupPropertiesWidgetFactory::supportsLayer( QgsMapLayer * ) const
{
return false;
}

bool QgsLayerTreeGroupPropertiesWidgetFactory::supportsLayerTreeGroup( QgsLayerTreeGroup * ) const
{
return true;
}

@@ -0,0 +1,71 @@
/***************************************************************************
qgslayertreegrouppropertieswidget.h
---------------------
begin : November 2021
copyright : (C) 2021 by 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. *
* *
***************************************************************************/

#ifndef QGSLAYERTREEGROUPPROPERTIESWIDGET_H
#define QGSLAYERTREEGROUPPROPERTIESWIDGET_H

#include "qgsmaplayerconfigwidget.h"
#include "qgsmaplayerconfigwidgetfactory.h"
#include "ui_qgslayertreegrouppropertieswidgetbase.h"
#include <QPointer>

class QgsAnnotationLayer;
class QgsAnnotationItemBaseWidget;
class QStackedWidget;

class QgsLayerTreeGroupPropertiesWidget : public QgsMapLayerConfigWidget, public Ui::QgsLayerTreeGroupPropertiesWidgetBase
{
Q_OBJECT
public:

QgsLayerTreeGroupPropertiesWidget( QgsMapCanvas *canvas, QWidget *parent );
~QgsLayerTreeGroupPropertiesWidget() override;

void syncToLayer( QgsMapLayer *layer ) override;
void setMapLayerConfigWidgetContext( const QgsMapLayerConfigWidgetContext &context ) override;
void setDockMode( bool dockMode ) override;

public slots:
void apply() override;

private slots:

void onLayerPropertyChanged();
private:

QPointer< QgsLayerTreeGroup > mLayerTreeGroup;
bool mBlockLayerUpdates = false;

std::unique_ptr< QgsPaintEffect > mPaintEffect;

};


class QgsLayerTreeGroupPropertiesWidgetFactory : public QObject, public QgsMapLayerConfigWidgetFactory
{
Q_OBJECT
public:
explicit QgsLayerTreeGroupPropertiesWidgetFactory( QObject *parent = nullptr );

QgsMapLayerConfigWidget *createWidget( QgsMapLayer *layer, QgsMapCanvas *canvas, bool dockWidget, QWidget *parent ) const override;
bool supportLayerPropertiesDialog() const override;
bool supportsStyleDock() const override;
bool supportsLayer( QgsMapLayer *layer ) const override;
bool supportsLayerTreeGroup( QgsLayerTreeGroup *group ) const override;
};



#endif // QGSLAYERTREEGROUPPROPERTIESWIDGET_H
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsLayerTreeGroupPropertiesWidgetBase</class>
<widget class="QWidget" name="QgsLayerTreeGroupPropertiesWidgetBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>424</width>
<height>702</height>
</rect>
</property>
<property name="windowTitle">
<string>Annotation Item Properties</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="mRenderAsGroupCheckBox">
<property name="title">
<string>Render Layers as a Group</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="lblTransparency">
<property name="text">
<string>Opacity</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="lblBlend">
<property name="text">
<string>Blending mode</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QgsBlendModeComboBox" name="mBlendModeComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>4</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QgsOpacityWidget" name="mOpacityWidget" native="true">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
</layout>
</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>
</widget>
<customwidgets>
<customwidget>
<class>QgsBlendModeComboBox</class>
<extends>QComboBox</extends>
<header>qgsblendmodecombobox.h</header>
</customwidget>
<customwidget>
<class>QgsOpacityWidget</class>
<extends>QWidget</extends>
<header>qgsopacitywidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

0 comments on commit f91ace8

Please sign in to comment.