Skip to content
Permalink
Browse files

Streamline expression context generation (#3350)

* 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 58ea21124e93077ac275a71e3b3f4f9f804191cd
Showing with 643 additions and 659 deletions.
  1. +1 −1 ci/travis/linux/qt4/before_install.sh
  2. +1 −1 ci/travis/linux/qt5/before_install.sh
  3. +12 −0 doc/api_break.dox
  4. +1 −1 python/core/composer/qgscomposerattributetablev2.sip
  5. +1 −1 python/core/composer/qgscomposerframe.sip
  6. +1 −1 python/core/composer/qgscomposeritem.sip
  7. +1 −1 python/core/composer/qgscomposermap.sip
  8. +1 −1 python/core/composer/qgscomposermapgrid.sip
  9. +1 −1 python/core/composer/qgscomposerobject.sip
  10. +1 −1 python/core/composer/qgscomposition.sip
  11. +1 −0 python/core/core.sip
  12. +41 −0 python/core/qgsexpressioncontextgenerator.sip
  13. +2 −0 python/core/qgsproject.sip
  14. +2 −0 python/core/qgsvectorlayer.sip
  15. +7 −10 python/gui/qgsdatadefinedbutton.sip
  16. +7 −10 python/gui/qgsfieldexpressionwidget.sip
  17. +7 −1 python/gui/symbology-ng/qgsrendererwidget.sip
  18. +2 −8 python/gui/symbology-ng/qgssymbollayerwidget.sip
  19. +0 −6 python/gui/symbology-ng/qgssymbolslistwidget.sip
  20. +5 −17 src/app/composer/qgsatlascompositionwidget.cpp
  21. +7 −8 src/app/composer/qgsattributeselectiondialog.cpp
  22. +3 −1 src/app/composer/qgsattributeselectiondialog.h
  23. +2 −1 src/app/composer/qgscomposerarrowwidget.cpp
  24. +2 −2 src/app/composer/qgscomposerattributetablewidget.cpp
  25. +3 −15 src/app/composer/qgscomposerhtmlwidget.cpp
  26. +4 −17 src/app/composer/qgscomposeritemwidget.cpp
  27. +2 −2 src/app/composer/qgscomposerlabelwidget.cpp
  28. +3 −15 src/app/composer/qgscomposermapwidget.cpp
  29. +1 −13 src/app/composer/qgscomposerpicturewidget.cpp
  30. +2 −1 src/app/composer/qgscomposerpolygonwidget.cpp
  31. +2 −1 src/app/composer/qgscomposerpolylinewidget.cpp
  32. +2 −1 src/app/composer/qgscomposershapewidget.cpp
  33. +2 −2 src/app/composer/qgscomposertablewidget.cpp
  34. +3 −14 src/app/composer/qgscompositionwidget.cpp
  35. +4 −5 src/app/qgsattributetabledialog.cpp
  36. +3 −1 src/app/qgsattributetabledialog.h
  37. +5 −9 src/app/qgsdiagramproperties.cpp
  38. +3 −1 src/app/qgsdiagramproperties.h
  39. +5 −6 src/app/qgslabelinggui.cpp
  40. +3 −1 src/app/qgslabelinggui.h
  41. +4 −9 src/app/qgsstatisticalsummarydockwidget.cpp
  42. +3 −1 src/app/qgsstatisticalsummarydockwidget.h
  43. +4 −4 src/app/qgsvectorlayerproperties.cpp
  44. +2 −2 src/app/qgsvectorlayerproperties.h
  45. +1 −0 src/core/CMakeLists.txt
  46. +2 −3 src/core/composer/qgscomposerarrow.cpp
  47. +7 −7 src/core/composer/qgscomposerattributetable.cpp
  48. +10 −10 src/core/composer/qgscomposerattributetablev2.cpp
  49. +1 −1 src/core/composer/qgscomposerattributetablev2.h
  50. +3 −3 src/core/composer/qgscomposerframe.cpp
  51. +1 −1 src/core/composer/qgscomposerframe.h
  52. +5 −14 src/core/composer/qgscomposerhtml.cpp
  53. +13 −23 src/core/composer/qgscomposeritem.cpp
  54. +1 −1 src/core/composer/qgscomposeritem.h
  55. +4 −4 src/core/composer/qgscomposerlabel.cpp
  56. +16 −32 src/core/composer/qgscomposermap.cpp
  57. +1 −1 src/core/composer/qgscomposermap.h
  58. +10 −12 src/core/composer/qgscomposermapgrid.cpp
  59. +1 −1 src/core/composer/qgscomposermapgrid.h
  60. +2 −3 src/core/composer/qgscomposermapoverview.cpp
  61. +4 −6 src/core/composer/qgscomposernodesitem.cpp
  62. +7 −9 src/core/composer/qgscomposerobject.cpp
  63. +3 −3 src/core/composer/qgscomposerobject.h
  64. +4 −14 src/core/composer/qgscomposerpicture.cpp
  65. +1 −3 src/core/composer/qgscomposerpolygon.cpp
  66. +2 −3 src/core/composer/qgscomposerpolyline.cpp
  67. +2 −3 src/core/composer/qgscomposershape.cpp
  68. +15 −24 src/core/composer/qgscomposition.cpp
  69. +2 −2 src/core/composer/qgscomposition.h
  70. +2 −3 src/core/composer/qgspaperitem.cpp
  71. +3 −15 src/core/qgsaggregatecalculator.cpp
  72. +0 −2 src/core/qgsaggregatecalculator.h
  73. +48 −0 src/core/qgsexpressioncontextgenerator.h
  74. +10 −0 src/core/qgsproject.cpp
  75. +4 −1 src/core/qgsproject.h
  76. +52 −40 src/core/qgsvectorlayer.cpp
  77. +4 −1 src/core/qgsvectorlayer.h
  78. +1 −14 src/gui/editorwidgets/qgsrelationreferenceconfigdlg.cpp
  79. +6 −8 src/gui/qgsdatadefinedbutton.cpp
  80. +9 −13 src/gui/qgsdatadefinedbutton.h
  81. +13 −15 src/gui/qgsfieldexpressionwidget.cpp
  82. +10 −13 src/gui/qgsfieldexpressionwidget.h
  83. +24 −26 src/gui/symbology-ng/qgscategorizedsymbolrendererwidget.cpp
  84. +3 −1 src/gui/symbology-ng/qgscategorizedsymbolrendererwidget.h
  85. +7 −9 src/gui/symbology-ng/qgsgraduatedsymbolrendererwidget.cpp
  86. +4 −2 src/gui/symbology-ng/qgsgraduatedsymbolrendererwidget.h
  87. +7 −9 src/gui/symbology-ng/qgsheatmaprendererwidget.cpp
  88. +4 −2 src/gui/symbology-ng/qgsheatmaprendererwidget.h
  89. +7 −9 src/gui/symbology-ng/qgsrendererwidget.cpp
  90. +13 −4 src/gui/symbology-ng/qgsrendererwidget.h
  91. +7 −9 src/gui/symbology-ng/qgssizescalewidget.cpp
  92. +2 −1 src/gui/symbology-ng/qgssizescalewidget.h
  93. +20 −24 src/gui/symbology-ng/qgssymbollayerwidget.cpp
  94. +8 −5 src/gui/symbology-ng/qgssymbollayerwidget.h
  95. +8 −12 src/gui/symbology-ng/qgssymbolslistwidget.cpp
  96. +4 −3 src/gui/symbology-ng/qgssymbolslistwidget.h
  97. +4 −4 src/gui/symbology-ng/qgsvectorfieldsymbollayerwidget.cpp
  98. +13 −13 tests/src/core/testqgscomposermapgrid.cpp
  99. +1 −1 tests/src/python/acceptable_missing_doc.py
  100. +39 −11 tests/src/python/test_qgsdoccoverage.py
  101. +14 −8 tests/src/python/test_qgssipcoverage.py
  102. +5 −5 tests/src/python/utilities.py
@@ -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
@@ -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
@@ -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>
@@ -281,5 +281,5 @@ class QgsComposerAttributeTableV2 : QgsComposerTableV2
*/
bool getTableContents( QgsComposerTableContents &contents );

virtual QgsExpressionContext* createExpressionContext() const /Factory/;
virtual QgsExpressionContext createExpressionContext() const;
};
@@ -78,5 +78,5 @@ class QgsComposerFrame: QgsComposerItem
*/
bool isEmpty() const;

virtual QgsExpressionContext* createExpressionContext() const;
virtual QgsExpressionContext createExpressionContext() const;
};
@@ -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
@@ -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();
@@ -796,6 +796,6 @@ class QgsComposerMapGrid : QgsComposerMapItem
*/
QColor frameFillColor2() const;

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

};
@@ -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:

@@ -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();
@@ -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
@@ -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;
};
@@ -324,6 +324,8 @@ class QgsProject : QObject
*/
void setEvaluateDefaultValues( bool evaluateDefaultValues );

QgsExpressionContext createExpressionContext() const;

protected:

/** Set error message from read/write operation
@@ -1385,6 +1385,8 @@ class QgsVectorLayer : QgsMapLayer
*/
void setMapTipTemplate( const QString& mapTipTemplate );

QgsExpressionContext createExpressionContext() const;

public slots:
/**
* Select feature by its ID
@@ -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.
@@ -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
@@ -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;
@@ -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:
/**
@@ -68,10 +68,4 @@ class QgsSymbolsListWidget : QWidget

signals:
void changed();

protected:
void populateSymbolView();
void populateSymbols( const QStringList& symbols );
void updateSymbolColor();
void updateSymbolInfo();
};
@@ -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 )
{
@@ -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();
}
@@ -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 )
@@ -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 )
@@ -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
@@ -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() ) );
@@ -23,6 +23,7 @@
#include <QSet>
#include <QItemDelegate>
#include "ui_qgsattributeselectiondialogbase.h"
#include "qgsexpressioncontextgenerator.h"

class QGridLayout;
class QgsVectorLayer;
@@ -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

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

// QgsComposerColumnWidthDelegate
@@ -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 )
{
@@ -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 )
{

1 comment on commit 58ea211

@nyalldawson

This comment has been minimized.

Copy link
Collaborator

@nyalldawson nyalldawson commented on 58ea211 Aug 10, 2016

Belated +1 ;)

Please sign in to comment.
You can’t perform that action at this time.