Skip to content

Commit 85a17c2

Browse files
committed
Add QgsMapLayerAction and QgsMapLayerActionRegistry, which can be
used as a generic api for registering actions which can apply to a specific map layer or layer type. Create a QgsMapLayerAction for setting features as the current atlas feature for compositions. This work was kindly sponsored by SIGE (www.sige.ch).
1 parent fc3094f commit 85a17c2

22 files changed

+723
-17
lines changed

python/core/composer/qgsatlascomposition.sip

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ public:
8282
/** Prepare the atlas map for the given feature. Sets the extent and context variables */
8383
void prepareForFeature( int i );
8484

85+
/** Prepare the atlas map for the given feature. Sets the extent and context variables */
86+
void prepareForFeature( QgsFeature * feat );
87+
8588
/** Returns the current filename. Must be called after prepareForFeature( i ) */
8689
const QString& currentFilename() const;
8790

@@ -108,4 +111,11 @@ public:
108111
signals:
109112
/** emitted when one of the parameters changes */
110113
void parameterChanged();
114+
115+
/** emitted when atlas is enabled or disabled */
116+
void toggled( bool );
117+
118+
/**Is emitted when the coverage layer for an atlas changes*/
119+
void coverageLayerChanged( QgsVectorLayer* layer );
120+
111121
};

python/gui/attributetable/qgsattributetablemodel.sip

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ class QgsAttributeTableModel : QAbstractTableModel
22
{
33
%TypeHeaderCode
44
#include <qgsattributetablemodel.h>
5+
#include <qgsmaplayeractionregistry.h>
56
%End
67
public:
78
enum Role
@@ -133,6 +134,11 @@ class QgsAttributeTableModel : QAbstractTableModel
133134
*/
134135
void executeAction( int action, const QModelIndex &idx ) const;
135136

137+
/**
138+
* Execute a QgsMapLayerAction
139+
*/
140+
void executeMapLayerAction( QgsMapLayerAction* action, const QModelIndex &idx ) const;
141+
136142
/**
137143
* Return the feature attributes at given model index
138144
* @return feature attributes at given model index

python/gui/attributetable/qgsdualview.sip

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,17 @@ class QgsAttributeTableAction : QAction
132132
void execute();
133133
void featureForm();
134134
};
135+
136+
class QgsAttributeTableMapLayerAction : QAction
137+
{
138+
%TypeHeaderCode
139+
#include <qgsdualview.h>
140+
%End
141+
142+
public:
143+
QgsAttributeTableMapLayerAction( const QString &name, QgsDualView *dualView, QgsMapLayerAction* action, const QModelIndex &fieldIdx );
144+
145+
public slots:
146+
void execute();
147+
148+
};

python/gui/gui.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
%Include qgsmapcanvasitem.sip
4343
%Include qgsmapcanvasmap.sip
4444
%Include qgsmapcanvassnapper.sip
45+
%Include qgsmaplayeractionregistry.sip
4546
%Include qgsmapoverviewcanvas.sip
4647
%Include qgsmaptip.sip
4748
%Include qgsmaptool.sip
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
class QgsMapLayerAction : QAction
2+
{
3+
%TypeHeaderCode
4+
#include <qgsmaplayeractionregistry.h>
5+
%End
6+
7+
public:
8+
9+
/**Creates a map layer action which can run on any layer*/
10+
QgsMapLayerAction( QString name, QObject *parent );
11+
/**Creates a map layer action which can run only on a specific layer*/
12+
QgsMapLayerAction( QString name, QObject *parent, QgsMapLayer* layer );
13+
/**Creates a map layer action which can run on a specific type of layer*/
14+
QgsMapLayerAction( QString name, QObject *parent, QgsMapLayer::LayerType layerType );
15+
16+
~QgsMapLayerAction();
17+
18+
/** True if action can run using the specified layer */
19+
bool canRunUsingLayer( QgsMapLayer* layer ) const;
20+
21+
/** Triggers the action with the specified layer and feature. This also emits the triggeredForLayer( QgsMapLayer *)
22+
* and triggered() slots */
23+
void triggerForFeature( QgsMapLayer* layer, QgsFeature* feature );
24+
25+
/** Triggers the action with the specified layer. This also emits the triggered() slot. */
26+
void triggerForLayer( QgsMapLayer* layer );
27+
28+
signals:
29+
/** Triggered when action has been run for a specific feature */
30+
void triggeredForFeature( QgsMapLayer* layer, QgsFeature* feature );
31+
32+
/** Triggered when action has been run for a specific layer */
33+
void triggeredForLayer( QgsMapLayer* layer );
34+
35+
};
36+
37+
class QgsMapLayerActionRegistry : QObject
38+
{
39+
%TypeHeaderCode
40+
#include <qgsmaplayeractionregistry.h>
41+
%End
42+
43+
public:
44+
45+
//! Returns the instance pointer, creating the object on the first call
46+
static QgsMapLayerActionRegistry * instance();
47+
48+
~QgsMapLayerActionRegistry();
49+
50+
/**Adds a map layer action to the registry*/
51+
void addMapLayerAction( QgsMapLayerAction * action );
52+
53+
/**Returns the map layer actions which can run on the specified layer*/
54+
QList<QgsMapLayerAction *> mapLayerActions( QgsMapLayer* layer );
55+
56+
/**Removes a map layer action from the registry*/
57+
bool removeMapLayerAction( QgsMapLayerAction *action );
58+
59+
/**Sets the default action for a layer*/
60+
void setDefaultActionForLayer( QgsMapLayer* layer, QgsMapLayerAction* action );
61+
/**Returns the default action for a layer*/
62+
QgsMapLayerAction * defaultActionForLayer( QgsMapLayer* layer );
63+
64+
protected:
65+
//! protected constructor
66+
QgsMapLayerActionRegistry( QObject * parent = 0 );
67+
68+
signals:
69+
/** Triggered when an action is added or removed from the registry */
70+
void changed();
71+
72+
};

src/app/composer/qgscomposer.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
#include "qgsmessageviewer.h"
5555
#include "qgscontexthelp.h"
5656
#include "qgscursors.h"
57+
#include "qgsmaplayeractionregistry.h"
58+
#include "qgsgeometry.h"
5759

5860
#include <QCloseEvent>
5961
#include <QCheckBox>
@@ -94,6 +96,7 @@ QgsComposer::QgsComposer( QgisApp *qgis, const QString& title )
9496
, mTitle( title )
9597
, mQgis( qgis )
9698
, mUndoView( 0 )
99+
, mAtlasFeatureAction( 0 )
97100
{
98101
setupUi( this );
99102
setWindowTitle( mTitle );
@@ -509,6 +512,7 @@ QgsComposer::QgsComposer( QgisApp *qgis, const QString& title )
509512
mActionExportAtlasAsPDF->setEnabled( false );
510513
QgsAtlasComposition* atlasMap = &mComposition->atlasComposition();
511514
connect( atlasMap, SIGNAL( toggled( bool ) ), this, SLOT( toggleAtlasControls( bool ) ) );
515+
connect( atlasMap, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( updateAtlasMapLayerAction( QgsVectorLayer * ) ) );
512516

513517
// Create size grip (needed by Mac OS X for QMainWindow if QStatusBar is not visible)
514518
//should not be needed now that composer has a status bar?
@@ -704,6 +708,12 @@ void QgsComposer::setTitle( const QString& title )
704708
{
705709
mWindowAction->setText( title );
706710
}
711+
712+
//update atlas map layer action name if required
713+
if ( mAtlasFeatureAction )
714+
{
715+
mAtlasFeatureAction->setText( QString( tr( "Set as atlas feature for %1" ) ).arg( mTitle ) );
716+
}
707717
}
708718

709719
void QgsComposer::updateStatusCursorPos( QPointF cursorPosition )
@@ -820,6 +830,8 @@ void QgsComposer::toggleAtlasControls( bool atlasEnabled )
820830
mActionExportAtlasAsImage->setEnabled( atlasEnabled );
821831
mActionExportAtlasAsSVG->setEnabled( atlasEnabled );
822832
mActionExportAtlasAsPDF->setEnabled( atlasEnabled );
833+
834+
updateAtlasMapLayerAction( atlasEnabled );
823835
}
824836

825837
void QgsComposer::on_mActionAtlasPreview_triggered( bool checked )
@@ -2655,6 +2667,8 @@ void QgsComposer::readXML( const QDomElement& composerElem, const QDomDocument&
26552667
mActionExportAtlasAsSVG->setEnabled( atlasMap->enabled() );
26562668
mActionExportAtlasAsPDF->setEnabled( atlasMap->enabled() );
26572669
connect( atlasMap, SIGNAL( toggled( bool ) ), this, SLOT( toggleAtlasControls( bool ) ) );
2670+
connect( atlasMap, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( updateAtlasMapLayerAction( QgsVectorLayer * ) ) );
2671+
updateAtlasMapLayerAction( atlasMap->enabled() );
26582672

26592673
setSelectionTool();
26602674
}
@@ -3083,3 +3097,57 @@ void QgsComposer::writeWorldFile( QString worldFileName, double a, double b, dou
30833097
fout << QString::number( c, 'f' ) << "\r\n";
30843098
fout << QString::number( f, 'f' ) << "\r\n";
30853099
}
3100+
3101+
3102+
void QgsComposer::setAtlasFeature( QgsMapLayer* layer, QgsFeature * feat )
3103+
{
3104+
//update expression variables
3105+
QgsExpression::setSpecialColumn( "$atlasfeatureid", feat->id() );
3106+
QgsExpression::setSpecialColumn( "$atlasgeometry", QVariant::fromValue( *( feat->geometry() ) ) );
3107+
3108+
emit atlasPreviewFeatureChanged();
3109+
3110+
//check if composition has atlas preview
3111+
QgsAtlasComposition& atlas = mComposition->atlasComposition();
3112+
if ( ! atlas.enabled() || ! mComposition->atlasMode() == QgsComposition::PreviewAtlas || atlas.coverageLayer() != layer )
3113+
{
3114+
//either atlas preview isn't enabled, or layer doesn't match
3115+
return;
3116+
}
3117+
3118+
//set current preview feature id
3119+
atlas.prepareForFeature( feat );
3120+
}
3121+
3122+
void QgsComposer::updateAtlasMapLayerAction( QgsVectorLayer *coverageLayer )
3123+
{
3124+
if ( mAtlasFeatureAction )
3125+
{
3126+
delete mAtlasFeatureAction;
3127+
mAtlasFeatureAction = 0;
3128+
}
3129+
3130+
if ( coverageLayer )
3131+
{
3132+
mAtlasFeatureAction = new QgsMapLayerAction( QString( tr( "Set as atlas feature for %1" ) ).arg( mTitle ), this, coverageLayer );
3133+
QgsMapLayerActionRegistry::instance()->addMapLayerAction( mAtlasFeatureAction );
3134+
connect( mAtlasFeatureAction, SIGNAL( triggeredForFeature( QgsMapLayer*, QgsFeature* ) ), this, SLOT( setAtlasFeature( QgsMapLayer*, QgsFeature* ) ) );
3135+
}
3136+
}
3137+
3138+
void QgsComposer::updateAtlasMapLayerAction( bool atlasEnabled )
3139+
{
3140+
if ( mAtlasFeatureAction )
3141+
{
3142+
delete mAtlasFeatureAction;
3143+
mAtlasFeatureAction = 0;
3144+
}
3145+
3146+
if ( atlasEnabled )
3147+
{
3148+
QgsAtlasComposition& atlas = mComposition->atlasComposition();
3149+
mAtlasFeatureAction = new QgsMapLayerAction( QString( tr( "Set as atlas feature for %1" ) ).arg( mTitle ), this, atlas.coverageLayer() );
3150+
QgsMapLayerActionRegistry::instance()->addMapLayerAction( mAtlasFeatureAction );
3151+
connect( mAtlasFeatureAction, SIGNAL( triggeredForFeature( QgsMapLayer*, QgsFeature* ) ), this, SLOT( setAtlasFeature( QgsMapLayer*, QgsFeature* ) ) );
3152+
}
3153+
}

src/app/composer/qgscomposer.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class QgsComposerView;
3838
class QgsComposition;
3939
class QgsMapCanvas;
4040
class QgsAtlasComposition;
41+
class QgsMapLayerAction;
4142

4243
class QGridLayout;
4344
class QDomNode;
@@ -475,6 +476,9 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
475476
//! Exports either either the whole atlas or just the current feature as a PDF, depending on mode
476477
void exportCompositionAsPDF( QgsComposer::OutputMode mode );
477478

479+
//! Updates the "set as atlas feature" map layer action, removing it if atlas is disabled
480+
void updateAtlasMapLayerAction( bool atlasEnabled );
481+
478482
/**Composer title*/
479483
QString mTitle;
480484

@@ -551,6 +555,8 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
551555
//! @note added in 1.9
552556
QMenu* mHelpMenu;
553557

558+
QgsMapLayerAction* mAtlasFeatureAction;
559+
554560
signals:
555561
void printAsRasterChanged( bool state );
556562

@@ -579,6 +585,13 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
579585
//! Toggles the state of the atlas preview and navigation controls
580586
//! @note added in 2.1
581587
void toggleAtlasControls( bool atlasEnabled );
588+
589+
//! Sets the specified feature as the current atlas feature
590+
//! @note added in 2.1
591+
void setAtlasFeature( QgsMapLayer* layer, QgsFeature * feat );
592+
593+
//! Updates the "set as atlas feature" map layer action when atlas coverage layer changes
594+
void updateAtlasMapLayerAction( QgsVectorLayer* coverageLayer );
582595
};
583596

584597
#endif

src/app/qgisapp.cpp

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@
192192
#include "qgsvectorlayerproperties.h"
193193
#include "qgsmessagelogviewer.h"
194194
#include "qgsdataitem.h"
195+
#include "qgsmaplayeractionregistry.h"
195196

196197
#include "qgssublayersdialog.h"
197198
#include "ogr/qgsopenvectorlayerdialog.h"
@@ -609,6 +610,8 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent,
609610

610611
activateDeactivateLayerRelatedActions( NULL ); // after members were created
611612

613+
connect( QgsMapLayerActionRegistry::instance(), SIGNAL( changed() ), this, SLOT( refreshActionFeatureAction() ) );
614+
612615
// set application's caption
613616
QString caption = tr( "QGIS - %1 ('%2')" ).arg( QGis::QGIS_VERSION ).arg( QGis::QGIS_RELEASE_NAME );
614617
setWindowTitle( caption );
@@ -4276,7 +4279,27 @@ void QgisApp::updateDefaultFeatureAction( QAction *action )
42764279
mFeatureActionMenu->setActiveAction( action );
42774280

42784281
int index = mFeatureActionMenu->actions().indexOf( action );
4279-
vlayer->actions()->setDefaultAction( index );
4282+
4283+
if ( vlayer->actions()->size() > 0 && index < vlayer->actions()->size() )
4284+
{
4285+
vlayer->actions()->setDefaultAction( index );
4286+
QgsMapLayerActionRegistry::instance()->setDefaultActionForLayer( vlayer, 0 );
4287+
}
4288+
else
4289+
{
4290+
//action is from QgsMapLayerActionRegistry
4291+
vlayer->actions()->setDefaultAction( -1 );
4292+
4293+
QgsMapLayerAction * mapLayerAction = dynamic_cast<QgsMapLayerAction *>( action );
4294+
if ( mapLayerAction )
4295+
{
4296+
QgsMapLayerActionRegistry::instance()->setDefaultActionForLayer( vlayer, mapLayerAction );
4297+
}
4298+
else
4299+
{
4300+
QgsMapLayerActionRegistry::instance()->setDefaultActionForLayer( vlayer, 0 );
4301+
}
4302+
}
42804303

42814304
doFeatureAction();
42824305
}
@@ -4298,6 +4321,24 @@ void QgisApp::refreshFeatureActions()
42984321
mFeatureActionMenu->setActiveAction( action );
42994322
}
43004323
}
4324+
4325+
//add actions registered in QgsMapLayerActionRegistry
4326+
QList<QgsMapLayerAction *> registeredActions = QgsMapLayerActionRegistry::instance()->mapLayerActions( vlayer );
4327+
if ( actions->size() > 0 && registeredActions.size() > 0 )
4328+
{
4329+
//add a seperator between user defined and standard actions
4330+
mFeatureActionMenu->addSeparator();
4331+
}
4332+
4333+
for ( int i = 0; i < registeredActions.size(); i++ )
4334+
{
4335+
mFeatureActionMenu->addAction( registeredActions.at( i ) );
4336+
if ( registeredActions.at( i ) == QgsMapLayerActionRegistry::instance()->defaultActionForLayer( vlayer ) )
4337+
{
4338+
mFeatureActionMenu->setActiveAction( registeredActions.at( i ) );
4339+
}
4340+
}
4341+
43014342
}
43024343

43034344
void QgisApp::measure()
@@ -8489,7 +8530,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
84898530

84908531
bool isEditable = vlayer->isEditable();
84918532
bool layerHasSelection = vlayer->selectedFeatureCount() > 0;
8492-
bool layerHasActions = vlayer->actions()->size() > 0;
8533+
bool layerHasActions = vlayer->actions()->size() + QgsMapLayerActionRegistry::instance()->mapLayerActions( vlayer ).size() > 0;
84938534

84948535
bool canChangeAttributes = dprovider->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
84958536
bool canDeleteFeatures = dprovider->capabilities() & QgsVectorDataProvider::DeleteFeatures;
@@ -8733,8 +8774,20 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
87338774
}
87348775
}
87358776

8777+
void QgisApp::refreshActionFeatureAction()
8778+
{
8779+
QgsMapLayer* layer = activeLayer();
87368780

8781+
if ( layer->type() != QgsMapLayer::VectorLayer )
8782+
{
8783+
return;
8784+
}
87378785

8786+
QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( layer );
8787+
8788+
bool layerHasActions = vlayer->actions()->size() + QgsMapLayerActionRegistry::instance()->mapLayerActions( vlayer ).size() > 0;
8789+
mActionFeatureAction->setEnabled( layerHasActions );
8790+
}
87388791

87398792
/////////////////////////////////////////////////////////////////
87408793
//

0 commit comments

Comments
 (0)