Navigation Menu

Skip to content

Commit

Permalink
[FEATURE] use expressions engine to evaluate feature actions
Browse files Browse the repository at this point in the history
work done for Regione Toscana-SIGTA
  • Loading branch information
brushtyler committed Jan 11, 2012
1 parent 35f5da7 commit 1fe82b3
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 50 deletions.
37 changes: 30 additions & 7 deletions src/app/qgsattributeactiondialog.cpp
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -143,16 +145,36 @@ 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" ) );

if ( !action.isNull() )
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();
Expand Down Expand Up @@ -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 );
}
}
Expand Down
1 change: 1 addition & 0 deletions src/app/qgsattributeactiondialog.h
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgsfeatureaction.cpp
Expand Up @@ -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 )
Expand Down
11 changes: 5 additions & 6 deletions src/app/qgsidentifyresults.cpp
Expand Up @@ -429,7 +429,7 @@ void QgsIdentifyResults::contextMenuEvent( QContextMenuEvent* event )

mActionPopup = new QMenu();

int idx = 0;
int idx = -1;
QTreeWidgetItem *featItem = featureItem( item );
if ( featItem )
{
Expand Down Expand Up @@ -549,17 +549,15 @@ 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;

QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( featItem->parent()->data( 0, Qt::UserRole ).value<QObject *>() );
if ( !layer )
return;

idx = -1;
int idx = -1;
if ( item->parent() == featItem )
{
QString fieldName = item->data( 0, Qt::DisplayRole ).toString();
Expand All @@ -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 )
Expand Down
128 changes: 115 additions & 13 deletions src/core/qgsattributeaction.cpp
Expand Up @@ -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 )
{
Expand All @@ -44,7 +52,6 @@ void QgsAttributeAction::doAction( int index, const QgsAttributeMap &attributes,
return;

const QgsAction &action = at( index );

if ( !action.runable() )
return;

Expand All @@ -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() );
}
}

Expand All @@ -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.

Expand Down Expand Up @@ -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" );
Expand Down

0 comments on commit 1fe82b3

Please sign in to comment.