QGIS API Documentation 3.99.0-Master (8e76e220402)
Loading...
Searching...
No Matches
qgsactionmanager.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsactionmanager.cpp
3
4 A class that stores and controls the management and execution of actions
5 associated. Actions are defined to be external programs that are run
6 with user-specified inputs that can depend on the value of layer
7 attributes.
8
9 -------------------
10 begin : Oct 24 2004
11 copyright : (C) 2004 by Gavin Macaulay
12 email : gavin at macaulay dot co dot nz
13
14 ***************************************************************************/
15
16/***************************************************************************
17 * *
18 * This program is free software; you can redistribute it and/or modify *
19 * it under the terms of the GNU General Public License as published by *
20 * the Free Software Foundation; either version 2 of the License, or *
21 * (at your option) any later version. *
22 * *
23 ***************************************************************************/
24
25#include "qgsactionmanager.h"
26
27#include "qgsaction.h"
28#include "qgsdataprovider.h"
29#include "qgsexpression.h"
31#include "qgslogger.h"
32#include "qgsproject.h"
33#include "qgsrunprocess.h"
34#include "qgsvectorlayer.h"
35
36#include <QDesktopServices>
37#include <QDomElement>
38#include <QFileInfo>
39#include <QList>
40#include <QRegularExpression>
41#include <QSettings>
42#include <QString>
43#include <QStringList>
44#include <QUrl>
45
46#include "moc_qgsactionmanager.cpp"
47
48using namespace Qt::StringLiterals;
49
53
54QUuid QgsActionManager::addAction( Qgis::AttributeActionType type, const QString &name, const QString &command, bool capture )
55{
56 QgsAction action( type, name, command, capture );
58 return action.id();
59}
60
61QUuid QgsActionManager::addAction( Qgis::AttributeActionType type, const QString &name, const QString &command, const QString &icon, bool capture )
62{
63 QgsAction action( type, name, command, icon, capture );
65 return action.id();
66}
67
69{
70 QgsDebugMsgLevel( "add action " + action.name(), 3 );
71 mActions.append( action );
72 if ( mLayer && mLayer->dataProvider() && !action.notificationMessage().isEmpty() )
73 {
74 mLayer->dataProvider()->setListening( true );
75 if ( !mOnNotifyConnected )
76 {
77 QgsDebugMsgLevel( u"connecting to notify"_s, 3 );
78 connect( mLayer->dataProvider(), &QgsDataProvider::notify, this, &QgsActionManager::onNotifyRunActions );
79 mOnNotifyConnected = true;
80 }
81 }
82}
83
84void QgsActionManager::onNotifyRunActions( const QString &message )
85{
86 for ( const QgsAction &act : std::as_const( mActions ) )
87 {
88 if ( !act.notificationMessage().isEmpty() && QRegularExpression( act.notificationMessage() ).match( message ).hasMatch() )
89 {
90 if ( !act.isValid() || !act.runable() )
91 continue;
92
93 QgsExpressionContext context = createExpressionContext();
94
95 Q_ASSERT( mLayer ); // if there is no layer, then where is the notification coming from ?
96 context << QgsExpressionContextUtils::layerScope( mLayer );
98
99 QString expandedAction = QgsExpression::replaceExpressionText( act.command(), &context );
100 if ( expandedAction.isEmpty() )
101 continue;
102 runAction( QgsAction( act.type(), act.name(), expandedAction, act.capture() ) );
103 }
104 }
105}
106
107void QgsActionManager::removeAction( QUuid actionId )
108{
109 int i = 0;
110 for ( const QgsAction &action : std::as_const( mActions ) )
111 {
112 if ( action.id() == actionId )
113 {
114 mActions.removeAt( i );
115 break;
116 }
117 ++i;
118 }
119
120 if ( mOnNotifyConnected )
121 {
122 bool hasActionOnNotify = false;
123 for ( const QgsAction &action : std::as_const( mActions ) )
124 hasActionOnNotify |= !action.notificationMessage().isEmpty();
125 if ( !hasActionOnNotify && mLayer && mLayer->dataProvider() )
126 {
127 // note that there is no way of knowing if the provider is listening only because
128 // this class has hasked it to, so we do not reset the provider listening state here
129 disconnect( mLayer->dataProvider(), &QgsDataProvider::notify, this, &QgsActionManager::onNotifyRunActions );
130 mOnNotifyConnected = false;
131 }
132 }
133}
134
135void QgsActionManager::doAction( QUuid actionId, const QgsFeature &feature, int defaultValueIndex, const QgsExpressionContextScope &scope )
136{
137 QgsExpressionContext context = createExpressionContext();
138 QgsExpressionContextScope *actionScope = new QgsExpressionContextScope( scope );
139 actionScope->addVariable( QgsExpressionContextScope::StaticVariable( u"field_index"_s, defaultValueIndex, true ) );
140 if ( defaultValueIndex >= 0 && defaultValueIndex < feature.fields().size() )
141 actionScope->addVariable( QgsExpressionContextScope::StaticVariable( u"field_name"_s, feature.fields().at( defaultValueIndex ).name(), true ) );
142 actionScope->addVariable( QgsExpressionContextScope::StaticVariable( u"field_value"_s, feature.attribute( defaultValueIndex ), true ) );
143 context << actionScope;
144 doAction( actionId, feature, context );
145}
146
147void QgsActionManager::doAction( QUuid actionId, const QgsFeature &feat, const QgsExpressionContext &context )
148{
149 QgsAction act = action( actionId );
150
151 if ( !act.isValid() || !act.runable() )
152 return;
153
154 QgsExpressionContext actionContext( context );
155
156 if ( mLayer )
157 actionContext << QgsExpressionContextUtils::layerScope( mLayer );
158 actionContext.setFeature( feat );
159
160 QString expandedAction = QgsExpression::replaceExpressionText( act.command(), &actionContext );
161 if ( expandedAction.isEmpty() )
162 return;
163
164 QgsAction newAction( act.type(), act.name(), expandedAction, act.capture() );
165 runAction( newAction );
166}
167
169{
170 mActions.clear();
171 if ( mOnNotifyConnected && mLayer && mLayer->dataProvider() )
172 {
173 // note that there is no way of knowing if the provider is listening only because
174 // this class has hasked it to, so we do not reset the provider listening state here
175 disconnect( mLayer->dataProvider(), &QgsDataProvider::notify, this, &QgsActionManager::onNotifyRunActions );
176 mOnNotifyConnected = false;
177 }
178}
179
180QList<QgsAction> QgsActionManager::actions( const QString &actionScope ) const
181{
182 if ( actionScope.isNull() )
183 return mActions;
184 else
185 {
186 QList<QgsAction> actions;
187
188 for ( const QgsAction &action : std::as_const( mActions ) )
189 {
190 if ( action.actionScopes().contains( actionScope ) )
191 actions.append( action );
192 }
193
194 return actions;
195 }
196}
197
198void QgsActionManager::runAction( const QgsAction &action )
199{
200 switch ( action.type() )
201 {
203 {
204 QFileInfo finfo( action.command() );
205 if ( finfo.exists() && finfo.isFile() )
206 QDesktopServices::openUrl( QUrl::fromLocalFile( action.command() ) );
207 else
208 QDesktopServices::openUrl( QUrl( action.command(), QUrl::TolerantMode ) );
209 break;
210 }
214 {
215 action.run( QgsExpressionContext() );
216 break;
217 }
222 {
223 // The QgsRunProcess instance created by this static function
224 // deletes itself when no longer needed.
225 QgsRunProcess::create( action.command(), action.capture() );
226 break;
227 }
228 }
229}
230
231QgsExpressionContext QgsActionManager::createExpressionContext() const
232{
233 QgsExpressionContext context;
236 if ( mLayer )
237 context << QgsExpressionContextUtils::layerScope( mLayer );
238
239 return context;
240}
241
242bool QgsActionManager::writeXml( QDomNode &layer_node ) const
243{
244 QDomElement aActions = layer_node.ownerDocument().createElement( u"attributeactions"_s );
245 for ( QMap<QString, QUuid>::const_iterator defaultAction = mDefaultActions.constBegin(); defaultAction != mDefaultActions.constEnd(); ++ defaultAction )
246 {
247 QDomElement defaultActionElement = layer_node.ownerDocument().createElement( u"defaultAction"_s );
248 defaultActionElement.setAttribute( u"key"_s, defaultAction.key() );
249 defaultActionElement.setAttribute( u"value"_s, defaultAction.value().toString() );
250 aActions.appendChild( defaultActionElement );
251 }
252
253 for ( const QgsAction &action : std::as_const( mActions ) )
254 {
255 action.writeXml( aActions );
256 }
257 layer_node.appendChild( aActions );
258
259 return true;
260}
261
262bool QgsActionManager::readXml( const QDomNode &layer_node, const QgsReadWriteContext &context )
263{
264 clearActions();
265
266 QDomNode aaNode = layer_node.namedItem( u"attributeactions"_s );
267
268 if ( !aaNode.isNull() )
269 {
270 QDomNodeList actionsettings = aaNode.toElement().elementsByTagName( u"actionsetting"_s );
271 for ( int i = 0; i < actionsettings.size(); ++i )
272 {
274 action.readXml( actionsettings.item( i ), context );
275 addAction( action );
276 }
277
278 QDomNodeList defaultActionNodes = aaNode.toElement().elementsByTagName( u"defaultAction"_s );
279
280 for ( int i = 0; i < defaultActionNodes.size(); ++i )
281 {
282 QDomElement defaultValueElem = defaultActionNodes.at( i ).toElement();
283 mDefaultActions.insert( defaultValueElem.attribute( u"key"_s ), QUuid( defaultValueElem.attribute( u"value"_s ) ) );
284 }
285 }
286 return true;
287}
288
290{
291 for ( const QgsAction &action : std::as_const( mActions ) )
292 {
293 if ( action.id() == id )
294 return action;
295 }
296
297 return QgsAction();
298}
299
300QgsAction QgsActionManager::action( const QString &id ) const
301{
302 for ( const QgsAction &action : std::as_const( mActions ) )
303 {
304 if ( action.id().toString() == id )
305 return action;
306 }
307
308 return QgsAction();
309}
310
311void QgsActionManager::setDefaultAction( const QString &actionScope, QUuid actionId )
312{
313 mDefaultActions[ actionScope ] = actionId;
314}
315
316QgsAction QgsActionManager::defaultAction( const QString &actionScope )
317{
318 return action( mDefaultActions.value( actionScope ) );
319}
AttributeActionType
Attribute action types.
Definition qgis.h:4782
@ Mac
MacOS specific.
Definition qgis.h:4785
@ OpenUrl
Open URL action.
Definition qgis.h:4788
@ Unix
Unix specific.
Definition qgis.h:4787
@ SubmitUrlMultipart
POST data to an URL using "multipart/form-data".
Definition qgis.h:4790
@ Windows
Windows specific.
Definition qgis.h:4786
@ SubmitUrlEncoded
POST data to an URL, using "application/x-www-form-urlencoded" or "application/json" if the body is v...
Definition qgis.h:4789
void removeAction(QUuid actionId)
Remove an action by its id.
bool writeXml(QDomNode &layer_node) const
Writes the actions out in XML format.
QList< QgsAction > actions(const QString &actionScope=QString()) const
Returns a list of actions that are available in the given action scope.
QgsVectorLayer * layer() const
Returns the layer.
void doAction(QUuid actionId, const QgsFeature &feature, int defaultValueIndex=0, const QgsExpressionContextScope &scope=QgsExpressionContextScope())
Does the given action.
void clearActions()
Removes all actions.
QUuid addAction(Qgis::AttributeActionType type, const QString &name, const QString &command, bool capture=false)
Add an action with the given name and action details.
void setDefaultAction(const QString &actionScope, QUuid actionId)
Each scope can have a default action.
QgsActionManager(QgsVectorLayer *layer)
Constructor.
QgsAction defaultAction(const QString &actionScope)
Each scope can have a default action.
QgsAction action(QUuid id) const
Gets an action by its id.
bool readXml(const QDomNode &layer_node, const QgsReadWriteContext &context=QgsReadWriteContext())
Reads the actions in in XML format.
Utility class that encapsulates an action based on vector attributes.
Definition qgsaction.h:38
QString name() const
The name of the action. This may be a longer description.
Definition qgsaction.h:116
Qgis::AttributeActionType type() const
The action type.
Definition qgsaction.h:154
bool runable() const
Checks if the action is runable on the current platform.
Definition qgsaction.cpp:44
bool isValid() const
Returns true if this action was a default constructed one.
Definition qgsaction.h:131
QString command() const
Returns the command that is executed by this action.
Definition qgsaction.h:145
bool capture() const
Whether to capture output for display when this action is run.
Definition qgsaction.h:157
void notify(const QString &msg)
Emitted when the datasource issues a notification.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
static QgsExpressionContextScope * notificationScope(const QString &message=QString())
Creates a new scope which contains variables and functions relating to provider notifications.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFields fields
Definition qgsfeature.h:70
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
QString name
Definition qgsfield.h:65
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
static QgsProject * instance()
Returns the QgsProject singleton instance.
A container for the context for various read/write operations on objects.
static QgsRunProcess * create(const QString &action, bool capture)
Represents a vector layer which manages a vector based dataset.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
Single variable definition for use within a QgsExpressionContextScope.