mirror of
https://github.com/qgis/QGIS.git
synced 2025-11-11 00:06:09 -05:00
Merge pull request #4943 from nyalldawson/exp_layer_rel
Add items for project map layers and relations to expression builder
This commit is contained in:
commit
0328b7af48
@ -236,6 +236,30 @@ Sets the expression string for the widget
|
|||||||
Update the list of function files found at the given path
|
Update the list of function files found at the given path
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
QStandardItemModel *model();
|
||||||
|
%Docstring
|
||||||
|
Returns a pointer to the dialog's function item model.
|
||||||
|
This method is exposed for testing purposes only - it should not be used to modify the model.
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
:rtype: QStandardItemModel
|
||||||
|
%End
|
||||||
|
|
||||||
|
QgsProject *project();
|
||||||
|
%Docstring
|
||||||
|
Returns the project currently associated with the widget.
|
||||||
|
.. seealso:: setProject()
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
:rtype: QgsProject
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setProject( QgsProject *project );
|
||||||
|
%Docstring
|
||||||
|
Sets the ``project`` currently associated with the widget. This
|
||||||
|
controls which layers and relations and other project-specific items are shown in the widget.
|
||||||
|
.. seealso:: project()
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
%End
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
void loadSampleValues();
|
void loadSampleValues();
|
||||||
|
|||||||
5
resources/function_help/json/Map Layers
Normal file
5
resources/function_help/json/Map Layers
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "Map Layers",
|
||||||
|
"type": "group",
|
||||||
|
"description": "Contains a list of map layers available in the current project."
|
||||||
|
}
|
||||||
5
resources/function_help/json/Relations
Normal file
5
resources/function_help/json/Relations
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "Relations",
|
||||||
|
"type": "group",
|
||||||
|
"description": "Contains a list of relations available in the current project."
|
||||||
|
}
|
||||||
@ -25,6 +25,9 @@
|
|||||||
#include "qgsfeatureiterator.h"
|
#include "qgsfeatureiterator.h"
|
||||||
#include "qgsvectorlayer.h"
|
#include "qgsvectorlayer.h"
|
||||||
#include "qgssettings.h"
|
#include "qgssettings.h"
|
||||||
|
#include "qgsproject.h"
|
||||||
|
#include "qgsrelationmanager.h"
|
||||||
|
#include "qgsrelation.h"
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@ -42,6 +45,7 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent )
|
|||||||
, mLayer( nullptr )
|
, mLayer( nullptr )
|
||||||
, highlighter( nullptr )
|
, highlighter( nullptr )
|
||||||
, mExpressionValid( false )
|
, mExpressionValid( false )
|
||||||
|
, mProject( QgsProject::instance() )
|
||||||
{
|
{
|
||||||
setupUi( this );
|
setupUi( this );
|
||||||
|
|
||||||
@ -440,6 +444,32 @@ void QgsExpressionBuilderWidget::loadRecent( const QString &collection )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QgsExpressionBuilderWidget::loadLayers()
|
||||||
|
{
|
||||||
|
if ( !mProject )
|
||||||
|
return;
|
||||||
|
|
||||||
|
QMap<QString, QgsMapLayer *> layers = mProject->mapLayers();
|
||||||
|
QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
|
||||||
|
for ( ; layerIt != layers.constEnd(); ++layerIt )
|
||||||
|
{
|
||||||
|
registerItemForAllGroups( QStringList() << tr( "Map Layers" ), layerIt.value()->name(), QStringLiteral( "'%1'" ).arg( layerIt.key() ), formatLayerHelp( layerIt.value() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsExpressionBuilderWidget::loadRelations()
|
||||||
|
{
|
||||||
|
if ( !mProject )
|
||||||
|
return;
|
||||||
|
|
||||||
|
QMap<QString, QgsRelation> relations = mProject->relationManager()->relations();
|
||||||
|
QMap<QString, QgsRelation>::const_iterator relIt = relations.constBegin();
|
||||||
|
for ( ; relIt != relations.constEnd(); ++relIt )
|
||||||
|
{
|
||||||
|
registerItemForAllGroups( QStringList() << tr( "Relations" ), relIt->name(), QStringLiteral( "'%1'" ).arg( relIt->id() ), formatRelationHelp( relIt.value() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QgsExpressionBuilderWidget::updateFunctionTree()
|
void QgsExpressionBuilderWidget::updateFunctionTree()
|
||||||
{
|
{
|
||||||
mModel->clear();
|
mModel->clear();
|
||||||
@ -495,6 +525,12 @@ void QgsExpressionBuilderWidget::updateFunctionTree()
|
|||||||
registerItemForAllGroups( func->groups(), func->name(), ' ' + name + ' ', func->helpText() );
|
registerItemForAllGroups( func->groups(), func->name(), ' ' + name + ' ', func->helpText() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load relation names
|
||||||
|
loadRelations();
|
||||||
|
|
||||||
|
// load layer IDs
|
||||||
|
loadLayers();
|
||||||
|
|
||||||
loadExpressionContext();
|
loadExpressionContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,6 +650,36 @@ void QgsExpressionBuilderWidget::registerItemForAllGroups( const QStringList &gr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString QgsExpressionBuilderWidget::formatRelationHelp( const QgsRelation &relation ) const
|
||||||
|
{
|
||||||
|
QString text = QStringLiteral( "<p>%1</p>" ).arg( tr( "Inserts the relation ID for the relation named '%1'." ).arg( relation.name() ) );
|
||||||
|
text.append( QStringLiteral( "<p>%1</p>" ).arg( tr( "Current value: '%1'" ).arg( relation.id() ) ) );
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsExpressionBuilderWidget::formatLayerHelp( const QgsMapLayer *layer ) const
|
||||||
|
{
|
||||||
|
QString text = QStringLiteral( "<p>%1</p>" ).arg( tr( "Inserts the layer ID for the layer named '%1'." ).arg( layer->name() ) );
|
||||||
|
text.append( QStringLiteral( "<p>%1</p>" ).arg( tr( "Current value: '%1'" ).arg( layer->id() ) ) );
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStandardItemModel *QgsExpressionBuilderWidget::model()
|
||||||
|
{
|
||||||
|
return mModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsProject *QgsExpressionBuilderWidget::project()
|
||||||
|
{
|
||||||
|
return mProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsExpressionBuilderWidget::setProject( QgsProject *project )
|
||||||
|
{
|
||||||
|
mProject = project;
|
||||||
|
updateFunctionTree();
|
||||||
|
}
|
||||||
|
|
||||||
void QgsExpressionBuilderWidget::showEvent( QShowEvent *e )
|
void QgsExpressionBuilderWidget::showEvent( QShowEvent *e )
|
||||||
{
|
{
|
||||||
QWidget::showEvent( e );
|
QWidget::showEvent( e );
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
class QgsFields;
|
class QgsFields;
|
||||||
class QgsExpressionHighlighter;
|
class QgsExpressionHighlighter;
|
||||||
|
class QgsRelation;
|
||||||
|
|
||||||
/** \ingroup gui
|
/** \ingroup gui
|
||||||
* An expression item that can be used in the QgsExpressionBuilderWidget tree.
|
* An expression item that can be used in the QgsExpressionBuilderWidget tree.
|
||||||
@ -225,6 +226,28 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp
|
|||||||
*/
|
*/
|
||||||
void updateFunctionFileList( const QString &path );
|
void updateFunctionFileList( const QString &path );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a pointer to the dialog's function item model.
|
||||||
|
* This method is exposed for testing purposes only - it should not be used to modify the model.
|
||||||
|
* \since QGIS 3.0
|
||||||
|
*/
|
||||||
|
QStandardItemModel *model();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the project currently associated with the widget.
|
||||||
|
* \see setProject()
|
||||||
|
* \since QGIS 3.0
|
||||||
|
*/
|
||||||
|
QgsProject *project();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the \a project currently associated with the widget. This
|
||||||
|
* controls which layers and relations and other project-specific items are shown in the widget.
|
||||||
|
* \see project()
|
||||||
|
* \since QGIS 3.0
|
||||||
|
*/
|
||||||
|
void setProject( QgsProject *project );
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -286,6 +309,12 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp
|
|||||||
|
|
||||||
void loadExpressionContext();
|
void loadExpressionContext();
|
||||||
|
|
||||||
|
//! Loads current project relations names/id into the expression help tree
|
||||||
|
void loadRelations();
|
||||||
|
|
||||||
|
//! Loads current project layer names/ids into the expression help tree
|
||||||
|
void loadLayers();
|
||||||
|
|
||||||
/** Registers a node item for the expression builder, adding multiple items when the function exists in multiple groups
|
/** Registers a node item for the expression builder, adding multiple items when the function exists in multiple groups
|
||||||
* \param groups The groups the item will be show in the tree view. If a group doesn't exist it will be created.
|
* \param groups The groups the item will be show in the tree view. If a group doesn't exist it will be created.
|
||||||
* \param label The label that is show to the user for the item in the tree.
|
* \param label The label that is show to the user for the item in the tree.
|
||||||
@ -300,6 +329,16 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp
|
|||||||
QgsExpressionItem::ItemType type = QgsExpressionItem::ExpressionNode,
|
QgsExpressionItem::ItemType type = QgsExpressionItem::ExpressionNode,
|
||||||
bool highlightedItem = false, int sortOrder = 1 );
|
bool highlightedItem = false, int sortOrder = 1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a HTML formatted string for use as a \a relation item help.
|
||||||
|
*/
|
||||||
|
QString formatRelationHelp( const QgsRelation &relation ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a HTML formatted string for use as a \a layer item help.
|
||||||
|
*/
|
||||||
|
QString formatLayerHelp( const QgsMapLayer *layer ) const;
|
||||||
|
|
||||||
bool mAutoSave;
|
bool mAutoSave;
|
||||||
QString mFunctionsPath;
|
QString mFunctionsPath;
|
||||||
QgsVectorLayer *mLayer = nullptr;
|
QgsVectorLayer *mLayer = nullptr;
|
||||||
@ -314,6 +353,7 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp
|
|||||||
QString mRecentKey;
|
QString mRecentKey;
|
||||||
QMap<QString, QStringList> mFieldValues;
|
QMap<QString, QStringList> mFieldValues;
|
||||||
QgsExpressionContext mExpressionContext;
|
QgsExpressionContext mExpressionContext;
|
||||||
|
QPointer< QgsProject > mProject;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QGSEXPRESSIONBUILDER_H
|
#endif // QGSEXPRESSIONBUILDER_H
|
||||||
|
|||||||
@ -47,6 +47,7 @@ ADD_PYTHON_TEST(PyQgsDistanceArea test_qgsdistancearea.py)
|
|||||||
ADD_PYTHON_TEST(PyQgsEditWidgets test_qgseditwidgets.py)
|
ADD_PYTHON_TEST(PyQgsEditWidgets test_qgseditwidgets.py)
|
||||||
ADD_PYTHON_TEST(PyQgsEllipsoidUtils test_qgsellipsoidutils.py)
|
ADD_PYTHON_TEST(PyQgsEllipsoidUtils test_qgsellipsoidutils.py)
|
||||||
ADD_PYTHON_TEST(PyQgsExpression test_qgsexpression.py)
|
ADD_PYTHON_TEST(PyQgsExpression test_qgsexpression.py)
|
||||||
|
ADD_PYTHON_TEST(PyQgsExpressionBuilderWidget test_qgsexpressionbuilderwidget.py)
|
||||||
ADD_PYTHON_TEST(PyQgsExpressionLineEdit test_qgsexpressionlineedit.py)
|
ADD_PYTHON_TEST(PyQgsExpressionLineEdit test_qgsexpressionlineedit.py)
|
||||||
ADD_PYTHON_TEST(PyQgsExtentGroupBox test_qgsextentgroupbox.py)
|
ADD_PYTHON_TEST(PyQgsExtentGroupBox test_qgsextentgroupbox.py)
|
||||||
ADD_PYTHON_TEST(PyQgsFeature test_qgsfeature.py)
|
ADD_PYTHON_TEST(PyQgsFeature test_qgsfeature.py)
|
||||||
|
|||||||
171
tests/src/python/test_qgsexpressionbuilderwidget.py
Normal file
171
tests/src/python/test_qgsexpressionbuilderwidget.py
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""QGIS Unit tests for QgsExpressionBuilderWidget
|
||||||
|
|
||||||
|
.. note:: 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.
|
||||||
|
"""
|
||||||
|
__author__ = 'Nyall Dawson'
|
||||||
|
__date__ = '30/07/2017'
|
||||||
|
__copyright__ = 'Copyright 2017, The QGIS Project'
|
||||||
|
# This will get replaced with a git SHA1 when you do a git archive
|
||||||
|
__revision__ = '$Format:%H$'
|
||||||
|
|
||||||
|
import qgis # NOQA
|
||||||
|
|
||||||
|
from qgis.PyQt.QtCore import Qt
|
||||||
|
from qgis.testing import start_app, unittest
|
||||||
|
from qgis.gui import QgsExpressionBuilderWidget
|
||||||
|
from qgis.core import (QgsExpressionContext,
|
||||||
|
QgsExpressionContextScope,
|
||||||
|
QgsProject,
|
||||||
|
QgsVectorLayer,
|
||||||
|
QgsRelation,
|
||||||
|
QgsFeature,
|
||||||
|
QgsGeometry)
|
||||||
|
start_app()
|
||||||
|
|
||||||
|
|
||||||
|
def createReferencingLayer():
|
||||||
|
layer = QgsVectorLayer("Point?field=fldtxt:string&field=foreignkey:integer",
|
||||||
|
"referencinglayer", "memory")
|
||||||
|
pr = layer.dataProvider()
|
||||||
|
f1 = QgsFeature()
|
||||||
|
f1.setFields(layer.pendingFields())
|
||||||
|
f1.setAttributes(["test1", 123])
|
||||||
|
f2 = QgsFeature()
|
||||||
|
f2.setFields(layer.pendingFields())
|
||||||
|
f2.setAttributes(["test2", 123])
|
||||||
|
f3 = QgsFeature()
|
||||||
|
f3.setFields(layer.pendingFields())
|
||||||
|
f3.setAttributes(["foobar'bar", 124])
|
||||||
|
assert pr.addFeatures([f1, f2, f3])
|
||||||
|
return layer
|
||||||
|
|
||||||
|
|
||||||
|
def createReferencedLayer():
|
||||||
|
layer = QgsVectorLayer(
|
||||||
|
"Point?field=x:string&field=y:integer&field=z:integer",
|
||||||
|
"referencedlayer", "memory")
|
||||||
|
pr = layer.dataProvider()
|
||||||
|
f1 = QgsFeature()
|
||||||
|
f1.setFields(layer.pendingFields())
|
||||||
|
f1.setAttributes(["foo", 123, 321])
|
||||||
|
f2 = QgsFeature()
|
||||||
|
f2.setFields(layer.pendingFields())
|
||||||
|
f2.setAttributes(["bar", 456, 654])
|
||||||
|
f3 = QgsFeature()
|
||||||
|
f3.setFields(layer.pendingFields())
|
||||||
|
f3.setAttributes(["foobar'bar", 789, 554])
|
||||||
|
assert pr.addFeatures([f1, f2, f3])
|
||||||
|
return layer
|
||||||
|
|
||||||
|
|
||||||
|
class TestQgsExpressionBuilderWidget(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.referencedLayer = createReferencedLayer()
|
||||||
|
self.referencingLayer = createReferencingLayer()
|
||||||
|
QgsProject.instance().addMapLayers([self.referencedLayer, self.referencingLayer])
|
||||||
|
|
||||||
|
def testFunctionPresent(self):
|
||||||
|
""" check through widget model to ensure it is initially populated with functions """
|
||||||
|
w = QgsExpressionBuilderWidget()
|
||||||
|
m = w.model()
|
||||||
|
# check that some standard expression functions are shown
|
||||||
|
items = m.findItems('lower', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
items = m.findItems('upper', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
items = m.findItems('asdasdasda#$@#$', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 0)
|
||||||
|
|
||||||
|
def testVariables(self):
|
||||||
|
""" check through widget model to ensure it is populated with variables """
|
||||||
|
w = QgsExpressionBuilderWidget()
|
||||||
|
m = w.model()
|
||||||
|
|
||||||
|
s = QgsExpressionContextScope()
|
||||||
|
s.setVariable('my_var1', 'x')
|
||||||
|
s.setVariable('my_var2', 'y')
|
||||||
|
c = QgsExpressionContext()
|
||||||
|
c.appendScope(s)
|
||||||
|
|
||||||
|
# check that variables are added when setting context
|
||||||
|
w.setExpressionContext(c)
|
||||||
|
items = m.findItems('my_var1', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
items = m.findItems('my_var2', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
items = m.findItems('not_my_var', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 0)
|
||||||
|
# double check that functions are still only there once
|
||||||
|
items = m.findItems('lower', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
items = m.findItems('upper', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
|
||||||
|
def testLayers(self):
|
||||||
|
""" check that layers are shown in widget model"""
|
||||||
|
p = QgsProject.instance()
|
||||||
|
layer = QgsVectorLayer("Point", "layer1", "memory")
|
||||||
|
layer2 = QgsVectorLayer("Point", "layer2", "memory")
|
||||||
|
p.addMapLayers([layer, layer2])
|
||||||
|
|
||||||
|
w = QgsExpressionBuilderWidget()
|
||||||
|
m = w.model()
|
||||||
|
|
||||||
|
# check that layers are shown
|
||||||
|
items = m.findItems('layer1', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
items = m.findItems('layer2', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
|
||||||
|
# change project
|
||||||
|
p2 = QgsProject()
|
||||||
|
layer3 = QgsVectorLayer("Point", "layer3", "memory")
|
||||||
|
p2.addMapLayers([layer3])
|
||||||
|
w.setProject(p2)
|
||||||
|
m = w.model()
|
||||||
|
items = m.findItems('layer1', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 0)
|
||||||
|
items = m.findItems('layer2', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 0)
|
||||||
|
items = m.findItems('layer3', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
|
||||||
|
def testRelations(self):
|
||||||
|
""" check that layers are shown in widget model"""
|
||||||
|
p = QgsProject.instance()
|
||||||
|
|
||||||
|
# not valid, but doesn't matter for test....
|
||||||
|
rel = QgsRelation()
|
||||||
|
rel.setId('rel1')
|
||||||
|
rel.setName('Relation Number One')
|
||||||
|
rel.setReferencingLayer(self.referencingLayer.id())
|
||||||
|
rel.setReferencedLayer(self.referencedLayer.id())
|
||||||
|
rel.addFieldPair('foreignkey', 'y')
|
||||||
|
|
||||||
|
rel2 = QgsRelation()
|
||||||
|
rel2.setId('rel2')
|
||||||
|
rel2.setName('Relation Number Two')
|
||||||
|
rel2.setReferencingLayer(self.referencingLayer.id())
|
||||||
|
rel2.setReferencedLayer(self.referencedLayer.id())
|
||||||
|
rel2.addFieldPair('foreignkey', 'y')
|
||||||
|
|
||||||
|
p.relationManager().addRelation(rel)
|
||||||
|
p.relationManager().addRelation(rel2)
|
||||||
|
|
||||||
|
w = QgsExpressionBuilderWidget()
|
||||||
|
m = w.model()
|
||||||
|
|
||||||
|
# check that relations are shown
|
||||||
|
items = m.findItems('Relation Number One', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
items = m.findItems('Relation Number Two', Qt.MatchRecursive)
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Loading…
x
Reference in New Issue
Block a user