Quantum GIS API Documentation
1.8
|
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 }