Skip to content

Commit

Permalink
[feature] Expose data defined control over raster layer opacity
Browse files Browse the repository at this point in the history
Allows the opacity of a raster to be varied e.g. on different
pages of an atlas, depending on the visibility of another layer,
by temporal variables, etc...
  • Loading branch information
nyalldawson committed Jun 25, 2021
1 parent c013c78 commit 2a9c32b
Show file tree
Hide file tree
Showing 10 changed files with 435 additions and 162 deletions.
1 change: 1 addition & 0 deletions python/gui/auto_generated/qgsmaplayerconfigwidget.sip.in
Expand Up @@ -64,6 +64,7 @@ Will be called when live update is enabled.


protected:

};

/************************************************************************
Expand Down
Expand Up @@ -43,6 +43,9 @@ Adds a properties page factory to the raster layer properties dialog.
.. versionadded:: 3.18
%End

virtual QgsExpressionContext createExpressionContext() const;


protected slots:

};
Expand Down
Expand Up @@ -27,6 +27,16 @@ Widget to control a layers transparency and related options
Widget to control a layers transparency and related options
%End

void setContext( const QgsSymbolWidgetContext &context );
%Docstring
Sets the ``context`` in which the dialog is shown, e.g., the associated map canvas and expression contexts.

.. versionadded:: 3.22
%End

virtual QgsExpressionContext createExpressionContext() const;


public slots:


Expand All @@ -41,6 +51,9 @@ Sync the widget state to the layer set for the widget.
Apply any changes on the widget to the set layer.
%End

protected:


};
/************************************************************************
* This file has been generated automatically from *
Expand Down
1 change: 1 addition & 0 deletions src/gui/qgsmaplayerconfigwidget.h
Expand Up @@ -82,6 +82,7 @@ class GUI_EXPORT QgsMapLayerConfigWidget : public QgsPanelWidget
#endif

protected:

QgsMapLayer *mLayer = nullptr;
QgsMapCanvas *mMapCanvas = nullptr;
};
Expand Down
57 changes: 57 additions & 0 deletions src/gui/raster/qgsrasterlayerproperties.cpp
Expand Up @@ -67,6 +67,7 @@

#include "qgsrasterlayertemporalpropertieswidget.h"
#include "qgsprojecttimesettings.h"
#include "qgsexpressioncontextutils.h"

#include <QDesktopServices>
#include <QTableWidgetItem>
Expand Down Expand Up @@ -225,6 +226,13 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv
return;
}

mContext << QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope( QgsProject::instance() )
<< QgsExpressionContextUtils::atlasScope( nullptr );
if ( mMapCanvas )
mContext << QgsExpressionContextUtils::mapSettingsScope( mMapCanvas->mapSettings() );
mContext << QgsExpressionContextUtils::layerScope( mRasterLayer );

QgsRasterDataProvider *provider = mRasterLayer->dataProvider();

// Only do pyramids if dealing directly with GDAL.
Expand Down Expand Up @@ -446,6 +454,8 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv

#endif

initializeDataDefinedButton( mOpacityDDBtn, QgsRasterPipe::RendererOpacity );

mRenderTypeComboBox_currentIndexChanged( widgetIndex );

// update based on lyr's current state
Expand Down Expand Up @@ -516,6 +526,11 @@ void QgsRasterLayerProperties::addPropertiesPageFactory( const QgsMapLayerConfig
}
}

QgsExpressionContext QgsRasterLayerProperties::createExpressionContext() const
{
return mContext;
}

void QgsRasterLayerProperties::setupTransparencyTable( int nBands )
{
tableTransparency->clear();
Expand Down Expand Up @@ -903,6 +918,9 @@ void QgsRasterLayerProperties::sync()

mTemporalWidget->syncToLayer();

mPropertyCollection = mRasterLayer->pipe()->dataDefinedProperties();
updateDataDefinedButtons();

for ( QgsMapLayerConfigWidget *page : std::as_const( mLayerPropertiesPages ) )
{
page->syncToLayer( mRasterLayer );
Expand Down Expand Up @@ -1122,6 +1140,8 @@ void QgsRasterLayerProperties::apply()
mRasterLayer->setCustomProperty( "WMSPublishDataSourceUrl", mPublishDataSourceUrlCheckBox->isChecked() );
mRasterLayer->setCustomProperty( "WMSBackgroundLayer", mBackgroundLayerCheckBox->isChecked() );

mRasterLayer->pipe()->setDataDefinedProperties( mPropertyCollection );

// Force a redraw of the legend
mRasterLayer->setLegend( QgsMapLayerLegend::defaultRasterLegend( mRasterLayer ) );

Expand Down Expand Up @@ -1582,6 +1602,43 @@ void QgsRasterLayerProperties::optionsStackedWidget_CurrentChanged( int index )
}
}

void QgsRasterLayerProperties::initializeDataDefinedButton( QgsPropertyOverrideButton *button, QgsRasterPipe::Property key )
{
button->blockSignals( true );
button->init( key, mPropertyCollection, QgsRasterPipe::propertyDefinitions(), nullptr );
connect( button, &QgsPropertyOverrideButton::changed, this, &QgsRasterLayerProperties::updateProperty );
button->registerExpressionContextGenerator( this );
button->blockSignals( false );
}

void QgsRasterLayerProperties::updateDataDefinedButtons()
{
const auto propertyOverrideButtons { findChildren< QgsPropertyOverrideButton * >() };
for ( QgsPropertyOverrideButton *button : propertyOverrideButtons )
{
updateDataDefinedButton( button );
}
}

void QgsRasterLayerProperties::updateDataDefinedButton( QgsPropertyOverrideButton *button )
{
if ( !button )
return;

if ( button->propertyKey() < 0 )
return;

QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
whileBlocking( button )->setToProperty( mPropertyCollection.property( key ) );
}

void QgsRasterLayerProperties::updateProperty()
{
QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
mPropertyCollection.setProperty( key, button->toProperty() );
}

void QgsRasterLayerProperties::pbnImportTransparentPixelValues_clicked()
{
int myLineCounter = 0;
Expand Down
34 changes: 33 additions & 1 deletion src/gui/raster/qgsrasterlayerproperties.h
Expand Up @@ -29,6 +29,7 @@
#include "qgsmaptoolemitpoint.h"
#include "qgis_gui.h"
#include "qgsresamplingutils.h"
#include "qgsrasterpipe.h"

class QgsPointXY;
class QgsMapLayer;
Expand All @@ -43,6 +44,7 @@ class QgsWebView;
class QgsProviderSourceWidget;
class QgsMapLayerConfigWidgetFactory;
class QgsMapLayerConfigWidget;
class QgsPropertyOverrideButton;


/**
Expand All @@ -52,7 +54,7 @@ class QgsMapLayerConfigWidget;
* \since QGIS 3.12 (in the GUI API)
*/

class GUI_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private Ui::QgsRasterLayerPropertiesBase
class GUI_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private Ui::QgsRasterLayerPropertiesBase, private QgsExpressionContextGenerator
{
Q_OBJECT

Expand Down Expand Up @@ -85,12 +87,40 @@ class GUI_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private
*/
void addPropertiesPageFactory( const QgsMapLayerConfigWidgetFactory *factory );

QgsExpressionContext createExpressionContext() const override;

protected slots:
//! \brief auto slot executed when the active page in the main widget stack is changed
void optionsStackedWidget_CurrentChanged( int index ) override SIP_SKIP ;

private:

// TODO -- consider moving these to a common raster widget base class

/**
* Registers a property override button, setting up its initial value, connections and description.
* \param button button to register
* \param key corresponding data defined property key
*/
void initializeDataDefinedButton( QgsPropertyOverrideButton *button, QgsRasterPipe::Property key );

/**
* Updates all property override buttons to reflect the widgets's current properties.
*/
void updateDataDefinedButtons();

/**
* Updates a specific property override \a button to reflect the widgets's current properties.
*/
void updateDataDefinedButton( QgsPropertyOverrideButton *button );

//! Temporary property collection
QgsPropertyCollection mPropertyCollection;

private slots:

void updateProperty();

//! \brief Applies the settings made in the dialog without closing the box
void apply();
//! \brief Called when cancel button is pressed
Expand Down Expand Up @@ -277,6 +307,8 @@ class GUI_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private

QgsWebView *mMetadataViewer = nullptr;

QgsExpressionContext mContext;

friend class QgsAppScreenShots;
};
#endif
87 changes: 86 additions & 1 deletion src/gui/raster/qgsrastertransparencywidget.cpp
Expand Up @@ -33,7 +33,8 @@
#include "qgsrasteridentifyresult.h"
#include "qgsmultibandcolorrenderer.h"
#include "qgsdoublevalidator.h"

#include "qgsexpressioncontextutils.h"
#include "qgstemporalcontroller.h"

QgsRasterTransparencyWidget::QgsRasterTransparencyWidget( QgsRasterLayer *layer, QgsMapCanvas *canvas, QWidget *parent )
: QgsMapLayerConfigWidget( layer, canvas, parent )
Expand Down Expand Up @@ -69,6 +70,47 @@ QgsRasterTransparencyWidget::QgsRasterTransparencyWidget( QgsRasterLayer *layer,
{
pbnAddValuesFromDisplay->setEnabled( false );
}

initializeDataDefinedButton( mOpacityDDBtn, QgsRasterPipe::RendererOpacity );
}

void QgsRasterTransparencyWidget::setContext( const QgsSymbolWidgetContext &context )
{
mContext = context;
}

QgsExpressionContext QgsRasterTransparencyWidget::createExpressionContext() const
{
QgsExpressionContext expContext;
expContext << QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope( QgsProject::instance() )
<< QgsExpressionContextUtils::atlasScope( nullptr );

if ( QgsMapCanvas *canvas = mContext.mapCanvas() )
{
expContext << QgsExpressionContextUtils::mapSettingsScope( canvas->mapSettings() )
<< new QgsExpressionContextScope( canvas->expressionContextScope() );
if ( const QgsExpressionContextScopeGenerator *generator = dynamic_cast< const QgsExpressionContextScopeGenerator * >( canvas->temporalController() ) )
{
expContext << generator->createExpressionContextScope();
}
}
else
{
expContext << QgsExpressionContextUtils::mapSettingsScope( QgsMapSettings() );
}

if ( mRasterLayer )
expContext << QgsExpressionContextUtils::layerScope( mRasterLayer );

// additional scopes
const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
{
expContext.appendScope( new QgsExpressionContextScope( scope ) );
}

return expContext;
}

void QgsRasterTransparencyWidget::syncToLayer()
Expand Down Expand Up @@ -128,6 +170,9 @@ void QgsRasterTransparencyWidget::syncToLayer()
leNoDataValue->setText( QString() );
}

mPropertyCollection = mRasterLayer->pipe()->dataDefinedProperties();
updateDataDefinedButtons();

populateTransparencyTable( mRasterLayer->renderer() );
}

Expand Down Expand Up @@ -462,6 +507,46 @@ void QgsRasterTransparencyWidget::apply()
//set global transparency
rasterRenderer->setOpacity( mOpacityWidget->opacity() );
}

mRasterLayer->pipe()->setDataDefinedProperties( mPropertyCollection );
}

void QgsRasterTransparencyWidget::initializeDataDefinedButton( QgsPropertyOverrideButton *button, QgsRasterPipe::Property key )
{
button->blockSignals( true );
button->init( key, mPropertyCollection, QgsRasterPipe::propertyDefinitions(), nullptr );
connect( button, &QgsPropertyOverrideButton::changed, this, &QgsRasterTransparencyWidget::updateProperty );
button->registerExpressionContextGenerator( this );
button->blockSignals( false );
}

void QgsRasterTransparencyWidget::updateDataDefinedButtons()
{
const auto propertyOverrideButtons { findChildren< QgsPropertyOverrideButton * >() };
for ( QgsPropertyOverrideButton *button : propertyOverrideButtons )
{
updateDataDefinedButton( button );
}
}

void QgsRasterTransparencyWidget::updateDataDefinedButton( QgsPropertyOverrideButton *button )
{
if ( !button )
return;

if ( button->propertyKey() < 0 )
return;

QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
whileBlocking( button )->setToProperty( mPropertyCollection.property( key ) );
}

void QgsRasterTransparencyWidget::updateProperty()
{
QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
mPropertyCollection.setProperty( key, button->toProperty() );
emit widgetChanged();
}

void QgsRasterTransparencyWidget::pixelSelected( const QgsPointXY &canvasPoint )
Expand Down

0 comments on commit 2a9c32b

Please sign in to comment.