Skip to content

Commit

Permalink
Merge pull request #2678 from m-kuhn/transaction-group
Browse files Browse the repository at this point in the history
[FEATURE] Autotransaction mode and transaction groups
  • Loading branch information
m-kuhn committed Jan 15, 2016
2 parents 01434e8 + 8ee25b7 commit e1adc63
Show file tree
Hide file tree
Showing 16 changed files with 685 additions and 106 deletions.
1 change: 1 addition & 0 deletions python/core/core.sip
Expand Up @@ -19,6 +19,7 @@
%Include qgis.sip

%Include qgstransaction.sip
%Include qgstransactiongroup.sip
%Include qgsapplication.sip
%Include qgsattributeaction.sip
%Include qgsbrowsermodel.sip
Expand Down
5 changes: 5 additions & 0 deletions python/core/qgstransaction.sip
Expand Up @@ -59,6 +59,11 @@ class QgsTransaction : QObject /Abstract/
/** Executes sql */
virtual bool executeSql( const QString& sql, QString& error /Out/ ) = 0;

/**
* Checks if a the provider of a give layer supports transactions.
*/
static bool supportsTransaction( const QgsVectorLayer* layer );

signals:
/**
* Emitted after a rollback
Expand Down
56 changes: 56 additions & 0 deletions python/core/qgstransactiongroup.sip
@@ -0,0 +1,56 @@
/***************************************************************************
qgstransactiongroup.h - QgsTransactionGroup

---------------------
begin : 15.1.2016
copyright : (C) 2016 by Matthias Kuhn
email : mmatthias@opengis.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. *
* *
***************************************************************************/

class QgsTransactionGroup : QObject
{
%TypeHeaderCode
#include "qgstransactiongroup.h"
%End
public:
explicit QgsTransactionGroup( QObject *parent = 0 );

~QgsTransactionGroup();

/**
* Add a layer to this transaction group.
*
* Will return true if it is compatible and has been added.
*/
bool addLayer( QgsVectorLayer* layer );

/**
* Return the connection string used by this transaction group.
* Layers need be compatible when added.
*/
QString connString() const;

/**
* Return the provider key used by this transaction group.
* Layers need be compatible when added.
*/
QString providerKey() const;

/**
* Returns true if there are no layers in this transaction group.
*/
bool isEmpty() const;

signals:
/**
* Will be emitted whenever there is a commit error
*/
void commitError( const QString& msg );
};
24 changes: 12 additions & 12 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -1015,14 +1015,6 @@ class QgsVectorLayer : QgsMapLayer
*/
bool setReadOnly( bool readonly = true );

/**
* Make layer editable.
* This starts an edit session on this layer. Changes made in this edit session will not
* be made persistent until {@link commitChanges()} is called and can be reverted by calling
* {@link rollBack()}.
*/
bool startEditing();

/** Change feature's geometry */
bool changeGeometry( QgsFeatureId fid, QgsGeometry* geom );

Expand Down Expand Up @@ -1493,6 +1485,15 @@ class QgsVectorLayer : QgsMapLayer
/** Check if there is a join with a layer that will be removed */
void checkJoinLayerRemove( const QString& theLayerId );

/**
* Make layer editable.
* This starts an edit session on this layer. Changes made in this edit session will not
* be made persistent until {@link commitChanges()} is called and can be reverted by calling
* {@link rollBack()}.
*/
bool startEditing();


protected slots:
void invalidateSymbolCountedFlag();

Expand All @@ -1516,6 +1517,9 @@ class QgsVectorLayer : QgsMapLayer
/** Is emitted, when layer is checked for modifications. Use for last-minute additions */
void beforeModifiedCheck() const;

/** Is emitted, before editing on this layer is started */
void beforeEditingStarted();

/** Is emitted, when editing on this layer has started*/
void editingStarted();

Expand Down Expand Up @@ -1663,10 +1667,6 @@ class QgsVectorLayer : QgsMapLayer
*/
void writeCustomSymbology( QDomElement& element, QDomDocument& doc, QString& errorMessage ) const;

private slots:
void onRelationsLoaded();
void onJoinedFieldsChanged();
void onFeatureDeleted( QgsFeatureId fid );

protected:
/** Set the extent */
Expand Down
59 changes: 57 additions & 2 deletions src/app/qgisapp.cpp
Expand Up @@ -233,6 +233,8 @@
#include "qgslegendfilterbutton.h"
#include "qgsvirtuallayerdefinition.h"
#include "qgsvirtuallayerdefinitionutils.h"
#include "qgstransaction.h"
#include "qgstransactiongroup.h"

#include "qgssublayersdialog.h"
#include "ogr/qgsopenvectorlayerdialog.h"
Expand Down Expand Up @@ -7512,6 +7514,22 @@ void QgisApp::removingLayers( const QStringList& theLayers )

toggleEditing( vlayer, false );
}

if ( autoTransaction() )
{
for ( QMap< QPair< QString, QString>, QgsTransactionGroup*>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
{
if ( tg.value()->isEmpty() )
{
delete tg.value();
tg = mTransactionGroups.erase( tg );
}
else
{
++tg;
}
}
}
}

void QgisApp::removeAllLayers()
Expand Down Expand Up @@ -9334,9 +9352,8 @@ void QgisApp::extentChanged()

void QgisApp::layersWereAdded( const QList<QgsMapLayer *>& theLayers )
{
for ( int i = 0; i < theLayers.size(); ++i )
Q_FOREACH ( QgsMapLayer* layer, theLayers )
{
QgsMapLayer * layer = theLayers.at( i );
QgsDataProvider *provider = nullptr;

QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
Expand All @@ -9352,6 +9369,27 @@ void QgisApp::layersWereAdded( const QList<QgsMapLayer *>& theLayers )
connect( vlayer, SIGNAL( editingStarted() ), this, SLOT( layerEditStateChanged() ) );
connect( vlayer, SIGNAL( editingStopped() ), this, SLOT( layerEditStateChanged() ) );
}

if ( autoTransaction() )
{
if ( QgsTransaction::supportsTransaction( vlayer ) )
{
QString connString = QgsDataSourceURI( vlayer->source() ).connectionInfo();
QString key = vlayer->providerType();

QgsTransactionGroup* tg = mTransactionGroups.value( qMakePair( key, connString ) );

if ( !tg )
{
tg = new QgsTransactionGroup();
mTransactionGroups.insert( qMakePair( key, connString ), tg );

connect( tg, SIGNAL( commitError ), this, SLOT( displayMapToolMessage( QString, QgsMessageBar::MessageLevel ) ) );
}
tg->addLayer( vlayer );
}
}

provider = vProvider;
}

Expand Down Expand Up @@ -9995,6 +10033,23 @@ void QgisApp::refreshActionFeatureAction()
mActionFeatureAction->setEnabled( layerHasActions );
}

bool QgisApp::autoTransaction() const
{
QSettings settings;
return settings.value( "/qgis/autoTransaction", false ).toBool();
}

void QgisApp::setAutoTransaction( bool state )
{
QSettings settings;

if ( settings.value( "/qgis/autoTransaction", false ).toBool() != state )
{

settings.setValue( "/qgis/autoTransaction", state );
}
}

/////////////////////////////////////////////////////////////////
//
//
Expand Down
7 changes: 7 additions & 0 deletions src/app/qgisapp.h
Expand Up @@ -64,6 +64,7 @@ class QgsProviderRegistry;
class QgsPythonUtils;
class QgsRectangle;
class QgsSnappingUtils;
class QgsTransactionGroup;
class QgsUndoWidget;
class QgsUserInputDockWidget;
class QgsVectorLayer;
Expand Down Expand Up @@ -671,6 +672,9 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow

QMenu *panelMenu() { return mPanelMenu; }

bool autoTransaction() const;
void setAutoTransaction( bool state );

protected:

//! Handle state changes (WindowTitleChange)
Expand Down Expand Up @@ -1668,6 +1672,9 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow

QgsComposerManager *mComposerManager;

//! map of transaction group: QPair( providerKey, connString ) -> transactionGroup
QMap< QPair< QString, QString>, QgsTransactionGroup*> mTransactionGroups;

//! Persistent tile scale slider
QgsTileScaleWidget *mpTileScaleWidget;

Expand Down
2 changes: 2 additions & 0 deletions src/app/qgsoptions.cpp
Expand Up @@ -591,6 +591,7 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl ) :
cbxAddOracleDC->setChecked( mSettings->value( "/qgis/addOracleDC", false ).toBool() );
cbxCompileExpressions->setChecked( mSettings->value( "/qgis/compileExpressions", true ).toBool() );
cbxCreateRasterLegendIcons->setChecked( mSettings->value( "/qgis/createRasterLegendIcons", false ).toBool() );
cbxAutoTransaction->setChecked( QgisApp::instance()->autoTransaction() );
cbxCopyWKTGeomFromTable->setChecked( mSettings->value( "/qgis/copyGeometryAsWKT", true ).toBool() );
leNullValue->setText( mSettings->value( "qgis/nullValue", "NULL" ).toString() );
cbxIgnoreShapeEncoding->setChecked( mSettings->value( "/qgis/ignoreShapeEncoding", true ).toBool() );
Expand Down Expand Up @@ -1141,6 +1142,7 @@ void QgsOptions::saveOptions()
mSettings->setValue( "/qgis/defaultLegendGraphicResolution", mLegendGraphicResolutionSpinBox->value() );
bool createRasterLegendIcons = mSettings->value( "/qgis/createRasterLegendIcons", false ).toBool();
mSettings->setValue( "/qgis/createRasterLegendIcons", cbxCreateRasterLegendIcons->isChecked() );
QgisApp::instance()->setAutoTransaction( cbxAutoTransaction->isChecked() );
mSettings->setValue( "/qgis/copyGeometryAsWKT", cbxCopyWKTGeomFromTable->isChecked() );
mSettings->setValue( "/qgis/new_layers_visible", chkAddedVisibility->isChecked() );
mSettings->setValue( "/qgis/enable_anti_aliasing", chkAntiAliasing->isChecked() );
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -190,6 +190,7 @@ SET(QGIS_CORE_SRCS
qgstolerance.cpp
qgstracer.cpp
qgstransaction.cpp
qgstransactiongroup.cpp
qgsvectordataprovider.cpp
qgsvectorfilewriter.cpp
qgsvectorlayer.cpp
Expand Down Expand Up @@ -459,6 +460,7 @@ SET(QGIS_CORE_MOC_HDRS
qgssnappingutils.h
qgstracer.h
qgstransaction.h
qgstransactiongroup.h
qgsvectordataprovider.h
qgsvectorlayercache.h
qgsvectorlayereditbuffer.h
Expand Down
35 changes: 13 additions & 22 deletions src/core/qgstransaction.cpp
Expand Up @@ -83,18 +83,12 @@ QgsTransaction::~QgsTransaction()

bool QgsTransaction::addLayer( const QString& layerId )
{
if ( mTransactionActive )
return false;

QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( layerId ) );
return addLayer( layer );
}

bool QgsTransaction::addLayer( QgsVectorLayer* layer )
{
if ( mTransactionActive )
return false;

if ( !layer )
return false;

Expand All @@ -119,6 +113,10 @@ bool QgsTransaction::addLayer( QgsVectorLayer* layer )
connect( this, SIGNAL( afterRollback() ), layer->dataProvider(), SIGNAL( dataChanged() ) );
connect( QgsMapLayerRegistry::instance(), SIGNAL( layersWillBeRemoved( QStringList ) ), this, SLOT( onLayersDeleted( QStringList ) ) );
mLayers.insert( layer );

if ( mTransactionActive )
layer->dataProvider()->setTransaction( this );

return true;
}

Expand All @@ -141,14 +139,6 @@ bool QgsTransaction::commit( QString& errorMsg )
if ( !mTransactionActive )
return false;

Q_FOREACH ( QgsVectorLayer* l, mLayers )
{
if ( !l || l->isEditable() )
{
return false;
}
}

if ( !commitTransaction( errorMsg ) )
return false;

Expand All @@ -162,14 +152,6 @@ bool QgsTransaction::rollback( QString& errorMsg )
if ( !mTransactionActive )
return false;

Q_FOREACH ( QgsVectorLayer* l, mLayers )
{
if ( l->isEditable() )
{
return false;
}
}

if ( !rollbackTransaction( errorMsg ) )
return false;

Expand All @@ -181,6 +163,15 @@ bool QgsTransaction::rollback( QString& errorMsg )
return true;
}

bool QgsTransaction::supportsTransaction( const QgsVectorLayer* layer )
{
QLibrary* lib = QgsProviderRegistry::instance()->providerLibrary( layer->providerType() );
if ( !lib )
return false;

return lib->resolve( "createTransaction" );
}

void QgsTransaction::onLayersDeleted( const QStringList& layerids )
{
Q_FOREACH ( const QString& layerid, layerids )
Expand Down
13 changes: 9 additions & 4 deletions src/core/qgstransaction.h
Expand Up @@ -60,10 +60,10 @@ class CORE_EXPORT QgsTransaction : public QObject

virtual ~QgsTransaction();

/** Add layer to the transaction. The layer must not be in edit mode. The transaction must not be active. */
/** Add layer to the transaction. The layer must not be in edit mode.*/
bool addLayer( const QString& layerId );

/** Add layer to the transaction. The layer must not be in edit mode. The transaction must not be active. */
/** Add layer to the transaction. The layer must not be in edit mode.*/
bool addLayer( QgsVectorLayer* layer );

/** Begin transaction
Expand All @@ -76,15 +76,20 @@ class CORE_EXPORT QgsTransaction : public QObject
* Some providers might not honour the statement timeout. */
bool begin( QString& errorMsg, int statementTimeout = 20 );

/** Commit transaction. All layers need to be in read-only mode. */
/** Commit transaction. */
bool commit( QString& errorMsg );

/** Roll back transaction. All layers need to be in read-only mode. */
/** Roll back transaction. */
bool rollback( QString& errorMsg );

/** Executes sql */
virtual bool executeSql( const QString& sql, QString& error ) = 0;

/**
* Checks if a the provider of a give layer supports transactions.
*/
static bool supportsTransaction( const QgsVectorLayer* layer );

signals:
/**
* Emitted after a rollback
Expand Down

0 comments on commit e1adc63

Please sign in to comment.