Skip to content

Commit

Permalink
Add QgsMapLayerAction and QgsMapLayerActionRegistry, which can be
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
nyalldawson committed Jan 20, 2014
1 parent fc3094f commit 85a17c2
Show file tree
Hide file tree
Showing 22 changed files with 723 additions and 17 deletions.
10 changes: 10 additions & 0 deletions python/core/composer/qgsatlascomposition.sip
Expand Up @@ -82,6 +82,9 @@ public:
/** Prepare the atlas map for the given feature. Sets the extent and context variables */
void prepareForFeature( int i );

/** Prepare the atlas map for the given feature. Sets the extent and context variables */
void prepareForFeature( QgsFeature * feat );

/** Returns the current filename. Must be called after prepareForFeature( i ) */
const QString& currentFilename() const;

Expand All @@ -108,4 +111,11 @@ public:
signals:
/** emitted when one of the parameters changes */
void parameterChanged();

/** emitted when atlas is enabled or disabled */
void toggled( bool );

/**Is emitted when the coverage layer for an atlas changes*/
void coverageLayerChanged( QgsVectorLayer* layer );

};
6 changes: 6 additions & 0 deletions python/gui/attributetable/qgsattributetablemodel.sip
Expand Up @@ -2,6 +2,7 @@ class QgsAttributeTableModel : QAbstractTableModel
{
%TypeHeaderCode
#include <qgsattributetablemodel.h>
#include <qgsmaplayeractionregistry.h>
%End
public:
enum Role
Expand Down Expand Up @@ -133,6 +134,11 @@ class QgsAttributeTableModel : QAbstractTableModel
*/
void executeAction( int action, const QModelIndex &idx ) const;

/**
* Execute a QgsMapLayerAction
*/
void executeMapLayerAction( QgsMapLayerAction* action, const QModelIndex &idx ) const;

/**
* Return the feature attributes at given model index
* @return feature attributes at given model index
Expand Down
14 changes: 14 additions & 0 deletions python/gui/attributetable/qgsdualview.sip
Expand Up @@ -132,3 +132,17 @@ class QgsAttributeTableAction : QAction
void execute();
void featureForm();
};

class QgsAttributeTableMapLayerAction : QAction
{
%TypeHeaderCode
#include <qgsdualview.h>
%End

public:
QgsAttributeTableMapLayerAction( const QString &name, QgsDualView *dualView, QgsMapLayerAction* action, const QModelIndex &fieldIdx );

public slots:
void execute();

};
1 change: 1 addition & 0 deletions python/gui/gui.sip
Expand Up @@ -42,6 +42,7 @@
%Include qgsmapcanvasitem.sip
%Include qgsmapcanvasmap.sip
%Include qgsmapcanvassnapper.sip
%Include qgsmaplayeractionregistry.sip
%Include qgsmapoverviewcanvas.sip
%Include qgsmaptip.sip
%Include qgsmaptool.sip
Expand Down
72 changes: 72 additions & 0 deletions python/gui/qgsmaplayeractionregistry.sip
@@ -0,0 +1,72 @@
class QgsMapLayerAction : QAction
{
%TypeHeaderCode
#include <qgsmaplayeractionregistry.h>
%End

public:

/**Creates a map layer action which can run on any layer*/
QgsMapLayerAction( QString name, QObject *parent );
/**Creates a map layer action which can run only on a specific layer*/
QgsMapLayerAction( QString name, QObject *parent, QgsMapLayer* layer );
/**Creates a map layer action which can run on a specific type of layer*/
QgsMapLayerAction( QString name, QObject *parent, QgsMapLayer::LayerType layerType );

~QgsMapLayerAction();

/** True if action can run using the specified layer */
bool canRunUsingLayer( QgsMapLayer* layer ) const;

/** Triggers the action with the specified layer and feature. This also emits the triggeredForLayer( QgsMapLayer *)
* and triggered() slots */
void triggerForFeature( QgsMapLayer* layer, QgsFeature* feature );

/** Triggers the action with the specified layer. This also emits the triggered() slot. */
void triggerForLayer( QgsMapLayer* layer );

signals:
/** Triggered when action has been run for a specific feature */
void triggeredForFeature( QgsMapLayer* layer, QgsFeature* feature );

/** Triggered when action has been run for a specific layer */
void triggeredForLayer( QgsMapLayer* layer );

};

class QgsMapLayerActionRegistry : QObject
{
%TypeHeaderCode
#include <qgsmaplayeractionregistry.h>
%End

public:

//! Returns the instance pointer, creating the object on the first call
static QgsMapLayerActionRegistry * instance();

~QgsMapLayerActionRegistry();

/**Adds a map layer action to the registry*/
void addMapLayerAction( QgsMapLayerAction * action );

/**Returns the map layer actions which can run on the specified layer*/
QList<QgsMapLayerAction *> mapLayerActions( QgsMapLayer* layer );

/**Removes a map layer action from the registry*/
bool removeMapLayerAction( QgsMapLayerAction *action );

/**Sets the default action for a layer*/
void setDefaultActionForLayer( QgsMapLayer* layer, QgsMapLayerAction* action );
/**Returns the default action for a layer*/
QgsMapLayerAction * defaultActionForLayer( QgsMapLayer* layer );

protected:
//! protected constructor
QgsMapLayerActionRegistry( QObject * parent = 0 );

signals:
/** Triggered when an action is added or removed from the registry */
void changed();

};
68 changes: 68 additions & 0 deletions src/app/composer/qgscomposer.cpp
Expand Up @@ -54,6 +54,8 @@
#include "qgsmessageviewer.h"
#include "qgscontexthelp.h"
#include "qgscursors.h"
#include "qgsmaplayeractionregistry.h"
#include "qgsgeometry.h"

#include <QCloseEvent>
#include <QCheckBox>
Expand Down Expand Up @@ -94,6 +96,7 @@ QgsComposer::QgsComposer( QgisApp *qgis, const QString& title )
, mTitle( title )
, mQgis( qgis )
, mUndoView( 0 )
, mAtlasFeatureAction( 0 )
{
setupUi( this );
setWindowTitle( mTitle );
Expand Down Expand Up @@ -509,6 +512,7 @@ QgsComposer::QgsComposer( QgisApp *qgis, const QString& title )
mActionExportAtlasAsPDF->setEnabled( false );
QgsAtlasComposition* atlasMap = &mComposition->atlasComposition();
connect( atlasMap, SIGNAL( toggled( bool ) ), this, SLOT( toggleAtlasControls( bool ) ) );
connect( atlasMap, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( updateAtlasMapLayerAction( QgsVectorLayer * ) ) );

// Create size grip (needed by Mac OS X for QMainWindow if QStatusBar is not visible)
//should not be needed now that composer has a status bar?
Expand Down Expand Up @@ -704,6 +708,12 @@ void QgsComposer::setTitle( const QString& title )
{
mWindowAction->setText( title );
}

//update atlas map layer action name if required
if ( mAtlasFeatureAction )
{
mAtlasFeatureAction->setText( QString( tr( "Set as atlas feature for %1" ) ).arg( mTitle ) );
}
}

void QgsComposer::updateStatusCursorPos( QPointF cursorPosition )
Expand Down Expand Up @@ -820,6 +830,8 @@ void QgsComposer::toggleAtlasControls( bool atlasEnabled )
mActionExportAtlasAsImage->setEnabled( atlasEnabled );
mActionExportAtlasAsSVG->setEnabled( atlasEnabled );
mActionExportAtlasAsPDF->setEnabled( atlasEnabled );

updateAtlasMapLayerAction( atlasEnabled );
}

void QgsComposer::on_mActionAtlasPreview_triggered( bool checked )
Expand Down Expand Up @@ -2655,6 +2667,8 @@ void QgsComposer::readXML( const QDomElement& composerElem, const QDomDocument&
mActionExportAtlasAsSVG->setEnabled( atlasMap->enabled() );
mActionExportAtlasAsPDF->setEnabled( atlasMap->enabled() );
connect( atlasMap, SIGNAL( toggled( bool ) ), this, SLOT( toggleAtlasControls( bool ) ) );
connect( atlasMap, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( updateAtlasMapLayerAction( QgsVectorLayer * ) ) );
updateAtlasMapLayerAction( atlasMap->enabled() );

setSelectionTool();
}
Expand Down Expand Up @@ -3083,3 +3097,57 @@ void QgsComposer::writeWorldFile( QString worldFileName, double a, double b, dou
fout << QString::number( c, 'f' ) << "\r\n";
fout << QString::number( f, 'f' ) << "\r\n";
}


void QgsComposer::setAtlasFeature( QgsMapLayer* layer, QgsFeature * feat )
{
//update expression variables
QgsExpression::setSpecialColumn( "$atlasfeatureid", feat->id() );
QgsExpression::setSpecialColumn( "$atlasgeometry", QVariant::fromValue( *( feat->geometry() ) ) );

emit atlasPreviewFeatureChanged();

//check if composition has atlas preview
QgsAtlasComposition& atlas = mComposition->atlasComposition();
if ( ! atlas.enabled() || ! mComposition->atlasMode() == QgsComposition::PreviewAtlas || atlas.coverageLayer() != layer )
{
//either atlas preview isn't enabled, or layer doesn't match
return;
}

//set current preview feature id
atlas.prepareForFeature( feat );
}

void QgsComposer::updateAtlasMapLayerAction( QgsVectorLayer *coverageLayer )
{
if ( mAtlasFeatureAction )
{
delete mAtlasFeatureAction;
mAtlasFeatureAction = 0;
}

if ( coverageLayer )
{
mAtlasFeatureAction = new QgsMapLayerAction( QString( tr( "Set as atlas feature for %1" ) ).arg( mTitle ), this, coverageLayer );
QgsMapLayerActionRegistry::instance()->addMapLayerAction( mAtlasFeatureAction );
connect( mAtlasFeatureAction, SIGNAL( triggeredForFeature( QgsMapLayer*, QgsFeature* ) ), this, SLOT( setAtlasFeature( QgsMapLayer*, QgsFeature* ) ) );
}
}

void QgsComposer::updateAtlasMapLayerAction( bool atlasEnabled )
{
if ( mAtlasFeatureAction )
{
delete mAtlasFeatureAction;
mAtlasFeatureAction = 0;
}

if ( atlasEnabled )
{
QgsAtlasComposition& atlas = mComposition->atlasComposition();
mAtlasFeatureAction = new QgsMapLayerAction( QString( tr( "Set as atlas feature for %1" ) ).arg( mTitle ), this, atlas.coverageLayer() );
QgsMapLayerActionRegistry::instance()->addMapLayerAction( mAtlasFeatureAction );
connect( mAtlasFeatureAction, SIGNAL( triggeredForFeature( QgsMapLayer*, QgsFeature* ) ), this, SLOT( setAtlasFeature( QgsMapLayer*, QgsFeature* ) ) );
}
}
13 changes: 13 additions & 0 deletions src/app/composer/qgscomposer.h
Expand Up @@ -38,6 +38,7 @@ class QgsComposerView;
class QgsComposition;
class QgsMapCanvas;
class QgsAtlasComposition;
class QgsMapLayerAction;

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

//! Updates the "set as atlas feature" map layer action, removing it if atlas is disabled
void updateAtlasMapLayerAction( bool atlasEnabled );

/**Composer title*/
QString mTitle;

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

QgsMapLayerAction* mAtlasFeatureAction;

signals:
void printAsRasterChanged( bool state );

Expand Down Expand Up @@ -579,6 +585,13 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
//! Toggles the state of the atlas preview and navigation controls
//! @note added in 2.1
void toggleAtlasControls( bool atlasEnabled );

//! Sets the specified feature as the current atlas feature
//! @note added in 2.1
void setAtlasFeature( QgsMapLayer* layer, QgsFeature * feat );

//! Updates the "set as atlas feature" map layer action when atlas coverage layer changes
void updateAtlasMapLayerAction( QgsVectorLayer* coverageLayer );
};

#endif
Expand Down
57 changes: 55 additions & 2 deletions src/app/qgisapp.cpp
Expand Up @@ -192,6 +192,7 @@
#include "qgsvectorlayerproperties.h"
#include "qgsmessagelogviewer.h"
#include "qgsdataitem.h"
#include "qgsmaplayeractionregistry.h"

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

activateDeactivateLayerRelatedActions( NULL ); // after members were created

connect( QgsMapLayerActionRegistry::instance(), SIGNAL( changed() ), this, SLOT( refreshActionFeatureAction() ) );

// set application's caption
QString caption = tr( "QGIS - %1 ('%2')" ).arg( QGis::QGIS_VERSION ).arg( QGis::QGIS_RELEASE_NAME );
setWindowTitle( caption );
Expand Down Expand Up @@ -4276,7 +4279,27 @@ void QgisApp::updateDefaultFeatureAction( QAction *action )
mFeatureActionMenu->setActiveAction( action );

int index = mFeatureActionMenu->actions().indexOf( action );
vlayer->actions()->setDefaultAction( index );

if ( vlayer->actions()->size() > 0 && index < vlayer->actions()->size() )
{
vlayer->actions()->setDefaultAction( index );
QgsMapLayerActionRegistry::instance()->setDefaultActionForLayer( vlayer, 0 );
}
else
{
//action is from QgsMapLayerActionRegistry
vlayer->actions()->setDefaultAction( -1 );

QgsMapLayerAction * mapLayerAction = dynamic_cast<QgsMapLayerAction *>( action );
if ( mapLayerAction )
{
QgsMapLayerActionRegistry::instance()->setDefaultActionForLayer( vlayer, mapLayerAction );
}
else
{
QgsMapLayerActionRegistry::instance()->setDefaultActionForLayer( vlayer, 0 );
}
}

doFeatureAction();
}
Expand All @@ -4298,6 +4321,24 @@ void QgisApp::refreshFeatureActions()
mFeatureActionMenu->setActiveAction( action );
}
}

//add actions registered in QgsMapLayerActionRegistry
QList<QgsMapLayerAction *> registeredActions = QgsMapLayerActionRegistry::instance()->mapLayerActions( vlayer );
if ( actions->size() > 0 && registeredActions.size() > 0 )
{
//add a seperator between user defined and standard actions
mFeatureActionMenu->addSeparator();
}

for ( int i = 0; i < registeredActions.size(); i++ )
{
mFeatureActionMenu->addAction( registeredActions.at( i ) );
if ( registeredActions.at( i ) == QgsMapLayerActionRegistry::instance()->defaultActionForLayer( vlayer ) )
{
mFeatureActionMenu->setActiveAction( registeredActions.at( i ) );
}
}

}

void QgisApp::measure()
Expand Down Expand Up @@ -8489,7 +8530,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )

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

bool canChangeAttributes = dprovider->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
bool canDeleteFeatures = dprovider->capabilities() & QgsVectorDataProvider::DeleteFeatures;
Expand Down Expand Up @@ -8733,8 +8774,20 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
}
}

void QgisApp::refreshActionFeatureAction()
{
QgsMapLayer* layer = activeLayer();

if ( layer->type() != QgsMapLayer::VectorLayer )
{
return;
}

QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( layer );

bool layerHasActions = vlayer->actions()->size() + QgsMapLayerActionRegistry::instance()->mapLayerActions( vlayer ).size() > 0;
mActionFeatureAction->setEnabled( layerHasActions );
}

/////////////////////////////////////////////////////////////////
//
Expand Down

0 comments on commit 85a17c2

Please sign in to comment.