mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-28 00:17:30 -05:00
[FEATURE] use expressions engine to evaluate feature actions
work done for Regione Toscana-SIGTA
This commit is contained in:
parent
35f5da70cd
commit
1fe82b316b
@ -22,11 +22,12 @@ back to QgsVectorLayer.
|
||||
|
||||
#include "qgsattributeactiondialog.h"
|
||||
#include "qgsattributeaction.h"
|
||||
#include "qgsexpressionbuilderdialog.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QHeaderView>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
QgsAttributeActionDialog::QgsAttributeActionDialog( QgsAttributeAction* actions,
|
||||
const QgsFieldMap& fields,
|
||||
@ -50,6 +51,7 @@ QgsAttributeActionDialog::QgsAttributeActionDialog( QgsAttributeAction* actions,
|
||||
connect( insertButton, SIGNAL( clicked() ), this, SLOT( insert() ) );
|
||||
connect( updateButton, SIGNAL( clicked() ), this, SLOT( update() ) );
|
||||
connect( insertFieldButton, SIGNAL( clicked() ), this, SLOT( insertField() ) );
|
||||
connect( insertExpressionButton, SIGNAL( clicked() ), this, SLOT( insertExpression() ) );
|
||||
|
||||
init();
|
||||
// Populate the combo box with the field names. Will the field names
|
||||
@ -143,9 +145,7 @@ void QgsAttributeActionDialog::swapRows( int row1, int row2 )
|
||||
|
||||
void QgsAttributeActionDialog::browse()
|
||||
{
|
||||
// Popup a file browser and place the results into the actionName
|
||||
// widget
|
||||
|
||||
// Popup a file browser and place the results into the action widget
|
||||
QString action = QFileDialog::getOpenFileName(
|
||||
this, tr( "Select an action", "File dialog window title" ) );
|
||||
|
||||
@ -153,6 +153,28 @@ void QgsAttributeActionDialog::browse()
|
||||
actionAction->insert( action );
|
||||
}
|
||||
|
||||
void QgsAttributeActionDialog::insertExpression()
|
||||
{
|
||||
QString selText = actionAction->selectedText();
|
||||
|
||||
// edit the selected expression if there's one
|
||||
if ( selText.startsWith( "[%" ) && selText.endsWith( "%]" ) )
|
||||
selText = selText.mid( 2, selText.size() - 3 );
|
||||
|
||||
// display the expression builder
|
||||
QgsExpressionBuilderDialog dlg( mActions->layer(), selText, this );
|
||||
dlg.setWindowTitle( tr( "Insert expression" ) );
|
||||
if ( dlg.exec() == QDialog::Accepted )
|
||||
{
|
||||
QString expression = dlg.expressionBuilder()->getExpressionString();
|
||||
//Only add the expression if the user has entered some text.
|
||||
if ( !expression.isEmpty() )
|
||||
{
|
||||
actionAction->insert( "[%" + expression + "%]" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAttributeActionDialog::remove()
|
||||
{
|
||||
QList<QTableWidgetItem *> selection = attributeActionTable->selectedItems();
|
||||
@ -234,13 +256,14 @@ void QgsAttributeActionDialog::update()
|
||||
|
||||
void QgsAttributeActionDialog::insertField()
|
||||
{
|
||||
// Take the selected field, preprend a % and insert into the action
|
||||
// field at the cursor position
|
||||
// Convert the selected field to an expression and
|
||||
// insert it into the action at the cursor position
|
||||
|
||||
if ( !fieldComboBox->currentText().isNull() )
|
||||
{
|
||||
QString field( "%" );
|
||||
QString field = "[% \"";
|
||||
field += fieldComboBox->currentText();
|
||||
field += "\" %]";
|
||||
actionAction->insert( field );
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ class QgsAttributeActionDialog: public QWidget, private Ui::QgsAttributeActionDi
|
||||
void remove();
|
||||
void insert();
|
||||
void insertField();
|
||||
void insertExpression();
|
||||
void apply();
|
||||
void update();
|
||||
void itemSelectionChanged();
|
||||
|
@ -36,7 +36,7 @@ QgsFeatureAction::QgsFeatureAction( const QString &name, QgsFeature &f, QgsVecto
|
||||
|
||||
void QgsFeatureAction::execute()
|
||||
{
|
||||
mLayer->actions()->doAction( mAction, mFeature.attributeMap(), mIdx );
|
||||
mLayer->actions()->doAction( mAction, mFeature, mIdx );
|
||||
}
|
||||
|
||||
QgsAttributeDialog *QgsFeatureAction::newDialog( bool cloneFeature )
|
||||
|
@ -429,7 +429,7 @@ void QgsIdentifyResults::contextMenuEvent( QContextMenuEvent* event )
|
||||
|
||||
mActionPopup = new QMenu();
|
||||
|
||||
int idx = 0;
|
||||
int idx = -1;
|
||||
QTreeWidgetItem *featItem = featureItem( item );
|
||||
if ( featItem )
|
||||
{
|
||||
@ -549,9 +549,7 @@ void QgsIdentifyResults::deactivate()
|
||||
|
||||
void QgsIdentifyResults::doAction( QTreeWidgetItem *item, int action )
|
||||
{
|
||||
int idx;
|
||||
QgsAttributeMap attributes;
|
||||
QTreeWidgetItem *featItem = retrieveAttributes( item, attributes, idx );
|
||||
QTreeWidgetItem *featItem = featureItem( item );
|
||||
if ( !featItem )
|
||||
return;
|
||||
|
||||
@ -559,7 +557,7 @@ void QgsIdentifyResults::doAction( QTreeWidgetItem *item, int action )
|
||||
if ( !layer )
|
||||
return;
|
||||
|
||||
idx = -1;
|
||||
int idx = -1;
|
||||
if ( item->parent() == featItem )
|
||||
{
|
||||
QString fieldName = item->data( 0, Qt::DisplayRole ).toString();
|
||||
@ -574,7 +572,8 @@ void QgsIdentifyResults::doAction( QTreeWidgetItem *item, int action )
|
||||
}
|
||||
}
|
||||
|
||||
layer->actions()->doAction( action, attributes, idx );
|
||||
int featIdx = featItem->data( 0, Qt::UserRole + 1 ).toInt();
|
||||
layer->actions()->doAction( action, mFeatures[ featIdx ], idx );
|
||||
}
|
||||
|
||||
QTreeWidgetItem *QgsIdentifyResults::featureItem( QTreeWidgetItem *item )
|
||||
|
@ -22,15 +22,23 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include <QList>
|
||||
|
||||
#include <QStringList>
|
||||
#include <QDomElement>
|
||||
|
||||
#include "qgsattributeaction.h"
|
||||
#include "qgspythonrunner.h"
|
||||
#include "qgsrunprocess.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsproject.h"
|
||||
#include <qgslogger.h>
|
||||
#include "qgsexpression.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QStringList>
|
||||
#include <QDomElement>
|
||||
#include <QSettings>
|
||||
#include <QDesktopServices>
|
||||
#include <QUrl>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
|
||||
void QgsAttributeAction::addAction( QgsAction::ActionType type, QString name, QString action, bool capture )
|
||||
{
|
||||
@ -44,7 +52,6 @@ void QgsAttributeAction::doAction( int index, const QgsAttributeMap &attributes,
|
||||
return;
|
||||
|
||||
const QgsAction &action = at( index );
|
||||
|
||||
if ( !action.runable() )
|
||||
return;
|
||||
|
||||
@ -58,29 +65,67 @@ void QgsAttributeAction::doAction( int index, const QgsAttributeMap &attributes,
|
||||
// the UI and the code in this function to select on the
|
||||
// action.capture() return value.
|
||||
|
||||
// The QgsRunProcess instance created by this static function
|
||||
// deletes itself when no longer needed.
|
||||
QString expandedAction = expandAction( action.action(), attributes, defaultValueIndex );
|
||||
if ( expandedAction.isEmpty() )
|
||||
return;
|
||||
|
||||
QgsAction newAction( action.type(), action.name(), expandedAction, action.capture() );
|
||||
runAction( newAction, executePython );
|
||||
}
|
||||
|
||||
void QgsAttributeAction::doAction( int index, QgsFeature &feat, int defaultValueIndex )
|
||||
{
|
||||
QMap<QString, QVariant> substitutionMap;
|
||||
if ( defaultValueIndex >= 0 )
|
||||
substitutionMap.insert( "$currfield", QVariant( defaultValueIndex ) );
|
||||
|
||||
doAction( index, feat, &substitutionMap );
|
||||
}
|
||||
|
||||
void QgsAttributeAction::doAction( int index, QgsFeature &feat,
|
||||
const QMap<QString, QVariant> *substitutionMap )
|
||||
{
|
||||
if ( index < 0 || index >= size() )
|
||||
return;
|
||||
|
||||
const QgsAction &action = at( index );
|
||||
if ( !action.runable() )
|
||||
return;
|
||||
|
||||
// search for expressions while expanding actions
|
||||
QString expandedAction = expandAction( action.action(), feat, substitutionMap );
|
||||
if ( expandedAction.isEmpty() )
|
||||
return;
|
||||
|
||||
QgsAction newAction( action.type(), action.name(), expandedAction, action.capture() );
|
||||
runAction( newAction );
|
||||
}
|
||||
|
||||
void QgsAttributeAction::runAction( const QgsAction &action, void ( *executePython )( const QString & ) )
|
||||
{
|
||||
if ( action.type() == QgsAction::GenericPython )
|
||||
{
|
||||
if ( executePython )
|
||||
{
|
||||
// deprecated
|
||||
executePython( expandedAction );
|
||||
executePython( action.action() );
|
||||
}
|
||||
else if ( smPythonExecute )
|
||||
{
|
||||
// deprecated
|
||||
smPythonExecute( expandedAction );
|
||||
smPythonExecute( action.action() );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsPythonRunner::run( expandedAction );
|
||||
// TODO: capture output from QgsPythonRunner
|
||||
QgsPythonRunner::run( action.action() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsRunProcess::create( expandedAction, action.capture() );
|
||||
// The QgsRunProcess instance created by this static function
|
||||
// deletes itself when no longer needed.
|
||||
QgsRunProcess::create( action.action(), action.capture() );
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,7 +134,7 @@ QString QgsAttributeAction::expandAction( QString action, const QgsAttributeMap
|
||||
{
|
||||
// This function currently replaces all %% characters in the action
|
||||
// with the value from values[clickedOnValue].second, and then
|
||||
// searches for all strings that go %attribite_name, where
|
||||
// searches for all strings that go %attribute_name, where
|
||||
// attribute_name is found in values[x].first, and replaces any that
|
||||
// it finds by values[s].second.
|
||||
|
||||
@ -134,6 +179,63 @@ QString QgsAttributeAction::expandAction( QString action, const QgsAttributeMap
|
||||
return expanded_action;
|
||||
}
|
||||
|
||||
QString QgsAttributeAction::expandAction( QString action, QgsFeature &feat, const QMap<QString, QVariant> *substitutionMap )
|
||||
{
|
||||
// This function currently replaces each expression between [% and %]
|
||||
// in the action with the result of its evaluation on the feature
|
||||
// passed as argument.
|
||||
|
||||
// Additional substitutions can be passed through the substitutionMap
|
||||
// parameter
|
||||
|
||||
QString expr_action;
|
||||
|
||||
int index = 0;
|
||||
while ( index < action.size() )
|
||||
{
|
||||
QRegExp rx = QRegExp( "\\[%([^\\]]+)%\\]" );
|
||||
|
||||
int pos = rx.indexIn( action, index );
|
||||
if ( pos < 0 )
|
||||
break;
|
||||
|
||||
int start = index;
|
||||
index = pos + rx.matchedLength();
|
||||
|
||||
QString to_replace = rx.cap(1).trimmed();
|
||||
QgsDebugMsg( "Found expression:" + to_replace );
|
||||
|
||||
if ( substitutionMap && substitutionMap->contains( to_replace ) )
|
||||
{
|
||||
expr_action += action.mid( start, pos - start ) + substitutionMap->value( to_replace ).toString();
|
||||
continue;
|
||||
}
|
||||
|
||||
QgsExpression* exp = new QgsExpression( to_replace );
|
||||
if ( exp->hasParserError() )
|
||||
{
|
||||
QgsDebugMsg( "Expression parser error:" + exp->parserErrorString() );
|
||||
expr_action += action.mid( start, index - start );
|
||||
continue;
|
||||
}
|
||||
|
||||
QVariant result = exp->evaluate( &feat, mLayer->pendingFields() );
|
||||
if ( exp->hasEvalError() )
|
||||
{
|
||||
QgsDebugMsg( "Expression parser eval error:" + exp->evalErrorString() );
|
||||
expr_action += action.mid( start, index - start );
|
||||
continue;
|
||||
}
|
||||
|
||||
QgsDebugMsg( "Expression result is: " + result.toString() );
|
||||
expr_action += action.mid( start, pos - start ) + result.toString();
|
||||
}
|
||||
|
||||
expr_action += action.mid( index );
|
||||
return expr_action;
|
||||
}
|
||||
|
||||
|
||||
bool QgsAttributeAction::writeXML( QDomNode& layer_node, QDomDocument& doc ) const
|
||||
{
|
||||
QDomElement aActions = doc.createElement( "attributeactions" );
|
||||
|
@ -107,24 +107,64 @@ class CORE_EXPORT QgsAttributeAction
|
||||
// dialog box.
|
||||
void addAction( QgsAction::ActionType type, QString name, QString action, bool capture = false );
|
||||
|
||||
//! Does the action using the given values. defaultValueIndex is an
|
||||
// index into values which indicates which value in the values vector
|
||||
// is to be used if the action has a default placeholder.
|
||||
// @note parameter executePython deprecated (and missing in python binding)
|
||||
void doAction( int index,
|
||||
/*! Does the action using the given values. defaultValueIndex is an
|
||||
* index into values which indicates which value in the values vector
|
||||
* is to be used if the action has a default placeholder.
|
||||
* @note parameter executePython deprecated (and missing in python binding)
|
||||
* @deprecated
|
||||
*/
|
||||
Q_DECL_DEPRECATED void doAction( int index,
|
||||
const QgsAttributeMap &attributes,
|
||||
int defaultValueIndex = 0,
|
||||
void ( *executePython )( const QString & ) = 0 );
|
||||
|
||||
/*! Does the given values. defaultValueIndex is the index of the
|
||||
* field to be used if the action has a $currfield placeholder.
|
||||
* @note added in 1.9
|
||||
*/
|
||||
void doAction( int index,
|
||||
QgsFeature &feat,
|
||||
int defaultValueIndex = 0 );
|
||||
|
||||
/*! Does the action using the expression builder to expand it
|
||||
* and getting values from the passed feature attribute map.
|
||||
* substitutionMap is used to pass custom substitutions, to replace
|
||||
* each key in the map with the associated value
|
||||
* @note added in 1.9
|
||||
*/
|
||||
void doAction( int index,
|
||||
QgsFeature &feat,
|
||||
const QMap<QString, QVariant> *substitutionMap = 0 );
|
||||
|
||||
//! Removes all actions
|
||||
void clearActions() { mActions.clear(); }
|
||||
|
||||
//! Expands the given action, replacing all %'s with the value as
|
||||
// given.
|
||||
QString expandAction( QString action,
|
||||
//! Return the layer
|
||||
QgsVectorLayer *layer() { return mLayer; }
|
||||
|
||||
/*! Expands the given action, replacing all %'s with the value as
|
||||
* given.
|
||||
* @deprecated
|
||||
*/
|
||||
Q_DECL_DEPRECATED QString expandAction( QString action,
|
||||
const QgsAttributeMap &attributes,
|
||||
uint defaultValueIndex );
|
||||
|
||||
/*! Expands the given action using the expression builder
|
||||
* This function currently replaces each expression between [% and %]
|
||||
* placeholders in the action with the result of its evaluation on
|
||||
* the feature passed as argument.
|
||||
*
|
||||
* Additional substitutions can be passed through the substitutionMap
|
||||
* parameter
|
||||
*
|
||||
* @note added in 1.9
|
||||
*/
|
||||
QString expandAction( QString action,
|
||||
QgsFeature &feat,
|
||||
const QMap<QString, QVariant> *substitutionMap = 0 );
|
||||
|
||||
|
||||
//! Writes the actions out in XML format
|
||||
bool writeXML( QDomNode& layer_node, QDomDocument& doc ) const;
|
||||
|
||||
@ -142,6 +182,9 @@ class CORE_EXPORT QgsAttributeAction
|
||||
QList<QgsAction> mActions;
|
||||
QgsVectorLayer *mLayer;
|
||||
static void ( *smPythonExecute )( const QString & );
|
||||
|
||||
void runAction( const QgsAction &action,
|
||||
void ( *executePython )( const QString & ) = 0 );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -555,21 +555,13 @@ void QgsAttributeTableModel::incomingChangeLayout()
|
||||
|
||||
void QgsAttributeTableModel::executeAction( int action, const QModelIndex &idx ) const
|
||||
{
|
||||
QgsAttributeMap attributes;
|
||||
|
||||
for ( int i = 0; i < mAttributes.size(); i++ )
|
||||
{
|
||||
attributes.insert( mAttributes[i], data( index( idx.row(), i ), Qt::EditRole ) );
|
||||
}
|
||||
|
||||
mLayer->actions()->doAction( action, attributes, fieldIdx( idx.column() ) );
|
||||
QgsFeature f = feature( idx );
|
||||
mLayer->actions()->doAction( action, f, fieldIdx( idx.column() ) );
|
||||
}
|
||||
|
||||
QgsFeature QgsAttributeTableModel::feature( QModelIndex &idx )
|
||||
QgsFeature QgsAttributeTableModel::feature( const QModelIndex &idx ) const
|
||||
{
|
||||
QgsFeature f;
|
||||
QgsAttributeMap attributes;
|
||||
|
||||
f.setFeatureId( rowToId( idx.row() ) );
|
||||
for ( int i = 0; i < mAttributes.size(); i++ )
|
||||
{
|
||||
|
@ -138,7 +138,7 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
|
||||
void executeAction( int action, const QModelIndex &idx ) const;
|
||||
|
||||
/** return feature attributes at given index */
|
||||
QgsFeature feature( QModelIndex &idx );
|
||||
QgsFeature feature( const QModelIndex &idx ) const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
|
@ -253,6 +253,38 @@
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="insertExpressionButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Inserts an expression into the action</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Insert expression...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="fieldComboBox">
|
||||
<property name="toolTip">
|
||||
|
Loading…
x
Reference in New Issue
Block a user