Move addFeature, startEditing and stopEditing vector layer actions

to an abstract base class QgsVectorLayerTools in the gui library,
so these can be reimplemented for a custom app, but can also be used
from plugins or in the gui library.
This commit is contained in:
Matthias Kuhn 2013-10-04 11:22:57 +02:00
parent 331f71acba
commit fb32966932
14 changed files with 374 additions and 14 deletions

View File

@ -69,6 +69,7 @@
%Include qgssearchquerybuilder.sip
%Include qgstextannotationitem.sip
%Include qgsvertexmarker.sip
%Include qgsvectorlayertools.sip
%Include qgssublayersdialog.sip
%Include qgscharacterselectdialog.sip
%Include qgscomposerruler.sip

View File

@ -484,6 +484,8 @@ class QgisInterface : QObject
virtual QDialog* getFeatureForm( QgsVectorLayer *l, QgsFeature &f ) = 0;
virtual QgsVectorLayerTools* vectorLayerTools() = 0;
virtual void preloadForm( QString uifile ) = 0;
/** Return vector layers in edit mode

View File

@ -0,0 +1,10 @@
class QgsVectorLayerTools
{
%TypeHeaderCode
#include <qgsvectorlayertools.h>
%End
public:
virtual bool addFeature( QgsVectorLayer* layer, QgsAttributeMap defaultValues = QgsAttributeMap(), const QgsGeometry& = QgsGeometry() ) = 0;
virtual bool startEditing( QgsVectorLayer* layer ) = 0;
virtual bool stopEditing( QgsVectorLayer* layer, bool allowCancel = true ) = 0;
};

View File

@ -43,6 +43,7 @@ SET(QGIS_APP_SRCS
qgslabelpreview.cpp
qgsloadstylefromdbdialog.cpp
qgssavestyletodbdialog.cpp
qgsguivectorlayertools.cpp
qgsmaptooladdfeature.cpp
qgsmaptooladdpart.cpp
@ -99,6 +100,7 @@ SET(QGIS_APP_SRCS
qgsrasterlayerproperties.cpp
qgstextannotationdialog.cpp
qgsshortcutsmanager.cpp
qgsguivectorlayertools.h
qgssnappingdialog.cpp
qgssvgannotationdialog.cpp
qgsundowidget.cpp

View File

@ -133,6 +133,7 @@
#include "qgshtmlannotationitem.h"
#include "qgsgenericprojectionselector.h"
#include "qgsgpsinformationwidget.h"
#include "qgsguivectorlayertools.h"
#include "qgslabelinggui.h"
#include "qgslegend.h"
#include "qgslayerorder.h"
@ -574,6 +575,8 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent,
mLogDock->setWidget( mLogViewer );
mLogDock->hide();
mVectorLayerTools = new QgsGuiVectorLayerTools();
mInternalClipboard = new QgsClipboard; // create clipboard
connect( mInternalClipboard, SIGNAL( changed() ), this, SLOT( clipboardChanged() ) );
mQgisInterface = new QgisAppInterface( this ); // create the interfce

View File

@ -57,9 +57,9 @@ class QgsPoint;
class QgsProviderRegistry;
class QgsPythonUtils;
class QgsRectangle;
class QgsUndoWidget;
class QgsVectorLayer;
class QgsVectorLayerTools;
class QDomDocument;
class QNetworkReply;
@ -242,6 +242,13 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
/** overloaded function used to sort menu entries alphabetically */
QMenu* createPopupMenu();
/**
* Access the vector layer tools. This will be an instance of {@see QgsGuiVectorLayerTools}
* by default.
* @return The vector layer tools
*/
QgsVectorLayerTools* vectorLayerTools() { return mVectorLayerTools; }
//! Actions to be inserted in menus and toolbars
QAction *actionNewProject() { return mActionNewProject; }
QAction *actionOpenProject() { return mActionOpenProject; }
@ -1518,6 +1525,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
QgsMessageBar *mInfoBar;
QWidget *mMacrosWarn;
QgsVectorLayerTools* mVectorLayerTools;
#ifdef HAVE_TOUCH
bool gestureEvent( QGestureEvent *event );
void tapAndHoldTriggered( QTapAndHoldGesture *gesture );

View File

@ -80,6 +80,7 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid
myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
context.setDistanceArea( myDa );
context.setVectorLayerTools( QgisApp::instance()->vectorLayerTools() );
// Initialize dual view
mMainView->init( mLayer, QgisApp::instance()->mapCanvas(), QgsFeatureRequest(), context );

View File

@ -15,16 +15,17 @@
* *
***************************************************************************/
#include "qgsfeatureaction.h"
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include "qgsidentifyresultsdialog.h"
#include "qgsattributedialog.h"
#include "qgslogger.h"
#include "qgsdistancearea.h"
#include "qgisapp.h"
#include "qgsproject.h"
#include "qgsattributedialog.h"
#include "qgsdistancearea.h"
#include "qgsfeatureaction.h"
#include "qgsguivectorlayertools.h"
#include "qgsidentifyresultsdialog.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"
#include "qgsproject.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
#include <QPushButton>
#include <QSettings>
@ -47,13 +48,18 @@ QgsAttributeDialog *QgsFeatureAction::newDialog( bool cloneFeature )
{
QgsFeature *f = cloneFeature ? new QgsFeature( mFeature ) : &mFeature;
QgsAttributeEditorContext context;
QgsDistanceArea myDa;
myDa.setSourceCrs( mLayer->crs() );
myDa.setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapRenderer()->hasCrsTransformEnabled() );
myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
QgsAttributeDialog *dialog = new QgsAttributeDialog( mLayer, f, cloneFeature, myDa );
context.setDistanceArea( myDa );
context.setVectorLayerTools( QgisApp::instance()->vectorLayerTools() );
QgsAttributeDialog *dialog = new QgsAttributeDialog( mLayer, f, cloneFeature, NULL, true, context );
if ( mLayer->actions()->size() > 0 )
{
@ -138,7 +144,7 @@ bool QgsFeatureAction::editFeature()
return res;
}
bool QgsFeatureAction::addFeature()
bool QgsFeatureAction::addFeature( const QgsAttributeMap& defaultAttributes )
{
if ( !mLayer || !mLayer->isEditable() )
return false;
@ -154,7 +160,12 @@ bool QgsFeatureAction::addFeature()
mFeature.initAttributes( fields.count() );
for ( int idx = 0; idx < fields.count(); ++idx )
{
if ( reuseLastValues && mLastUsedValues.contains( mLayer ) && mLastUsedValues[ mLayer ].contains( idx ) )
if ( defaultAttributes.contains( idx ) )
{
QgsDebugMsg( QString( "Using specified default %1 for %2" ).arg( defaultAttributes.value( idx ).toString() ).arg( idx ) );
mFeature.setAttribute( idx, defaultAttributes.value( idx ) );
}
else if ( reuseLastValues && mLastUsedValues.contains( mLayer ) && mLastUsedValues[ mLayer ].contains( idx ) )
{
QgsDebugMsg( QString( "reusing %1 for %2" ).arg( mLastUsedValues[ mLayer ][idx].toString() ).arg( idx ) );
mFeature.setAttribute( idx, mLastUsedValues[ mLayer ][idx] );

View File

@ -18,6 +18,7 @@
#define QGSFEATUREACTION_H
#include "qgsfeature.h"
#include "qgsvectorlayertools.h"
#include <QList>
#include <QPair>
@ -33,13 +34,23 @@ class APP_EXPORT QgsFeatureAction : public QAction
Q_OBJECT
public:
QgsFeatureAction( const QString &name, QgsFeature &f, QgsVectorLayer *vl, int action, int defaultAttr, QObject *parent );
QgsFeatureAction( const QString &name, QgsFeature &f, QgsVectorLayer *vl, int action = -1, int defaultAttr = -1, QObject *parent = NULL );
public slots:
void execute();
bool viewFeatureForm( QgsHighlight *h = 0 );
bool editFeature();
bool addFeature();
/**
* Add a new feature to the layer.
* Will set the default values to recently used or provider defaults based on settings
* and override with values in defaultAttributes if provided.
*
* @param defaultAttributes Provide some default attributes here if desired.
*
* @return true if feature was added
*/
bool addFeature( const QgsAttributeMap& defaultAttributes = QgsAttributeMap() );
private:
QgsAttributeDialog *newDialog( bool cloneFeature );

View File

@ -0,0 +1,162 @@
/***************************************************************************
qgsfeaturefactory.cpp
--------------------------------------
Date : 30.5.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <QMessageBox>
#include <QToolButton>
#include "qgsguivectorlayertools.h"
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include "qgsmessagebar.h"
#include "qgisapp.h"
#include "qgsapplication.h"
#include "qgsmessageviewer.h"
#include "qgsfeatureaction.h"
#include "qgsmapcanvas.h"
#include "qgsmessagebaritem.h"
QgsGuiVectorLayerTools::QgsGuiVectorLayerTools()
: QObject( NULL )
{}
bool QgsGuiVectorLayerTools::addFeature( QgsVectorLayer* layer, QgsAttributeMap defaultValues, const QgsGeometry& defaultGeometry )
{
QgsFeature f;
f.setGeometry( defaultGeometry );
QgsFeatureAction a( tr( "Add feature" ), f, layer );
return a.addFeature( defaultValues );
}
bool QgsGuiVectorLayerTools::startEditing( QgsVectorLayer* layer )
{
if ( !layer )
{
return false;
}
bool res = true;
if ( !layer->isEditable() && !layer->isReadOnly() )
{
if ( !( layer->dataProvider()->capabilities() & QgsVectorDataProvider::EditingCapabilities ) )
{
QgisApp::instance()->messageBar()->pushMessage( tr( "Start editing failed" ),
tr( "Provider cannot be opened for editing" ),
QgsMessageBar::INFO, QgisApp::instance()->messageTimeout() );
return false;
}
layer->startEditing();
}
return res;
}
bool QgsGuiVectorLayerTools::stopEditing( QgsVectorLayer* layer, bool allowCancel )
{
bool res = true;
if ( layer->isModified() )
{
QMessageBox::StandardButtons buttons = QMessageBox::Save | QMessageBox::Discard;
if ( allowCancel )
buttons |= QMessageBox::Cancel;
switch ( QMessageBox::information( 0,
tr( "Stop editing" ),
tr( "Do you want to save the changes to layer %1?" ).arg( layer->name() ),
buttons ) )
{
case QMessageBox::Cancel:
res = false;
break;
case QMessageBox::Save:
if ( !layer->commitChanges() )
{
commitError( layer );
// Leave the in-memory editing state alone,
// to give the user a chance to enter different values
// and try the commit again later
res = false;
}
layer->triggerRepaint();
break;
case QMessageBox::Discard:
QgisApp::instance()->mapCanvas()->freeze( true );
if ( !layer->rollBack() )
{
QgisApp::instance()->messageBar()->pushMessage( tr( "Error" ),
tr( "Problems during roll back" ),
QgsMessageBar::CRITICAL );
res = false;
}
QgisApp::instance()->mapCanvas()->freeze( false );
layer->triggerRepaint();
break;
default:
break;
}
}
else //layer not modified
{
QgisApp::instance()->mapCanvas()->freeze( true );
layer->rollBack();
QgisApp::instance()->mapCanvas()->freeze( false );
res = true;
layer->triggerRepaint();
}
return res;
}
void QgsGuiVectorLayerTools::commitError( QgsVectorLayer* vlayer )
{
QgsMessageViewer *mv = new QgsMessageViewer();
mv->setWindowTitle( tr( "Commit errors" ) );
mv->setMessageAsPlainText( tr( "Could not commit changes to layer %1" ).arg( vlayer->name() )
+ "\n\n"
+ tr( "Errors: %1\n" ).arg( vlayer->commitErrors().join( "\n " ) )
);
QToolButton *showMore = new QToolButton();
// store pointer to vlayer in data of QAction
QAction *act = new QAction( showMore );
act->setData( QVariant( QMetaType::QObjectStar, &vlayer ) );
act->setText( tr( "Show more" ) );
showMore->setStyleSheet( "background-color: rgba(255, 255, 255, 0); color: black; text-decoration: underline;" );
showMore->setCursor( Qt::PointingHandCursor );
showMore->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
showMore->addAction( act );
showMore->setDefaultAction( act );
connect( showMore, SIGNAL( triggered( QAction* ) ), mv, SLOT( exec() ) );
connect( showMore, SIGNAL( triggered( QAction* ) ), showMore, SLOT( deleteLater() ) );
// no timeout set, since notice needs attention and is only shown first time layer is labeled
QgsMessageBarItem *errorMsg = new QgsMessageBarItem(
tr( "Commit errors" ),
tr( "Could not commit changes to layer %1" ).arg( vlayer->name() ),
showMore,
QgsMessageBar::WARNING,
0,
QgisApp::instance()->messageBar() );
QgisApp::instance()->messageBar()->pushItem( errorMsg );
}

View File

@ -0,0 +1,68 @@
/***************************************************************************
qgsfeaturefactory.h
--------------------------------------
Date : 30.5.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSGUIVECTORLAYERTOOLS_H
#define QGSGUIVECTORLAYERTOOLS_H
#include "qgsvectorlayertools.h"
/**
* Implements all the dialogs and actions when editing on a vector layer is toggled
* or a feature is added.
*/
class QgsGuiVectorLayerTools : public QgsVectorLayerTools, public QObject
{
public:
QgsGuiVectorLayerTools();
/**
* This method should be called, whenever a new feature is added to a layer
*
* @param layer The layer to which the feature should be added
* @param defaultValues Default values for the feature to add
* @param defaultGeometry A default geometry to add to the feature
*
* @return True in case of success, False if the operation failed/was aborted
*/
bool addFeature( QgsVectorLayer *layer, QgsAttributeMap defaultValues, const QgsGeometry &defaultGeometry );
/**
* This should be called, whenever a vector layer should be switched to edit mode. If succesful
* the layer is switched to editable and an edit sessions started.
*
* @param layer The layer on which to start an edit session
*
* @return True, if the editing session was started
*/
bool startEditing( QgsVectorLayer* layer );
/**
* Should be called, when an editing session is ended and the features should be commited.
* An appropriate dialog asking the user if he wants to save the edits will be shown if
* allowCancel is set to true.
*
* @param layer The layer to commit
* @param allowCancel True if a cancel button should be offered
*
* @return True if successful
*/
bool stopEditing( QgsVectorLayer* layer , bool allowCancel = true );
private:
void commitError( QgsVectorLayer* vlayer );
};
#endif // QGSGUIVECTORLAYERTOOLS_H

View File

@ -16,6 +16,7 @@
#include "qgsattributeeditorcontext.h"
QgsAttributeEditorContext::QgsAttributeEditorContext()
: mVectorLayerTools( NULL )
{
}

View File

@ -21,6 +21,7 @@
#include <qgsdistancearea.h>
#include <qgsvectorlayer.h>
#include <qgsvectorlayertools.h>
/**
@ -41,6 +42,9 @@ class GUI_EXPORT QgsAttributeEditorContext
void setDistanceArea( const QgsDistanceArea& distanceArea ) { mDistanceArea = distanceArea; }
inline const QgsDistanceArea& distanceArea() { return mDistanceArea; }
void setVectorLayerTools( QgsVectorLayerTools* vlTools ) { mVectorLayerTools = vlTools; }
QgsVectorLayerTools* vectorLayerTools() { return mVectorLayerTools; }
/**
* When copying the context for another layer, call this.
* Will adjast the distance area for this layer
@ -49,7 +53,10 @@ class GUI_EXPORT QgsAttributeEditorContext
*/
void adjustForLayer( QgsVectorLayer* layer );
private:
QgsVectorLayerTools* mVectorLayerTools;
//! vectorlayer => ( fieldIdx, proxyWidget )
QMap<QgsVectorLayer*, QMap<int, QWidget*> > mProxyWidgets;

View File

@ -0,0 +1,73 @@
/***************************************************************************
QgsAbstractFeatureAction.h
--------------------------------------
Date : 29.5.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSVECTORLAYERTOOLS_H
#define QGSVECTORLAYERTOOLS_H
#include "qgsfeature.h"
#include "qgsgeometry.h"
class QgsVectorLayer;
/**
* Methods in this class are used to handle basic operations on vector layers.
* With an implementation of this class, parts of the application can ask for
* an operation to be done and the implementation will then take care of it.
*
* Reimplement this class, if you need to have custom checks or GUI elements
* in your application.
*
*/
class GUI_EXPORT QgsVectorLayerTools
{
public:
QgsVectorLayerTools()
{}
/**
* This method should/will be called, whenever a new feature will be added to the layer
*
* @param layer The layer to which the feature should be added
* @param defaultValues Default values for the feature to add
* @param defaultGeometry A default geometry to add to the feature
* @return True in case of success, False if the operation failed/was aborted
*/
virtual bool addFeature( QgsVectorLayer* layer, QgsAttributeMap defaultValues = QgsAttributeMap(), const QgsGeometry& defaultGeometry = QgsGeometry() ) = 0;
/**
* This will be called, whenever a vector layer should be switched to edit mode. Check the providers
* capability to edit in here.
* If successful layer->startEditing() will be called and true returned.
*
* @param layer The layer on which to start an edit session
*
* @return True, if the editing session was started
*/
virtual bool startEditing( QgsVectorLayer* layer ) = 0;
/**
* Will be called, when an editing session is ended and the features should be commited.
* Appropriate dialogs should be shown like
*
* @param layer The layer to commit
* @param allowCancel True if a cancel button should be offered
* @return True if successful
*/
virtual bool stopEditing( QgsVectorLayer* layer, bool allowCancel = true ) = 0;
};
#endif // QGSVECTORLAYERTOOLS_H