Skip to content

Commit

Permalink
Merge pull request #6513 from pblottiere/executesql_params
Browse files Browse the repository at this point in the history
[FEATURE][needs-docs] Add parameters to 'Execute SQL' algorithm
  • Loading branch information
Hugo Mercier committed Apr 27, 2018
2 parents 9ce21e4 + 2324706 commit 3984e90
Show file tree
Hide file tree
Showing 13 changed files with 397 additions and 6 deletions.
9 changes: 9 additions & 0 deletions python/core/expression/qgsexpression.sip.in
Expand Up @@ -369,6 +369,15 @@ Additional substitutions can be passed through the substitutionMap parameter
and area conversion

.. versionadded:: 2.12
%End

static QSet<QString> referencedVariables( const QString &text );
%Docstring
This function returns variables in each expression between [% and %].

:param text: The source string in which variables should be searched.

.. versionadded:: 3.2
%End

static double evaluateToDouble( const QString &text, const double fallbackValue );
Expand Down
Expand Up @@ -29,6 +29,7 @@ Source for the value of a parameter for a child algorithm within a model.
ChildOutput,
StaticValue,
Expression,
ExpressionText,
};

QgsProcessingModelChildParameterSource();
Expand All @@ -49,6 +50,8 @@ Returns a new QgsProcessingModelChildParameterSource which takes its value from
.. seealso:: :py:func:`fromChildOutput`

.. seealso:: :py:func:`fromExpression`

.. seealso:: :py:func:`fromExpressionText`
%End

static QgsProcessingModelChildParameterSource fromModelParameter( const QString &parameterName );
Expand All @@ -60,6 +63,8 @@ Returns a new QgsProcessingModelChildParameterSource which takes its value from
.. seealso:: :py:func:`fromChildOutput`

.. seealso:: :py:func:`fromExpression`

.. seealso:: :py:func:`fromExpressionText`
%End

static QgsProcessingModelChildParameterSource fromChildOutput( const QString &childId, const QString &outputName );
Expand All @@ -71,6 +76,8 @@ Returns a new QgsProcessingModelChildParameterSource which takes its value from
.. seealso:: :py:func:`fromModelParameter`

.. seealso:: :py:func:`fromExpression`

.. seealso:: :py:func:`fromExpressionText`
%End

static QgsProcessingModelChildParameterSource fromExpression( const QString &expression );
Expand All @@ -85,6 +92,29 @@ executed by the model.
.. seealso:: :py:func:`fromChildOutput`

.. seealso:: :py:func:`fromModelParameter`

.. seealso:: :py:func:`fromExpressionText`

.. versionadded:: 3.2
%End

static QgsProcessingModelChildParameterSource fromExpressionText( const QString &text );
%Docstring
Returns a new QgsProcessingModelChildParameterSource which takes its
value from a text with expressions. Expressions are evaluated just before
the child algorithm executes, and can use functions available
in its expression context to include results calculated from the child
algorithms already executed by the model.

.. seealso:: :py:func:`fromStaticValue`

.. seealso:: :py:func:`fromChildOutput`

.. seealso:: :py:func:`fromModelParameter`

.. seealso:: :py:func:`fromExpression`

.. versionadded:: 3.2
%End

Source source() const;
Expand Down Expand Up @@ -171,6 +201,29 @@ in its expression context to include results calculated from the child algorithm
executed by the model.

.. seealso:: :py:func:`expression`
%End

QString expressionText() const;
%Docstring
Returns the source's text with expressions. This is only used when the
source() is ExpressionText.

.. seealso:: :py:func:`setExpressionText`

.. versionadded:: 3.2
%End

void setExpressionText( const QString &text );
%Docstring
Sets the source's text containing expressions. Calling this will also
change the source() to ExpressionText. Expressions are evaluated just
before the child algorithm executes, and can use functions available
in its expression context to include results calculated from the child
algorithms already executed by the model.

.. seealso:: :py:func:`expressionText`

.. versionadded:: 3.2
%End

QVariant toVariant() const;
Expand Down
9 changes: 9 additions & 0 deletions python/gui/qgsfieldexpressionwidget.sip.in
Expand Up @@ -36,6 +36,15 @@ only when editing in the line edit is finished (focus lost, enter key pressed).
void setExpressionDialogTitle( const QString &title );
%Docstring
define the title used in the expression dialog
%End

void appendScope( QgsExpressionContextScope *scope /Transfer/ );
%Docstring
Appends a scope to the current expression context.

:param scope: The scope to add.

.. versionadded:: 3.2
%End

const QString expressionDialogTitle();
Expand Down
27 changes: 22 additions & 5 deletions python/plugins/processing/algs/qgis/ExecuteSQL.py
Expand Up @@ -30,6 +30,8 @@
QgsWkbTypes,
QgsProcessingAlgorithm,
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterDefinition,
QgsExpression,
QgsProcessingParameterString,
QgsProcessingParameterEnum,
QgsProcessingParameterCrs,
Expand All @@ -40,6 +42,21 @@
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm


class ParameterExecuteSql(QgsProcessingParameterDefinition):

def __init__(self, name='', description=''):
super().__init__(name, description)
self.setMetadata({
'widget_wrapper': 'processing.algs.qgis.ui.ExecuteSQLWidget.ExecuteSQLWidgetWrapper'
})

def type(self):
return 'execute_sql'

def clone(self):
return ParameterExecuteSql(self.name(), self.description())


class ExecuteSQL(QgisAlgorithm):

""" This algorithm allows executing an SQL query on a set of input
Expand Down Expand Up @@ -71,9 +88,7 @@ def initAlgorithm(self, config=None):
description=self.tr('Additional input datasources (called input1, .., inputN in the query)'),
optional=True))

self.addParameter(QgsProcessingParameterString(name=self.INPUT_QUERY,
description=self.tr('SQL query'),
multiLine=True))
self.addParameter(ParameterExecuteSql(name=self.INPUT_QUERY, description=self.tr('SQL query')))

self.addParameter(QgsProcessingParameterString(name=self.INPUT_UID_FIELD,
description=self.tr('Unique identifier field'), optional=True))
Expand Down Expand Up @@ -120,13 +135,15 @@ def processAlgorithm(self, parameters, context, feedback):
raise QgsProcessingException(
self.tr('Empty SQL. Please enter valid SQL expression and try again.'))
else:
df.setQuery(query)
localContext = self.createExpressionContext(parameters, context)
expandedQuery = QgsExpression.replaceExpressionText(query, localContext)
df.setQuery(expandedQuery)

if uid_field:
df.setUid(uid_field)

if geometry_type == 1: # no geometry
df.setGeometryWkbType(QgsWkbTypes.NullGeometry)
df.setGeometryWkbType(QgsWkbTypes.NoGeometry)
else:
if geometry_field:
df.setGeometryField(geometry_field)
Expand Down
135 changes: 135 additions & 0 deletions python/plugins/processing/algs/qgis/ui/ExecuteSQLWidget.py
@@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-

"""
***************************************************************************
ExecuteSQLWidget.py
---------------------
Date : November 2017
Copyright : (C) 2017 by Paul Blottiere
Email : blottiere dot paul 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. *
* *
***************************************************************************
"""

__author__ = 'Paul Blottiere'
__date__ = 'November 2018'
__copyright__ = '(C) 2018, Paul Blottiere'

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = '$Format:%H$'

import os

from qgis.PyQt import uic
from qgis.PyQt.QtWidgets import QTreeWidgetItem
from qgis.PyQt.QtCore import Qt

from qgis.core import (QgsApplication,
QgsExpressionContextScope,
QgsProcessingParameterString,
QgsProcessingParameterNumber,
QgsExpression,
QgsProcessingModelChildParameterSource,
QgsProcessingParameterFile,
QgsProcessingParameterField,
QgsProcessingOutputString,
QgsProcessingParameterExpression,
QgsProcessingOutputFile)

from qgis.gui import QgsFieldExpressionWidget

from processing.gui.wrappers import (WidgetWrapper,
dialogTypes,
DIALOG_MODELER)

pluginPath = os.path.dirname(__file__)
WIDGET, BASE = uic.loadUiType(os.path.join(pluginPath, 'ExecuteSQLWidgetBase.ui'))


class ExecuteSQLWidget(BASE, WIDGET):

def __init__(self, dialog):
super(ExecuteSQLWidget, self).__init__(None)
self.setupUi(self)
self.dialog = dialog
self.dialogType = dialogTypes[dialog.__class__.__name__]

self.mExpressionWidget = QgsFieldExpressionWidget()

# add model parameters in context scope if called from modeler
if self.dialogType == DIALOG_MODELER:
strings = dialog.getAvailableValuesOfType(
[QgsProcessingParameterString, QgsProcessingParameterNumber], [])
model_params = [dialog.resolveValueDescription(s) for s in strings]

scope = QgsExpressionContextScope()
for param in model_params:
var = QgsExpressionContextScope.StaticVariable(param)
scope.addVariable(var)

self.mExpressionWidget.appendScope(scope)

self.mHLayout.insertWidget(0, self.mExpressionWidget)

self.mInsert.clicked.connect(self.insert)

def insert(self):
if self.mExpressionWidget.currentText():
exp = '[% {} %]'.format(self.mExpressionWidget.currentText())
self.mText.insertPlainText(exp)

def setValue(self, value):
text = value

if self.dialogType == DIALOG_MODELER:
if isinstance(value, list):
for v in value:
if isinstance(v, QgsProcessingModelChildParameterSource) \
and v.source() == QgsProcessingModelChildParameterSource.ExpressionText:
text = v.expressionText()

self.mText.setPlainText(text)

def value(self):
value = self.mText.toPlainText()

if self.dialogType == DIALOG_MODELER:
expression_values = self._expressionValues(value)
if len(expression_values) > 1:
value = expression_values

return value

def _expressionValues(self, text):
strings = self.dialog.getAvailableValuesOfType(
[QgsProcessingParameterString, QgsProcessingParameterNumber], [])
model_params = [(self.dialog.resolveValueDescription(s), s) for s in strings]

variables = QgsExpression.referencedVariables(text)
expression_values = []
expression_values.append(QgsProcessingModelChildParameterSource.fromExpressionText(text))

for k, v in model_params:
if k in variables:
expression_values.append(v)

return expression_values


class ExecuteSQLWidgetWrapper(WidgetWrapper):

def createWidget(self):
return ExecuteSQLWidget(self.dialog)

def setValue(self, value):
self.widget.setValue(value)

def value(self):
return self.widget.value()
48 changes: 48 additions & 0 deletions python/plugins/processing/algs/qgis/ui/ExecuteSQLWidgetBase.ui
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0" rowspan="2" colspan="2">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="topMargin">
<number>10</number>
</property>
<item>
<widget class="QPlainTextEdit" name="mText"/>
</item>
<item>
<layout class="QHBoxLayout" name="mHLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="mInsert">
<property name="text">
<string>Insert</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

0 comments on commit 3984e90

Please sign in to comment.