Skip to content

Commit

Permalink
Streamline expression context generation (#3350)
Browse files Browse the repository at this point in the history
* Save more data to QML

 * Virtual fields
 * Map tips
 * Display expression
 * Read only flag

* Streamline expression context generation

Whenever an object is able to generate an expression context it
implements the method createExpressionContext() declared in
QgsExpressionContextGenerator.

This makes a cleaner API and allows using QgsFieldExpressionWidget and
QgsDataDefinedButton from python because standard OO programming
approaches are used instead of callbacks and void pointers.

* Colorize output of doc and sip tests

* Fix build

* Fix sip complaints

* Fix rebase problems

* Workaround failing bindings test
  • Loading branch information
m-kuhn committed Aug 10, 2016
1 parent f3e90f1 commit 58ea211
Show file tree
Hide file tree
Showing 102 changed files with 643 additions and 659 deletions.
2 changes: 1 addition & 1 deletion ci/travis/linux/qt4/before_install.sh
Expand Up @@ -21,4 +21,4 @@ curl -L https://github.com/opengisch/osgeo4travis/archive/qt4bin.tar.gz | tar -x
curl -L https://cmake.org/files/v3.5/cmake-3.5.0-Linux-x86_64.tar.gz | tar --strip-components=1 -zxC /home/travis/osgeo4travis

popd
pip install --user autopep8 nose2 pyyaml mock future
pip install --user autopep8 nose2 pyyaml mock future termcolor
2 changes: 1 addition & 1 deletion ci/travis/linux/qt5/before_install.sh
Expand Up @@ -28,4 +28,4 @@ curl -L https://github.com/opengisch/osgeo4travis/archive/qt5bin.tar.gz | tar -x
curl -L https://cmake.org/files/v3.5/cmake-3.5.0-Linux-x86_64.tar.gz | tar --strip-components=1 -zxC /home/travis/osgeo4travis
popd

pip install psycopg2 numpy nose2 pyyaml mock future
pip install psycopg2 numpy nose2 pyyaml mock future termcolor
12 changes: 12 additions & 0 deletions doc/api_break.dox
Expand Up @@ -393,6 +393,18 @@ be returned in place of a null pointer.</li>
QgsExpressionContext variables should be used in their place.</li>
</ul>

\subsection qgis_api_break_3_0_QgsDataDefinedButton QgsDataDefinedButton

<ul>
<li>registerGetExpressionContextCallback has been removed in favor of registerExpressionContextGenerator</li>
</ul>

\subsection qgis_api_break_3_0_QgsFieldExpressionWidget QgsFieldExpressionWidget

<ul>
<li>registerGetExpressionContextCallback has been removed in favor of registerExpressionContextGenerator</li>
</ul>

\subsection qgis_api_break_3_0_QgsDataDefinedSymbolDialog QgsDataDefinedSymbolDialog

<ul>
Expand Down
2 changes: 1 addition & 1 deletion python/core/composer/qgscomposerattributetablev2.sip
Expand Up @@ -281,5 +281,5 @@ class QgsComposerAttributeTableV2 : QgsComposerTableV2
*/
bool getTableContents( QgsComposerTableContents &contents );

virtual QgsExpressionContext* createExpressionContext() const /Factory/;
virtual QgsExpressionContext createExpressionContext() const;
};
2 changes: 1 addition & 1 deletion python/core/composer/qgscomposerframe.sip
Expand Up @@ -78,5 +78,5 @@ class QgsComposerFrame: QgsComposerItem
*/
bool isEmpty() const;

virtual QgsExpressionContext* createExpressionContext() const;
virtual QgsExpressionContext createExpressionContext() const;
};
2 changes: 1 addition & 1 deletion python/core/composer/qgscomposeritem.sip
Expand Up @@ -645,7 +645,7 @@ class QgsComposerItem : QgsComposerObject, QGraphicsRectItem
* scopes for global, project, composition, atlas and item properties.
* @note added in QGIS 2.12
*/
QgsExpressionContext* createExpressionContext() const /Factory/;
QgsExpressionContext createExpressionContext() const;

public slots:
/** Sets the item rotation
Expand Down
2 changes: 1 addition & 1 deletion python/core/composer/qgscomposermap.sip
Expand Up @@ -769,7 +769,7 @@ class QgsComposerMap : QgsComposerItem
* @note added in 2.6 */
void requestedExtent( QgsRectangle& extent ) const;

virtual QgsExpressionContext* createExpressionContext() const;
virtual QgsExpressionContext createExpressionContext() const;

signals:
void extentChanged();
Expand Down
2 changes: 1 addition & 1 deletion python/core/composer/qgscomposermapgrid.sip
Expand Up @@ -796,6 +796,6 @@ class QgsComposerMapGrid : QgsComposerMapItem
*/
QColor frameFillColor2() const;

virtual QgsExpressionContext* createExpressionContext() const;
virtual QgsExpressionContext createExpressionContext() const;

};
2 changes: 1 addition & 1 deletion python/core/composer/qgscomposerobject.sip
Expand Up @@ -142,7 +142,7 @@ class QgsComposerObject : QObject
* scopes for global, project and composition properties.
* @note added in QGIS 2.12
*/
virtual QgsExpressionContext* createExpressionContext() const /Factory/;
virtual QgsExpressionContext createExpressionContext() const;

public slots:

Expand Down
2 changes: 1 addition & 1 deletion python/core/composer/qgscomposition.sip
Expand Up @@ -845,7 +845,7 @@ class QgsComposition : QGraphicsScene
* scopes for global, project, composition and atlas properties.
* @note added in QGIS 2.12
*/
QgsExpressionContext* createExpressionContext() const;
QgsExpressionContext createExpressionContext() const;

protected:
void init();
Expand Down
1 change: 1 addition & 0 deletions python/core/core.sip
Expand Up @@ -49,6 +49,7 @@
%Include qgserror.sip
%Include qgsexpression.sip
%Include qgsexpressioncontext.sip
%Include qgsexpressioncontextgenerator.sip
%Include qgsfeature.sip
%Include qgsfeaturefilterprovider.sip
%Include qgsfeatureiterator.sip
Expand Down
41 changes: 41 additions & 0 deletions python/core/qgsexpressioncontextgenerator.sip
@@ -0,0 +1,41 @@
/***************************************************************************
qgsexpressioncontextgenerator.sip - QgsExpressionContextGenerator

---------------------
begin : 1.8.2016
copyright : (C) 2016 by Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************
* *
* 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. *
* *
***************************************************************************/
/**
* \ingroup core
* Abstract interface for generating an expression context.
*
* You need to implement this interface in a class and register this class with
* QgsFieldExpressionWidget::registerExpressionGenerator().
*
* Whenever this widget requires an expression context it will call the createExpressionContext()
* method to get a context.
*
* @note added in QGIS 3.0
*/

class QgsExpressionContextGenerator
{
%TypeHeaderCode
#include "qgsexpressioncontextgenerator.h"
%End

public:
/**
* This method needs to be reimplemented in all classes which implement this interface
* and return an expression context.
*/
virtual QgsExpressionContext createExpressionContext() const = 0;
};
2 changes: 2 additions & 0 deletions python/core/qgsproject.sip
Expand Up @@ -324,6 +324,8 @@ class QgsProject : QObject
*/
void setEvaluateDefaultValues( bool evaluateDefaultValues );

QgsExpressionContext createExpressionContext() const;

protected:

/** Set error message from read/write operation
Expand Down
2 changes: 2 additions & 0 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -1385,6 +1385,8 @@ class QgsVectorLayer : QgsMapLayer
*/
void setMapTipTemplate( const QString& mapTipTemplate );

QgsExpressionContext createExpressionContext() const;

public slots:
/**
* Select feature by its ID
Expand Down
17 changes: 7 additions & 10 deletions python/gui/qgsdatadefinedbutton.sip
Expand Up @@ -185,17 +185,14 @@ class QgsDataDefinedButton : QToolButton
*/
void clearCheckedWidgets();

//! Callback function for retrieving the expression context for the button
//typedef QgsExpressionContext( *ExpressionContextCallback )( const void* context );

/** Register callback function for retrieving the expression context for the button
* @param fnGetExpressionContext call back function, will be called when the data defined
* button requires the current expression context
* @param context context for callback function
* @note added in QGIS 2.12
* @note not available in Python bindings
/**
* Register an expression context generator class that will be used to retrieve
* an expression context for the button.
* @param generator A QgsExpressionContextGenerator class that will be used to
* create an expression context when required.
* @note added in QGIS 3.0
*/
//void registerGetExpressionContextCallback( ExpressionContextCallback fnGetExpressionContext, const void* context );
void registerExpressionContextGenerator( QgsExpressionContextGenerator* generator );

/**
* Sets an assistant used to define the data defined object properties.
Expand Down
17 changes: 7 additions & 10 deletions python/gui/qgsfieldexpressionwidget.sip
Expand Up @@ -56,17 +56,14 @@ class QgsFieldExpressionWidget : QWidget
//! Returns the currently used layer
QgsVectorLayer* layer() const;

//! Callback function for retrieving the expression context for the expression
//typedef QgsExpressionContext( *ExpressionContextCallback )( const void* context );

/** Register callback function for retrieving the expression context for the expression
* @param fnGetExpressionContext call back function, will be called when the widget requires
* the current expression context
* @param context context for callback function
* @note added in QGIS 2.12
* @note not available in Python bindings
/**
* Register an expression context generator class that will be used to retrieve
* an expression context for the widget.
* @param generator A QgsExpressionContextGenerator class that will be used to
* create an expression context when required.
* @note added in QGIS 3.0
*/
//void registerGetExpressionContextCallback( ExpressionContextCallback fnGetExpressionContext, const void* context );
void registerExpressionContextGenerator( QgsExpressionContextGenerator* generator );

signals:
//! the signal is emitted when the currently selected field changes
Expand Down
8 changes: 7 additions & 1 deletion python/gui/symbology-ng/qgsrendererwidget.sip
Expand Up @@ -122,9 +122,15 @@ class QgsDataDefinedValueDialog : QDialog
void dataDefinedChanged();

protected:
QgsDataDefined symbolDataDefined() const;
/**
* Should be called in the constructor of child classes.
*
* @note May be missing Python bindings depending on the platform.
*/
void init( const QString& description ); // needed in children ctor to call virtual

private:
QgsDataDefined symbolDataDefined() const;
virtual QgsDataDefined symbolDataDefined( const QgsSymbol* ) const = 0;
virtual double value( const QgsSymbol* ) const = 0;
virtual void setDataDefined( QgsSymbol* symbol, const QgsDataDefined& dd ) = 0;
Expand Down
10 changes: 2 additions & 8 deletions python/gui/symbology-ng/qgssymbollayerwidget.sip
Expand Up @@ -51,15 +51,9 @@ class QgsSymbolLayerWidget : QWidget
void setExpressionContext( QgsExpressionContext* context );

protected:
void registerDataDefinedButton( QgsDataDefinedButton* button, const QString& propertyName, QgsDataDefinedButton::DataType type, const QString& description );

void registerDataDefinedButton( QgsDataDefinedButton * button, const QString & propertyName, QgsDataDefinedButton::DataType type, const QString & description );

/** Get label for data defined entry.
* Implemented only for 'size' of marker symbols
* @note added in 2.1
* @deprecated no longer used
*/
virtual QString dataDefinedPropertyLabel( const QString &entryName ) /Deprecated/;
QgsExpressionContext createExpressionContext() const;

signals:
/**
Expand Down
6 changes: 0 additions & 6 deletions python/gui/symbology-ng/qgssymbolslistwidget.sip
Expand Up @@ -68,10 +68,4 @@ class QgsSymbolsListWidget : QWidget

signals:
void changed();

protected:
void populateSymbolView();
void populateSymbols( const QStringList& symbols );
void updateSymbolColor();
void updateSymbolInfo();
};
22 changes: 5 additions & 17 deletions src/app/composer/qgsatlascompositionwidget.cpp
Expand Up @@ -26,18 +26,6 @@
#include "qgscomposermap.h"
#include "qgsvectorlayer.h"

static QgsExpressionContext _getExpressionContext( const void* context )
{
const QgsComposition* composition = ( const QgsComposition* ) context;
if ( !composition )
{
return QgsExpressionContext();
}

QScopedPointer< QgsExpressionContext > expContext( composition->createExpressionContext() );
return QgsExpressionContext( *expContext );
}

QgsAtlasCompositionWidget::QgsAtlasCompositionWidget( QWidget* parent, QgsComposition* c ):
QWidget( parent ), mComposition( c )
{
Expand All @@ -58,7 +46,7 @@ QgsAtlasCompositionWidget::QgsAtlasCompositionWidget( QWidget* parent, QgsCompos
// connect to updates
connect( &mComposition->atlasComposition(), SIGNAL( parameterChanged() ), this, SLOT( updateGuiElements() ) );

mPageNameWidget->registerGetExpressionContextCallback( &_getExpressionContext, mComposition );
mPageNameWidget->registerExpressionContextGenerator( mComposition );

updateGuiElements();
}
Expand Down Expand Up @@ -133,8 +121,8 @@ void QgsAtlasCompositionWidget::on_mAtlasFilenameExpressionButton_clicked()
return;
}

QScopedPointer<QgsExpressionContext> context( mComposition->createExpressionContext() );
QgsExpressionBuilderDialog exprDlg( atlasMap->coverageLayer(), mAtlasFilenamePatternEdit->text(), this, "generic", *context );
QgsExpressionContext context = mComposition->createExpressionContext();
QgsExpressionBuilderDialog exprDlg( atlasMap->coverageLayer(), mAtlasFilenamePatternEdit->text(), this, "generic", context );
exprDlg.setWindowTitle( tr( "Expression based filename" ) );

if ( exprDlg.exec() == QDialog::Accepted )
Expand Down Expand Up @@ -306,8 +294,8 @@ void QgsAtlasCompositionWidget::on_mAtlasFeatureFilterButton_clicked()
return;
}

QScopedPointer<QgsExpressionContext> context( mComposition->createExpressionContext() );
QgsExpressionBuilderDialog exprDlg( vl, mAtlasFeatureFilterEdit->text(), this, "generic", *context );
QgsExpressionContext context = mComposition->createExpressionContext();
QgsExpressionBuilderDialog exprDlg( vl, mAtlasFeatureFilterEdit->text(), this, "generic", context );
exprDlg.setWindowTitle( tr( "Expression based filter" ) );

if ( exprDlg.exec() == QDialog::Accepted )
Expand Down
15 changes: 7 additions & 8 deletions src/app/composer/qgsattributeselectiondialog.cpp
Expand Up @@ -99,18 +99,17 @@ QgsComposerColumnSourceDelegate::QgsComposerColumnSourceDelegate( QgsVectorLayer

}

static QgsExpressionContext _getExpressionContext( const void* context )
QgsExpressionContext QgsComposerColumnSourceDelegate::createExpressionContext() const
{
const QgsComposerObject* object = ( const QgsComposerObject* ) context;
if ( !object )
if ( !mComposerObject )
{
return QgsExpressionContext();
}

QScopedPointer< QgsExpressionContext > expContext( object->createExpressionContext() );
expContext->lastScope()->setVariable( "row_number", 1 );
expContext->setHighlightedVariables( QStringList() << "row_number" );
return QgsExpressionContext( *expContext );
QgsExpressionContext expContext = mComposerObject->createExpressionContext();
expContext.lastScope()->setVariable( "row_number", 1 );
expContext.setHighlightedVariables( QStringList() << "row_number" );
return expContext;
}

QWidget* QgsComposerColumnSourceDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
Expand All @@ -120,7 +119,7 @@ QWidget* QgsComposerColumnSourceDelegate::createEditor( QWidget* parent, const Q

QgsFieldExpressionWidget *fieldExpression = new QgsFieldExpressionWidget( parent );
fieldExpression->setLayer( mVectorLayer );
fieldExpression->registerGetExpressionContextCallback( &_getExpressionContext, mComposerObject );
fieldExpression->registerExpressionContextGenerator( this );

//listen out for field changes
connect( fieldExpression, SIGNAL( fieldChanged( QString ) ), this, SLOT( commitAndCloseEditor() ) );
Expand Down
4 changes: 3 additions & 1 deletion src/app/composer/qgsattributeselectiondialog.h
Expand Up @@ -23,6 +23,7 @@
#include <QSet>
#include <QItemDelegate>
#include "ui_qgsattributeselectiondialogbase.h"
#include "qgsexpressioncontextgenerator.h"

class QGridLayout;
class QgsVectorLayer;
Expand Down Expand Up @@ -56,7 +57,7 @@ class QgsComposerColumnAlignmentDelegate : public QItemDelegate
// QgsComposerColumnAlignmentDelegate

/** A delegate for showing column attribute source as a QgsFieldExpressionWidget*/
class QgsComposerColumnSourceDelegate : public QItemDelegate
class QgsComposerColumnSourceDelegate : public QItemDelegate, private QgsExpressionContextGenerator
{
Q_OBJECT

Expand All @@ -71,6 +72,7 @@ class QgsComposerColumnSourceDelegate : public QItemDelegate
private:
QgsVectorLayer* mVectorLayer;
const QgsComposerObject* mComposerObject;
QgsExpressionContext createExpressionContext() const override;
};

// QgsComposerColumnWidthDelegate
Expand Down
3 changes: 2 additions & 1 deletion src/app/composer/qgscomposerarrowwidget.cpp
Expand Up @@ -311,7 +311,8 @@ void QgsComposerArrowWidget::on_mLineStyleButton_clicked()

QgsLineSymbol* newSymbol = mArrow->lineSymbol()->clone();
QgsSymbolSelectorDialog d( newSymbol, QgsStyle::defaultStyle(), nullptr, this );
d.setExpressionContext( mArrow->createExpressionContext() );
QgsExpressionContext context = mArrow->createExpressionContext();
d.setExpressionContext( &context );

if ( d.exec() == QDialog::Accepted )
{
Expand Down
4 changes: 2 additions & 2 deletions src/app/composer/qgscomposerattributetablewidget.cpp
Expand Up @@ -741,8 +741,8 @@ void QgsComposerAttributeTableWidget::on_mFeatureFilterButton_clicked()
return;
}

QScopedPointer<QgsExpressionContext> context( mComposerTable->createExpressionContext() );
QgsExpressionBuilderDialog exprDlg( mComposerTable->sourceLayer(), mFeatureFilterEdit->text(), this, "generic", *context );
QgsExpressionContext context = mComposerTable->createExpressionContext();
QgsExpressionBuilderDialog exprDlg( mComposerTable->sourceLayer(), mFeatureFilterEdit->text(), this, "generic", context );
exprDlg.setWindowTitle( tr( "Expression based filter" ) );
if ( exprDlg.exec() == QDialog::Accepted )
{
Expand Down

1 comment on commit 58ea211

@nyalldawson
Copy link
Collaborator

Choose a reason for hiding this comment

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

Belated +1 ;)

Please sign in to comment.