Quantum GIS API Documentation  1.8
src/core/qgsattributeaction.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                                qgsattributeaction.cpp
00003 
00004  A class that stores and controls the managment and execution of actions
00005  associated. Actions are defined to be external programs that are run
00006  with user-specified inputs that can depend on the value of layer
00007  attributes.
00008 
00009                              -------------------
00010     begin                : Oct 24 2004
00011     copyright            : (C) 2004 by Gavin Macaulay
00012     email                : gavin at macaulay dot co dot nz
00013 
00014  ***************************************************************************/
00015 
00016 /***************************************************************************
00017  *                                                                         *
00018  *   This program is free software; you can redistribute it and/or modify  *
00019  *   it under the terms of the GNU General Public License as published by  *
00020  *   the Free Software Foundation; either version 2 of the License, or     *
00021  *   (at your option) any later version.                                   *
00022  *                                                                         *
00023  ***************************************************************************/
00024 
00025 #include "qgsattributeaction.h"
00026 #include "qgspythonrunner.h"
00027 #include "qgsrunprocess.h"
00028 #include "qgsvectorlayer.h"
00029 #include "qgsproject.h"
00030 #include <qgslogger.h>
00031 #include "qgsexpression.h"
00032 
00033 #include <QList>
00034 #include <QStringList>
00035 #include <QDomElement>
00036 #include <QSettings>
00037 #include <QDesktopServices>
00038 #include <QUrl>
00039 #include <QDir>
00040 #include <QFileInfo>
00041 
00042 
00043 void QgsAttributeAction::addAction( QgsAction::ActionType type, QString name, QString action, bool capture )
00044 {
00045   mActions << QgsAction( type, name, action, capture );
00046 }
00047 
00048 void QgsAttributeAction::doAction( int index, const QgsAttributeMap &attributes,
00049                                    int defaultValueIndex, void ( *executePython )( const QString & ) )
00050 {
00051   if ( index < 0 || index >= size() )
00052     return;
00053 
00054   const QgsAction &action = at( index );
00055   if ( !action.runable() )
00056     return;
00057 
00058   // A couple of extra options for running the action may be
00059   // useful. For example,
00060   // - run the action inside a terminal (on unix)
00061   // - capture the stdout from the process and display in a dialog
00062   //   box
00063   //
00064   // The capture stdout one is partially implemented. It just needs
00065   // the UI and the code in this function to select on the
00066   // action.capture() return value.
00067 
00068   QString expandedAction = expandAction( action.action(), attributes, defaultValueIndex );
00069   if ( expandedAction.isEmpty() )
00070     return;
00071 
00072   QgsAction newAction( action.type(), action.name(), expandedAction, action.capture() );
00073   runAction( newAction, executePython );
00074 }
00075 
00076 void QgsAttributeAction::doAction( int index, QgsFeature &feat, int defaultValueIndex )
00077 {
00078   QMap<QString, QVariant> substitutionMap;
00079   if ( defaultValueIndex >= 0 )
00080   {
00081     if ( feat.attributeMap().contains( defaultValueIndex ) )
00082       substitutionMap.insert( "$currfield", feat.attributeMap()[ defaultValueIndex ] );
00083   }
00084 
00085   doAction( index, feat, &substitutionMap );
00086 }
00087 
00088 void QgsAttributeAction::doAction( int index, QgsFeature &feat,
00089                                    const QMap<QString, QVariant> *substitutionMap )
00090 {
00091   if ( index < 0 || index >= size() )
00092     return;
00093 
00094   const QgsAction &action = at( index );
00095   if ( !action.runable() )
00096     return;
00097 
00098   // search for expressions while expanding actions
00099   QString expandedAction = expandAction( action.action(), feat, substitutionMap );
00100   if ( expandedAction.isEmpty() )
00101     return;
00102 
00103   QgsAction newAction( action.type(), action.name(), expandedAction, action.capture() );
00104   runAction( newAction );
00105 }
00106 
00107 void QgsAttributeAction::runAction( const QgsAction &action, void ( *executePython )( const QString & ) )
00108 {
00109   if ( action.type() == QgsAction::OpenUrl )
00110   {
00111     QFileInfo finfo( action.action() );
00112     if ( finfo.exists() && finfo.isFile() )
00113       QDesktopServices::openUrl( QUrl::fromLocalFile( action.action() ) );
00114     else
00115       QDesktopServices::openUrl( QUrl( action.action(), QUrl::TolerantMode ) );
00116   }
00117   else if ( action.type() == QgsAction::GenericPython )
00118   {
00119     if ( executePython )
00120     {
00121       // deprecated
00122       executePython( action.action() );
00123     }
00124     else if ( smPythonExecute )
00125     {
00126       // deprecated
00127       smPythonExecute( action.action() );
00128     }
00129     else
00130     {
00131       // TODO: capture output from QgsPythonRunner (like QgsRunProcess does)
00132       QgsPythonRunner::run( action.action() );
00133     }
00134   }
00135   else
00136   {
00137     // The QgsRunProcess instance created by this static function
00138     // deletes itself when no longer needed.
00139     QgsRunProcess::create( action.action(), action.capture() );
00140   }
00141 }
00142 
00143 QString QgsAttributeAction::expandAction( QString action, const QgsAttributeMap &attributes,
00144     uint clickedOnValue )
00145 {
00146   // This function currently replaces all %% characters in the action
00147   // with the value from values[clickedOnValue].second, and then
00148   // searches for all strings that go %attribute_name, where
00149   // attribute_name is found in values[x].first, and replaces any that
00150   // it finds by values[s].second.
00151 
00152   // Additional substitutions could include symbols for $CWD, $HOME,
00153   // etc (and their OSX and Windows equivalents)
00154 
00155   // This function will potentially fall apart if any of the
00156   // substitutions produce text that could match another
00157   // substitution. May be better to adopt a two pass approach - identify
00158   // all matches and their substitutions and then do a second pass
00159   // for the actual substitutions.
00160 
00161   QString expanded_action;
00162   if ( attributes.contains( clickedOnValue ) )
00163     expanded_action = action.replace( "%%", attributes[clickedOnValue].toString() );
00164   else
00165     expanded_action = action;
00166 
00167   const QgsFieldMap &fields = mLayer->pendingFields();
00168 
00169   for ( int i = 0; i < 4; i++ )
00170   {
00171     for ( QgsAttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++ )
00172     {
00173       QgsFieldMap::const_iterator fit = fields.find( it.key() );
00174       if ( fit == fields.constEnd() )
00175         continue;
00176 
00177       QString to_replace;
00178       switch ( i )
00179       {
00180         case 0: to_replace = "[%" + fit->name() + "]"; break;
00181         case 1: to_replace = "[%" + mLayer->attributeDisplayName( it.key() ) + "]"; break;
00182         case 2: to_replace = "%" + fit->name(); break;
00183         case 3: to_replace = "%" + mLayer->attributeDisplayName( it.key() ); break;
00184       }
00185 
00186       expanded_action = expanded_action.replace( to_replace, it.value().toString() );
00187     }
00188   }
00189 
00190   return expanded_action;
00191 }
00192 
00193 QString QgsAttributeAction::expandAction( QString action, QgsFeature &feat, const QMap<QString, QVariant> *substitutionMap )
00194 {
00195   // This function currently replaces each expression between [% and %]
00196   // in the action with the result of its evaluation on the feature
00197   // passed as argument.
00198 
00199   // Additional substitutions can be passed through the substitutionMap
00200   // parameter
00201 
00202   QString expr_action;
00203 
00204   int index = 0;
00205   while ( index < action.size() )
00206   {
00207     QRegExp rx = QRegExp( "\\[%([^\\]]+)%\\]" );
00208 
00209     int pos = rx.indexIn( action, index );
00210     if ( pos < 0 )
00211       break;
00212 
00213     int start = index;
00214     index = pos + rx.matchedLength();
00215 
00216     QString to_replace = rx.cap( 1 ).trimmed();
00217     QgsDebugMsg( "Found expression: " + to_replace );
00218 
00219     if ( substitutionMap && substitutionMap->contains( to_replace ) )
00220     {
00221       expr_action += action.mid( start, pos - start ) + substitutionMap->value( to_replace ).toString();
00222       continue;
00223     }
00224 
00225     QgsExpression exp( to_replace );
00226     if ( exp.hasParserError() )
00227     {
00228       QgsDebugMsg( "Expression parser error: " + exp.parserErrorString() );
00229       expr_action += action.mid( start, index - start );
00230       continue;
00231     }
00232 
00233     QVariant result = exp.evaluate( &feat, mLayer->pendingFields() );
00234     if ( exp.hasEvalError() )
00235     {
00236       QgsDebugMsg( "Expression parser eval error: " + exp.evalErrorString() );
00237       expr_action += action.mid( start, index - start );
00238       continue;
00239     }
00240 
00241     QgsDebugMsg( "Expression result is: " + result.toString() );
00242     expr_action += action.mid( start, pos - start ) + result.toString();
00243   }
00244 
00245   expr_action += action.mid( index );
00246   return expr_action;
00247 }
00248 
00249 
00250 bool QgsAttributeAction::writeXML( QDomNode& layer_node, QDomDocument& doc ) const
00251 {
00252   QDomElement aActions = doc.createElement( "attributeactions" );
00253 
00254   for ( int i = 0; i < mActions.size(); i++ )
00255   {
00256     QDomElement actionSetting = doc.createElement( "actionsetting" );
00257     actionSetting.setAttribute( "type", mActions[i].type() );
00258     actionSetting.setAttribute( "name", mActions[i].name() );
00259     actionSetting.setAttribute( "action", mActions[i].action() );
00260     actionSetting.setAttribute( "capture", mActions[i].capture() );
00261     aActions.appendChild( actionSetting );
00262   }
00263   layer_node.appendChild( aActions );
00264 
00265   return true;
00266 }
00267 
00268 bool QgsAttributeAction::readXML( const QDomNode& layer_node )
00269 {
00270   mActions.clear();
00271 
00272   QDomNode aaNode = layer_node.namedItem( "attributeactions" );
00273 
00274   if ( !aaNode.isNull() )
00275   {
00276     QDomNodeList actionsettings = aaNode.childNodes();
00277     for ( unsigned int i = 0; i < actionsettings.length(); ++i )
00278     {
00279       QDomElement setting = actionsettings.item( i ).toElement();
00280       addAction(( QgsAction::ActionType ) setting.attributeNode( "type" ).value().toInt(),
00281                 setting.attributeNode( "name" ).value(),
00282                 setting.attributeNode( "action" ).value(),
00283                 setting.attributeNode( "capture" ).value().toInt() != 0 );
00284     }
00285   }
00286   return true;
00287 }
00288 
00289 void ( *QgsAttributeAction::smPythonExecute )( const QString & ) = 0;
00290 
00291 void QgsAttributeAction::setPythonExecute( void ( *runPython )( const QString & ) )
00292 {
00293   smPythonExecute = runPython;
00294 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines