|
QGIS API Documentation
master-59fd5e0
|
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::removeAction( int index ) 00049 { 00050 if ( index >= 0 && index < mActions.size() ) 00051 { 00052 mActions.removeAt( index ); 00053 } 00054 } 00055 00056 void QgsAttributeAction::doAction( int index, QgsFeature &feat, int defaultValueIndex ) 00057 { 00058 QMap<QString, QVariant> substitutionMap; 00059 if ( defaultValueIndex >= 0 ) 00060 { 00061 QVariant defaultValue = feat.attribute( defaultValueIndex ); 00062 if ( defaultValue.isValid() ) 00063 substitutionMap.insert( "$currfield", defaultValue ); 00064 } 00065 00066 doAction( index, feat, &substitutionMap ); 00067 } 00068 00069 void QgsAttributeAction::doAction( int index, QgsFeature &feat, 00070 const QMap<QString, QVariant> *substitutionMap ) 00071 { 00072 if ( index < 0 || index >= size() ) 00073 return; 00074 00075 const QgsAction &action = at( index ); 00076 if ( !action.runable() ) 00077 return; 00078 00079 // search for expressions while expanding actions 00080 QString expandedAction = QgsExpression::replaceExpressionText( action.action(), feat, mLayer , substitutionMap ); 00081 if ( expandedAction.isEmpty() ) 00082 return; 00083 00084 QgsAction newAction( action.type(), action.name(), expandedAction, action.capture() ); 00085 runAction( newAction ); 00086 } 00087 00088 void QgsAttributeAction::runAction( const QgsAction &action, void ( *executePython )( const QString & ) ) 00089 { 00090 if ( action.type() == QgsAction::OpenUrl ) 00091 { 00092 QFileInfo finfo( action.action() ); 00093 if ( finfo.exists() && finfo.isFile() ) 00094 QDesktopServices::openUrl( QUrl::fromLocalFile( action.action() ) ); 00095 else 00096 QDesktopServices::openUrl( QUrl( action.action(), QUrl::TolerantMode ) ); 00097 } 00098 else if ( action.type() == QgsAction::GenericPython ) 00099 { 00100 if ( executePython ) 00101 { 00102 // deprecated 00103 executePython( action.action() ); 00104 } 00105 else if ( smPythonExecute ) 00106 { 00107 // deprecated 00108 smPythonExecute( action.action() ); 00109 } 00110 else 00111 { 00112 // TODO: capture output from QgsPythonRunner (like QgsRunProcess does) 00113 QgsPythonRunner::run( action.action() ); 00114 } 00115 } 00116 else 00117 { 00118 // The QgsRunProcess instance created by this static function 00119 // deletes itself when no longer needed. 00120 QgsRunProcess::create( action.action(), action.capture() ); 00121 } 00122 } 00123 00124 QString QgsAttributeAction::expandAction( QString action, const QgsAttributeMap &attributes, 00125 uint clickedOnValue ) 00126 { 00127 // This function currently replaces all %% characters in the action 00128 // with the value from values[clickedOnValue].second, and then 00129 // searches for all strings that go %attribute_name, where 00130 // attribute_name is found in values[x].first, and replaces any that 00131 // it finds by values[s].second. 00132 00133 // Additional substitutions could include symbols for $CWD, $HOME, 00134 // etc (and their OSX and Windows equivalents) 00135 00136 // This function will potentially fall apart if any of the 00137 // substitutions produce text that could match another 00138 // substitution. May be better to adopt a two pass approach - identify 00139 // all matches and their substitutions and then do a second pass 00140 // for the actual substitutions. 00141 00142 QString expanded_action; 00143 if ( attributes.contains( clickedOnValue ) ) 00144 expanded_action = action.replace( "%%", attributes[clickedOnValue].toString() ); 00145 else 00146 expanded_action = action; 00147 00148 const QgsFields &fields = mLayer->pendingFields(); 00149 00150 for ( int i = 0; i < 4; i++ ) 00151 { 00152 for ( QgsAttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++ ) 00153 { 00154 int attrIdx = it.key(); 00155 if ( attrIdx < 0 || attrIdx >= fields.count() ) 00156 continue; 00157 00158 QString to_replace; 00159 switch ( i ) 00160 { 00161 case 0: to_replace = "[%" + fields[attrIdx].name() + "]"; break; 00162 case 1: to_replace = "[%" + mLayer->attributeDisplayName( attrIdx ) + "]"; break; 00163 case 2: to_replace = "%" + fields[attrIdx].name(); break; 00164 case 3: to_replace = "%" + mLayer->attributeDisplayName( attrIdx ); break; 00165 } 00166 00167 expanded_action = expanded_action.replace( to_replace, it.value().toString() ); 00168 } 00169 } 00170 00171 return expanded_action; 00172 } 00173 00174 QString QgsAttributeAction::expandAction( QString action, QgsFeature &feat, const QMap<QString, QVariant> *substitutionMap ) 00175 { 00176 // This function currently replaces each expression between [% and %] 00177 // in the action with the result of its evaluation on the feature 00178 // passed as argument. 00179 00180 // Additional substitutions can be passed through the substitutionMap 00181 // parameter 00182 00183 QString expr_action; 00184 00185 int index = 0; 00186 while ( index < action.size() ) 00187 { 00188 QRegExp rx = QRegExp( "\\[%([^\\]]+)%\\]" ); 00189 00190 int pos = rx.indexIn( action, index ); 00191 if ( pos < 0 ) 00192 break; 00193 00194 int start = index; 00195 index = pos + rx.matchedLength(); 00196 00197 QString to_replace = rx.cap( 1 ).trimmed(); 00198 QgsDebugMsg( "Found expression: " + to_replace ); 00199 00200 if ( substitutionMap && substitutionMap->contains( to_replace ) ) 00201 { 00202 expr_action += action.mid( start, pos - start ) + substitutionMap->value( to_replace ).toString(); 00203 continue; 00204 } 00205 00206 QgsExpression exp( to_replace ); 00207 if ( exp.hasParserError() ) 00208 { 00209 QgsDebugMsg( "Expression parser error: " + exp.parserErrorString() ); 00210 expr_action += action.mid( start, index - start ); 00211 continue; 00212 } 00213 00214 QVariant result = exp.evaluate( &feat, mLayer->pendingFields() ); 00215 if ( exp.hasEvalError() ) 00216 { 00217 QgsDebugMsg( "Expression parser eval error: " + exp.evalErrorString() ); 00218 expr_action += action.mid( start, index - start ); 00219 continue; 00220 } 00221 00222 QgsDebugMsg( "Expression result is: " + result.toString() ); 00223 expr_action += action.mid( start, pos - start ) + result.toString(); 00224 } 00225 00226 expr_action += action.mid( index ); 00227 return expr_action; 00228 } 00229 00230 00231 bool QgsAttributeAction::writeXML( QDomNode& layer_node, QDomDocument& doc ) const 00232 { 00233 QDomElement aActions = doc.createElement( "attributeactions" ); 00234 00235 for ( int i = 0; i < mActions.size(); i++ ) 00236 { 00237 QDomElement actionSetting = doc.createElement( "actionsetting" ); 00238 actionSetting.setAttribute( "type", mActions[i].type() ); 00239 actionSetting.setAttribute( "name", mActions[i].name() ); 00240 actionSetting.setAttribute( "action", mActions[i].action() ); 00241 actionSetting.setAttribute( "capture", mActions[i].capture() ); 00242 aActions.appendChild( actionSetting ); 00243 } 00244 layer_node.appendChild( aActions ); 00245 00246 return true; 00247 } 00248 00249 bool QgsAttributeAction::readXML( const QDomNode& layer_node ) 00250 { 00251 mActions.clear(); 00252 00253 QDomNode aaNode = layer_node.namedItem( "attributeactions" ); 00254 00255 if ( !aaNode.isNull() ) 00256 { 00257 QDomNodeList actionsettings = aaNode.childNodes(); 00258 for ( unsigned int i = 0; i < actionsettings.length(); ++i ) 00259 { 00260 QDomElement setting = actionsettings.item( i ).toElement(); 00261 addAction(( QgsAction::ActionType ) setting.attributeNode( "type" ).value().toInt(), 00262 setting.attributeNode( "name" ).value(), 00263 setting.attributeNode( "action" ).value(), 00264 setting.attributeNode( "capture" ).value().toInt() != 0 ); 00265 } 00266 } 00267 return true; 00268 } 00269 00270 void ( *QgsAttributeAction::smPythonExecute )( const QString & ) = 0; 00271 00272 void QgsAttributeAction::setPythonExecute( void ( *runPython )( const QString & ) ) 00273 { 00274 smPythonExecute = runPython; 00275 }