Editing buffer passthrough

This commit is contained in:
Alessandro Pasotti 2021-02-12 19:00:57 +01:00
parent 2596a720df
commit 8cb15c3260
9 changed files with 231 additions and 57 deletions

View File

@ -208,6 +208,7 @@ Returns ``True`` if the specified feature ID has been deleted but not committed.
%End %End
protected slots: protected slots:
void undoIndexChanged( int index ); void undoIndexChanged( int index );
@ -269,8 +270,6 @@ Emitted after committing an attribute rename
Constructor for QgsVectorLayerEditBuffer Constructor for QgsVectorLayerEditBuffer
%End %End
void updateFields( QgsFields &fields );
void updateFeatureGeometry( QgsFeature &f ); void updateFeatureGeometry( QgsFeature &f );
%Docstring %Docstring
Update feature with uncommitted geometry updates Update feature with uncommitted geometry updates

View File

@ -218,7 +218,10 @@ QString QgsTransaction::createSavepoint( QString &error SIP_OUT )
return QString(); return QString();
if ( !mLastSavePointIsDirty && !mSavepoints.isEmpty() ) if ( !mLastSavePointIsDirty && !mSavepoints.isEmpty() )
{
qDebug() << "Returning TOP >>>>> " << mSavepoints.top();
return mSavepoints.top(); return mSavepoints.top();
}
const QString name( QStringLiteral( "qgis" ) + ( QUuid::createUuid().toString().mid( 1, 24 ).replace( '-', QString() ) ) ); const QString name( QStringLiteral( "qgis" ) + ( QUuid::createUuid().toString().mid( 1, 24 ).replace( '-', QString() ) ) );

View File

@ -874,7 +874,7 @@ QgsRectangle QgsVectorLayer::extent() const
} }
if ( !mEditBuffer || if ( !mEditBuffer ||
( mEditBuffer->mDeletedFeatureIds.isEmpty() && mEditBuffer->mChangedGeometries.isEmpty() ) || ( !mDataProvider->transaction() && ( mEditBuffer->deletedFeatureIds().isEmpty() && mEditBuffer->changedGeometries().isEmpty() ) ) ||
QgsDataSourceUri( mDataProvider->dataSourceUri() ).useEstimatedMetadata() ) QgsDataSourceUri( mDataProvider->dataSourceUri() ).useEstimatedMetadata() )
{ {
mDataProvider->updateExtents(); mDataProvider->updateExtents();
@ -887,9 +887,10 @@ QgsRectangle QgsVectorLayer::extent() const
rect.combineExtentWith( r ); rect.combineExtentWith( r );
} }
if ( mEditBuffer ) if ( mEditBuffer && !mDataProvider->transaction() )
{ {
for ( QgsFeatureMap::const_iterator it = mEditBuffer->mAddedFeatures.constBegin(); it != mEditBuffer->mAddedFeatures.constEnd(); ++it ) const auto addedFeatures = mEditBuffer->addedFeatures();
for ( QgsFeatureMap::const_iterator it = addedFeatures.constBegin(); it != addedFeatures.constEnd(); ++it )
{ {
if ( it->hasGeometry() ) if ( it->hasGeometry() )
{ {
@ -3363,13 +3364,13 @@ long QgsVectorLayer::featureCount() const
if ( ! mDataProvider ) if ( ! mDataProvider )
return -1; return -1;
return mDataProvider->featureCount() + return mDataProvider->featureCount() +
( mEditBuffer ? mEditBuffer->mAddedFeatures.size() - mEditBuffer->mDeletedFeatureIds.size() : 0 ); ( mEditBuffer && ! mDataProvider->transaction() ? mEditBuffer->addedFeatures().size() - mEditBuffer->deletedFeatureIds().size() : 0 );
} }
QgsFeatureSource::FeatureAvailability QgsVectorLayer::hasFeatures() const QgsFeatureSource::FeatureAvailability QgsVectorLayer::hasFeatures() const
{ {
const QgsFeatureIds deletedFeatures( mEditBuffer ? mEditBuffer->deletedFeatureIds() : QgsFeatureIds() ); const QgsFeatureIds deletedFeatures( mEditBuffer && ! mDataProvider->transaction() ? mEditBuffer->deletedFeatureIds() : QgsFeatureIds() );
const QgsFeatureMap addedFeatures( mEditBuffer ? mEditBuffer->addedFeatures() : QgsFeatureMap() ); const QgsFeatureMap addedFeatures( mEditBuffer && ! mDataProvider->transaction() ? mEditBuffer->addedFeatures() : QgsFeatureMap() );
if ( mEditBuffer && !deletedFeatures.empty() ) if ( mEditBuffer && !deletedFeatures.empty() )
{ {
@ -3453,9 +3454,9 @@ bool QgsVectorLayer::rollBack( bool deleteBuffer )
return false; return false;
} }
bool rollbackExtent = !mEditBuffer->mDeletedFeatureIds.isEmpty() || bool rollbackExtent = !mDataProvider->transaction() && ( !mEditBuffer->deletedFeatureIds().isEmpty() ||
!mEditBuffer->mAddedFeatures.isEmpty() || !mEditBuffer->addedFeatures().isEmpty() ||
!mEditBuffer->mChangedGeometries.isEmpty(); !mEditBuffer->changedGeometries().isEmpty() );
emit beforeRollBack(); emit beforeRollBack();
@ -4040,7 +4041,7 @@ QSet<QVariant> QgsVectorLayer::uniqueValues( int index, int limit ) const
{ {
uniqueValues = mDataProvider->uniqueValues( index, limit ); uniqueValues = mDataProvider->uniqueValues( index, limit );
if ( mEditBuffer ) if ( mEditBuffer && ! mDataProvider->transaction() )
{ {
QSet<QString> vals; QSet<QString> vals;
const auto constUniqueValues = uniqueValues; const auto constUniqueValues = uniqueValues;
@ -4088,10 +4089,11 @@ QSet<QVariant> QgsVectorLayer::uniqueValues( int index, int limit ) const
case QgsFields::OriginEdit: case QgsFields::OriginEdit:
// the layer is editable, but in certain cases it can still be avoided going through all features // the layer is editable, but in certain cases it can still be avoided going through all features
if ( mEditBuffer->mDeletedFeatureIds.isEmpty() && if ( mDataProvider->transaction() || (
mEditBuffer->mAddedFeatures.isEmpty() && mEditBuffer->deletedFeatureIds().isEmpty() &&
!mEditBuffer->mDeletedAttributeIds.contains( index ) && mEditBuffer->addedFeatures().isEmpty() &&
mEditBuffer->mChangedAttributeValues.isEmpty() ) !mEditBuffer->deletedAttributeIds().contains( index ) &&
mEditBuffer->changedAttributeValues().isEmpty() ) )
{ {
uniqueValues = mDataProvider->uniqueValues( index, limit ); uniqueValues = mDataProvider->uniqueValues( index, limit );
return uniqueValues; return uniqueValues;
@ -4147,7 +4149,7 @@ QStringList QgsVectorLayer::uniqueStringsMatching( int index, const QString &sub
{ {
results = mDataProvider->uniqueStringsMatching( index, substring, limit, feedback ); results = mDataProvider->uniqueStringsMatching( index, substring, limit, feedback );
if ( mEditBuffer ) if ( mEditBuffer && ! mDataProvider->transaction() )
{ {
QgsFeatureMap added = mEditBuffer->addedFeatures(); QgsFeatureMap added = mEditBuffer->addedFeatures();
QMapIterator< QgsFeatureId, QgsFeature > addedIt( added ); QMapIterator< QgsFeatureId, QgsFeature > addedIt( added );
@ -4186,10 +4188,10 @@ QStringList QgsVectorLayer::uniqueStringsMatching( int index, const QString &sub
case QgsFields::OriginEdit: case QgsFields::OriginEdit:
// the layer is editable, but in certain cases it can still be avoided going through all features // the layer is editable, but in certain cases it can still be avoided going through all features
if ( mEditBuffer->mDeletedFeatureIds.isEmpty() && if ( mDataProvider->transaction() || ( mEditBuffer->deletedFeatureIds().isEmpty() &&
mEditBuffer->mAddedFeatures.isEmpty() && mEditBuffer->addedFeatures().isEmpty() &&
!mEditBuffer->mDeletedAttributeIds.contains( index ) && !mEditBuffer->deletedAttributeIds().contains( index ) &&
mEditBuffer->mChangedAttributeValues.isEmpty() ) mEditBuffer->changedAttributeValues().isEmpty() ) )
{ {
return mDataProvider->uniqueStringsMatching( index, substring, limit, feedback ); return mDataProvider->uniqueStringsMatching( index, substring, limit, feedback );
} }
@ -4257,7 +4259,7 @@ QVariant QgsVectorLayer::minimumOrMaximumValue( int index, bool minimum ) const
case QgsFields::OriginProvider: //a provider field case QgsFields::OriginProvider: //a provider field
{ {
QVariant val = minimum ? mDataProvider->minimumValue( index ) : mDataProvider->maximumValue( index ); QVariant val = minimum ? mDataProvider->minimumValue( index ) : mDataProvider->maximumValue( index );
if ( mEditBuffer ) if ( mEditBuffer && ! mDataProvider->transaction() )
{ {
QgsFeatureMap added = mEditBuffer->addedFeatures(); QgsFeatureMap added = mEditBuffer->addedFeatures();
QMapIterator< QgsFeatureId, QgsFeature > addedIt( added ); QMapIterator< QgsFeatureId, QgsFeature > addedIt( added );
@ -4290,10 +4292,10 @@ QVariant QgsVectorLayer::minimumOrMaximumValue( int index, bool minimum ) const
case QgsFields::OriginEdit: case QgsFields::OriginEdit:
{ {
// the layer is editable, but in certain cases it can still be avoided going through all features // the layer is editable, but in certain cases it can still be avoided going through all features
if ( mEditBuffer->mDeletedFeatureIds.isEmpty() && if ( mDataProvider->transaction() || ( mEditBuffer->deletedFeatureIds().isEmpty() &&
mEditBuffer->mAddedFeatures.isEmpty() && mEditBuffer->addedFeatures().isEmpty() &&
!mEditBuffer->mDeletedAttributeIds.contains( index ) && !mEditBuffer->deletedAttributeIds().contains( index ) &&
mEditBuffer->mChangedAttributeValues.isEmpty() ) mEditBuffer->changedAttributeValues().isEmpty() ) )
{ {
return minimum ? mDataProvider->minimumValue( index ) : mDataProvider->maximumValue( index ); return minimum ? mDataProvider->minimumValue( index ) : mDataProvider->maximumValue( index );
} }

View File

@ -184,6 +184,13 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject
*/ */
bool isFeatureDeleted( QgsFeatureId id ) const { return mDeletedFeatureIds.contains( id ); } bool isFeatureDeleted( QgsFeatureId id ) const { return mDeletedFeatureIds.contains( id ); }
/**
* Updates \a fields
* \note Not available in Python bindings
* \since QGIS 3.18
*/
void updateFields( QgsFields &fields ) SIP_SKIP;
//QString dumpEditBuffer(); //QString dumpEditBuffer();
protected slots: protected slots:
@ -236,8 +243,6 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject
//! Constructor for QgsVectorLayerEditBuffer //! Constructor for QgsVectorLayerEditBuffer
QgsVectorLayerEditBuffer() = default; QgsVectorLayerEditBuffer() = default;
void updateFields( QgsFields &fields );
//! Update feature with uncommitted geometry updates //! Update feature with uncommitted geometry updates
void updateFeatureGeometry( QgsFeature &f ); void updateFeatureGeometry( QgsFeature &f );
@ -257,7 +262,6 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject
protected: protected:
QgsVectorLayer *L = nullptr; QgsVectorLayer *L = nullptr;
friend class QgsVectorLayer;
friend class QgsVectorLayerUndoCommand; friend class QgsVectorLayerUndoCommand;
friend class QgsVectorLayerUndoCommandAddFeature; friend class QgsVectorLayerUndoCommandAddFeature;

View File

@ -32,7 +32,7 @@ bool QgsVectorLayerEditPassthrough::isModified() const
bool QgsVectorLayerEditPassthrough::modify( QgsVectorLayerUndoPassthroughCommand *cmd ) bool QgsVectorLayerEditPassthrough::modify( QgsVectorLayerUndoPassthroughCommand *cmd )
{ {
L->undoStack()->push( cmd ); // push takes owneship -> no need for cmd to be a smart ptr L->undoStack()->push( cmd ); // push takes ownership -> no need for cmd to be a smart ptr
if ( cmd->hasError() ) if ( cmd->hasError() )
return false; return false;
@ -109,6 +109,11 @@ bool QgsVectorLayerEditPassthrough::renameAttribute( int attr, const QString &ne
bool QgsVectorLayerEditPassthrough::commitChanges( QStringList & /*commitErrors*/ ) bool QgsVectorLayerEditPassthrough::commitChanges( QStringList & /*commitErrors*/ )
{ {
mModified = false; mModified = false;
for ( int i = 0; i < L->undoStack()->count(); ++i )
{
const QgsVectorLayerUndoPassthroughCommand *cmdPrt = static_cast<const QgsVectorLayerUndoPassthroughCommand *>( L->undoStack()->command( i ) );
qDebug() << cmdPrt->text();
}
return true; return true;
} }

View File

@ -71,12 +71,16 @@ QgsVectorLayerFeatureSource::QgsVectorLayerFeatureSource( const QgsVectorLayer *
else else
{ {
#endif #endif
mAddedFeatures = QgsFeatureMap( layer->editBuffer()->addedFeatures() ); // If we are inside a transaction the iterator "sees" the current status
mChangedGeometries = QgsGeometryMap( layer->editBuffer()->changedGeometries() ); if ( layer->dataProvider() && ! layer->dataProvider()->transaction() )
mDeletedFeatureIds = QgsFeatureIds( layer->editBuffer()->deletedFeatureIds() ); {
mChangedAttributeValues = QgsChangedAttributesMap( layer->editBuffer()->changedAttributeValues() ); mAddedFeatures = QgsFeatureMap( layer->editBuffer()->addedFeatures() );
mAddedAttributes = QList<QgsField>( layer->editBuffer()->addedAttributes() ); mChangedGeometries = QgsGeometryMap( layer->editBuffer()->changedGeometries() );
mDeletedAttributeIds = QgsAttributeList( layer->editBuffer()->deletedAttributeIds() ); mDeletedFeatureIds = QgsFeatureIds( layer->editBuffer()->deletedFeatureIds() );
mChangedAttributeValues = QgsChangedAttributesMap( layer->editBuffer()->changedAttributeValues() );
mAddedAttributes = QList<QgsField>( layer->editBuffer()->addedAttributes() );
mDeletedAttributeIds = QgsAttributeList( layer->editBuffer()->deletedAttributeIds() );
}
#if 0 #if 0
} }
#endif #endif

View File

@ -98,6 +98,10 @@ bool QgsVectorLayerUndoPassthroughCommand::rollBackToSavePoint()
{ {
setError(); setError();
} }
else
{
mBuffer->L->dataProvider()->transaction()->dirtyLastSavePoint();
}
} }
return !hasError(); return !hasError();
} }
@ -115,6 +119,7 @@ QgsVectorLayerUndoPassthroughCommandAddFeatures::QgsVectorLayerUndoPassthroughCo
mInitialFeatures.last().setId( sAddedIdLowWaterMark ); mInitialFeatures.last().setId( sAddedIdLowWaterMark );
} }
mFeatures = mInitialFeatures; mFeatures = mInitialFeatures;
} }
void QgsVectorLayerUndoPassthroughCommandAddFeatures::undo() void QgsVectorLayerUndoPassthroughCommandAddFeatures::undo()
@ -123,6 +128,7 @@ void QgsVectorLayerUndoPassthroughCommandAddFeatures::undo()
{ {
for ( const QgsFeature &f : qgis::as_const( mFeatures ) ) for ( const QgsFeature &f : qgis::as_const( mFeatures ) )
{ {
mBuffer->mAddedFeatures.remove( f.id() );
emit mBuffer->featureDeleted( f.id() ); emit mBuffer->featureDeleted( f.id() );
} }
mFeatures = mInitialFeatures; mFeatures = mInitialFeatures;
@ -132,10 +138,12 @@ void QgsVectorLayerUndoPassthroughCommandAddFeatures::undo()
void QgsVectorLayerUndoPassthroughCommandAddFeatures::redo() void QgsVectorLayerUndoPassthroughCommandAddFeatures::redo()
{ {
mFeatures = mInitialFeatures; mFeatures = mInitialFeatures;
if ( setSavePoint() && mBuffer->L->dataProvider()->addFeatures( mFeatures ) ) mBuffer->L->dataProvider()->clearErrors();
if ( setSavePoint() && mBuffer->L->dataProvider()->addFeatures( mFeatures ) && ! mBuffer->L->dataProvider()->hasErrors() )
{ {
for ( const QgsFeature &f : qgis::as_const( mFeatures ) ) for ( const QgsFeature &f : qgis::as_const( mFeatures ) )
{ {
mBuffer->mAddedFeatures.insert( f.id(), f );
emit mBuffer->featureAdded( f.id() ); emit mBuffer->featureAdded( f.id() );
} }
} }
@ -164,7 +172,8 @@ void QgsVectorLayerUndoPassthroughCommandDeleteFeatures::undo()
void QgsVectorLayerUndoPassthroughCommandDeleteFeatures::redo() void QgsVectorLayerUndoPassthroughCommandDeleteFeatures::redo()
{ {
if ( setSavePoint() && mBuffer->L->dataProvider()->deleteFeatures( mFids ) ) mBuffer->L->dataProvider()->clearErrors();
if ( setSavePoint() && mBuffer->L->dataProvider()->deleteFeatures( mFids ) && ! mBuffer->L->dataProvider()->hasErrors() )
{ {
for ( const QgsFeatureId &id : mFids ) for ( const QgsFeatureId &id : mFids )
{ {
@ -187,9 +196,18 @@ QgsVectorLayerUndoPassthroughCommandChangeGeometry::QgsVectorLayerUndoPassthroug
void QgsVectorLayerUndoPassthroughCommandChangeGeometry::undo() void QgsVectorLayerUndoPassthroughCommandChangeGeometry::undo()
{ {
if ( rollBackToSavePoint() ) if ( rollBackToSavePoint() )
{ {
emit mBuffer->geometryChanged( mFid, mOldGeom ); if ( mBuffer->mAddedFeatures.contains( mFid ) )
{
mBuffer->mAddedFeatures[ mFid ].setGeometry( mOldGeom );
}
else
{
// TODO: unless first change?
mBuffer->mChangedGeometries[mFid] = mOldGeom;
}
} }
} }
@ -197,8 +215,17 @@ void QgsVectorLayerUndoPassthroughCommandChangeGeometry::redo()
{ {
QgsGeometryMap geomMap; QgsGeometryMap geomMap;
geomMap.insert( mFid, mNewGeom ); geomMap.insert( mFid, mNewGeom );
if ( setSavePoint() && mBuffer->L->dataProvider()->changeGeometryValues( geomMap ) ) mBuffer->L->dataProvider()->clearErrors();
if ( setSavePoint() && mBuffer->L->dataProvider()->changeGeometryValues( geomMap ) && ! mBuffer->L->dataProvider()->hasErrors() )
{ {
if ( mBuffer->mAddedFeatures.contains( mFid ) )
{
mBuffer->mAddedFeatures[ mFid ].setGeometry( mNewGeom );
}
else
{
mBuffer->mChangedGeometries[ mFid ] = mNewGeom;
}
emit mBuffer->geometryChanged( mFid, mNewGeom ); emit mBuffer->geometryChanged( mFid, mNewGeom );
} }
else else
@ -210,29 +237,98 @@ void QgsVectorLayerUndoPassthroughCommandChangeGeometry::redo()
QgsVectorLayerUndoPassthroughCommandChangeAttribute::QgsVectorLayerUndoPassthroughCommandChangeAttribute( QgsVectorLayerEditBuffer *buffer, QgsFeatureId fid, int field, const QVariant &newValue ) QgsVectorLayerUndoPassthroughCommandChangeAttribute::QgsVectorLayerUndoPassthroughCommandChangeAttribute( QgsVectorLayerEditBuffer *buffer, QgsFeatureId fid, int field, const QVariant &newValue )
: QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "change attribute value" ) ) : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "change attribute value" ) )
, mFid( fid ) , mFid( fid )
, mField( field ) , mFieldIndex( field )
, mNewValue( newValue ) , mNewValue( newValue )
, mOldValue( mBuffer->L->getFeature( mFid ).attribute( field ) ) , mOldValue( mBuffer->L->getFeature( mFid ).attribute( field ) )
, mFirstChange( true )
{ {
if ( mBuffer->mAddedFeatures.contains( mFid ) )
{
// work with added feature
QgsFeatureMap::const_iterator it = mBuffer->mAddedFeatures.constFind( mFid );
Q_ASSERT( it != mBuffer->mAddedFeatures.constEnd() );
if ( it.value().attribute( mFieldIndex ).isValid() )
{
mOldValue = it.value().attribute( mFieldIndex );
mFirstChange = false;
}
}
else if ( mBuffer->mChangedAttributeValues.contains( mFid ) && mBuffer->mChangedAttributeValues[mFid].contains( mFieldIndex ) )
{
mOldValue = mBuffer->mChangedAttributeValues[mFid][mFieldIndex];
mFirstChange = false;
}
} }
void QgsVectorLayerUndoPassthroughCommandChangeAttribute::undo() void QgsVectorLayerUndoPassthroughCommandChangeAttribute::undo()
{ {
if ( rollBackToSavePoint() ) if ( rollBackToSavePoint() )
{ {
emit mBuffer->attributeValueChanged( mFid, mField, mOldValue ); QVariant original = mOldValue;
if ( mBuffer->mAddedFeatures.contains( mFid ) )
{
// added feature
QgsFeatureMap::iterator it = mBuffer->mAddedFeatures.find( mFid );
Q_ASSERT( it != mBuffer->mAddedFeatures.end() );
it.value().setAttribute( mFieldIndex, mOldValue );
}
else if ( mFirstChange )
{
// existing feature
mBuffer->mChangedAttributeValues[mFid].remove( mFieldIndex );
if ( mBuffer->mChangedAttributeValues[mFid].isEmpty() )
mBuffer->mChangedAttributeValues.remove( mFid );
if ( !mOldValue.isValid() )
{
// get old value from provider
QgsFeature tmp;
QgsFeatureRequest request;
request.setFilterFid( mFid );
request.setFlags( QgsFeatureRequest::NoGeometry );
request.setSubsetOfAttributes( QgsAttributeList() << mFieldIndex );
std::unique_ptr<QgsVectorLayer> layerClone( layer()->clone() );
QgsFeatureIterator fi = layerClone->getFeatures( request );
if ( fi.nextFeature( tmp ) )
original = tmp.attribute( mFieldIndex );
}
}
else
{
mBuffer->mChangedAttributeValues[mFid][mFieldIndex] = mOldValue;
}
emit mBuffer->attributeValueChanged( mFid, mFieldIndex, original );
} }
} }
void QgsVectorLayerUndoPassthroughCommandChangeAttribute::redo() void QgsVectorLayerUndoPassthroughCommandChangeAttribute::redo()
{ {
QgsAttributeMap map; QgsAttributeMap map;
map.insert( mField, mNewValue ); map.insert( mFieldIndex, mNewValue );
QgsChangedAttributesMap attribMap; QgsChangedAttributesMap attribMap;
attribMap.insert( mFid, map ); attribMap.insert( mFid, map );
if ( setSavePoint() && mBuffer->L->dataProvider()->changeAttributeValues( attribMap ) ) mBuffer->L->dataProvider()->clearErrors();
if ( setSavePoint() && mBuffer->L->dataProvider()->changeAttributeValues( attribMap ) && ! mBuffer->L->dataProvider()->hasErrors() )
{ {
emit mBuffer->attributeValueChanged( mFid, mField, mNewValue ); // Update existing feature
QgsFeatureMap::iterator it = mBuffer->mAddedFeatures.find( mFid );
if ( it != mBuffer->mAddedFeatures.end() )
{
it.value().setAttribute( mFieldIndex, mNewValue );
}
else
{
// changed attribute of existing feature
if ( !mBuffer->mChangedAttributeValues.contains( mFid ) )
{
mBuffer->mChangedAttributeValues.insert( mFid, QgsAttributeMap() );
}
mBuffer->mChangedAttributeValues[mFid].insert( mFieldIndex, mNewValue );
}
emit mBuffer->attributeValueChanged( mFid, mFieldIndex, mNewValue );
} }
else else
{ {
@ -251,7 +347,8 @@ void QgsVectorLayerUndoPassthroughCommandAddAttribute::undo()
// note that the deleteAttribute here is only necessary to inform the provider that // note that the deleteAttribute here is only necessary to inform the provider that
// an attribute is removed after the rollBackToSavePoint // an attribute is removed after the rollBackToSavePoint
const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() ); const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() );
if ( mBuffer->L->dataProvider()->deleteAttributes( QgsAttributeIds() << attr ) && rollBackToSavePoint() ) mBuffer->L->dataProvider()->clearErrors();
if ( mBuffer->L->dataProvider()->deleteAttributes( QgsAttributeIds() << attr ) && rollBackToSavePoint() && ! mBuffer->L->dataProvider()->hasErrors() )
{ {
mBuffer->updateLayerFields(); mBuffer->updateLayerFields();
emit mBuffer->attributeDeleted( attr ); emit mBuffer->attributeDeleted( attr );
@ -264,7 +361,8 @@ void QgsVectorLayerUndoPassthroughCommandAddAttribute::undo()
void QgsVectorLayerUndoPassthroughCommandAddAttribute::redo() void QgsVectorLayerUndoPassthroughCommandAddAttribute::redo()
{ {
if ( setSavePoint() && mBuffer->L->dataProvider()->addAttributes( QList<QgsField>() << mField ) ) mBuffer->L->dataProvider()->clearErrors();
if ( setSavePoint() && mBuffer->L->dataProvider()->addAttributes( QList<QgsField>() << mField ) && ! mBuffer->L->dataProvider()->hasErrors() )
{ {
mBuffer->updateLayerFields(); mBuffer->updateLayerFields();
const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() ); const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() );
@ -286,7 +384,8 @@ void QgsVectorLayerUndoPassthroughCommandDeleteAttribute::undo()
{ {
// note that the addAttributes here is only necessary to inform the provider that // note that the addAttributes here is only necessary to inform the provider that
// an attribute is added back after the rollBackToSavePoint // an attribute is added back after the rollBackToSavePoint
if ( mBuffer->L->dataProvider()->addAttributes( QList<QgsField>() << mField ) && rollBackToSavePoint() ) mBuffer->L->dataProvider()->clearErrors();
if ( mBuffer->L->dataProvider()->addAttributes( QList<QgsField>() << mField ) && rollBackToSavePoint() && ! mBuffer->L->dataProvider()->hasErrors() )
{ {
mBuffer->updateLayerFields(); mBuffer->updateLayerFields();
const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() ); const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() );
@ -301,7 +400,8 @@ void QgsVectorLayerUndoPassthroughCommandDeleteAttribute::undo()
void QgsVectorLayerUndoPassthroughCommandDeleteAttribute::redo() void QgsVectorLayerUndoPassthroughCommandDeleteAttribute::redo()
{ {
const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() ); const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() );
if ( setSavePoint() && mBuffer->L->dataProvider()->deleteAttributes( QgsAttributeIds() << attr ) ) mBuffer->L->dataProvider()->clearErrors();
if ( setSavePoint() && mBuffer->L->dataProvider()->deleteAttributes( QgsAttributeIds() << attr ) && ! mBuffer->L->dataProvider()->hasErrors() )
{ {
mBuffer->updateLayerFields(); mBuffer->updateLayerFields();
emit mBuffer->attributeDeleted( attr ); emit mBuffer->attributeDeleted( attr );
@ -326,7 +426,8 @@ void QgsVectorLayerUndoPassthroughCommandRenameAttribute::undo()
// an attribute is renamed after the rollBackToSavePoint // an attribute is renamed after the rollBackToSavePoint
QgsFieldNameMap map; QgsFieldNameMap map;
map[ mAttr ] = mOldName; map[ mAttr ] = mOldName;
if ( mBuffer->L->dataProvider()->renameAttributes( map ) && rollBackToSavePoint() ) mBuffer->L->dataProvider()->clearErrors();
if ( mBuffer->L->dataProvider()->renameAttributes( map ) && rollBackToSavePoint() && ! mBuffer->L->dataProvider()->hasErrors() )
{ {
mBuffer->updateLayerFields(); mBuffer->updateLayerFields();
emit mBuffer->attributeRenamed( mAttr, mOldName ); emit mBuffer->attributeRenamed( mAttr, mOldName );
@ -341,7 +442,8 @@ void QgsVectorLayerUndoPassthroughCommandRenameAttribute::redo()
{ {
QgsFieldNameMap map; QgsFieldNameMap map;
map[ mAttr ] = mNewName; map[ mAttr ] = mNewName;
if ( setSavePoint() && mBuffer->L->dataProvider()->renameAttributes( map ) ) mBuffer->L->dataProvider()->clearErrors();
if ( setSavePoint() && mBuffer->L->dataProvider()->renameAttributes( map ) && ! mBuffer->L->dataProvider()->hasErrors() )
{ {
mBuffer->updateLayerFields(); mBuffer->updateLayerFields();
emit mBuffer->attributeRenamed( mAttr, mNewName ); emit mBuffer->attributeRenamed( mAttr, mNewName );
@ -427,7 +529,8 @@ void QgsVectorLayerUndoPassthroughCommandChangeAttributes::redo()
{ {
QgsChangedAttributesMap attribMap; QgsChangedAttributesMap attribMap;
attribMap.insert( mFid, mNewValues ); attribMap.insert( mFid, mNewValues );
if ( setSavePoint() && mBuffer->L->dataProvider()->changeAttributeValues( attribMap ) ) mBuffer->L->dataProvider()->clearErrors();
if ( setSavePoint() && mBuffer->L->dataProvider()->changeAttributeValues( attribMap ) && ! mBuffer->L->dataProvider()->hasErrors() )
{ {
for ( auto it = mNewValues.constBegin(); it != mNewValues.constEnd(); ++it ) for ( auto it = mNewValues.constBegin(); it != mNewValues.constEnd(); ++it )
{ {

View File

@ -200,9 +200,10 @@ class CORE_EXPORT QgsVectorLayerUndoPassthroughCommandChangeAttribute: public Qg
private: private:
QgsFeatureId mFid; QgsFeatureId mFid;
const int mField; const int mFieldIndex;
const QVariant mNewValue; const QVariant mNewValue;
const QVariant mOldValue; QVariant mOldValue;
bool mFirstChange;
}; };
/** /**

View File

@ -10,15 +10,18 @@ __author__ = 'Nyall Dawson'
__date__ = '15/07/2016' __date__ = '15/07/2016'
__copyright__ = 'Copyright 2016, The QGIS Project' __copyright__ = 'Copyright 2016, The QGIS Project'
import os
import qgis # NOQA import qgis # NOQA
from qgis.PyQt.QtCore import QVariant, QTemporaryDir
from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsVectorLayer, from qgis.core import (QgsVectorLayer,
QgsFeature, QgsFeature,
QgsProject,
QgsGeometry, QgsGeometry,
QgsPointXY, QgsPointXY,
QgsField) QgsField,
QgsVectorFileWriter,
QgsCoordinateTransformContext)
from qgis.testing import start_app, unittest from qgis.testing import start_app, unittest
start_app() start_app()
@ -391,6 +394,56 @@ class TestQgsVectorLayerEditBuffer(unittest.TestCase):
self.assertEqual(layer.editBuffer().addedAttributes()[0].name(), 'new1') self.assertEqual(layer.editBuffer().addedAttributes()[0].name(), 'new1')
self.assertEqual(layer.editBuffer().addedAttributes()[1].name(), 'new2') self.assertEqual(layer.editBuffer().addedAttributes()[1].name(), 'new2')
def testTransactionGroup(self):
"""Test that the buffer works as expected when in transaction"""
ml = QgsVectorLayer('Point?field=int:integer&crs=epsg:4326', 'test', 'memory')
self.assertTrue(ml.isValid())
d = QTemporaryDir()
options = QgsVectorFileWriter.SaveVectorOptions()
options.driverName = 'GPKG'
options.layerName = 'layer_a'
err, _ = QgsVectorFileWriter.writeAsVectorFormatV2(ml, os.path.join(d.path(), 'transaction_test.gpkg'), QgsCoordinateTransformContext(), options)
self.assertEqual(err, QgsVectorFileWriter.NoError)
self.assertTrue(os.path.isfile(os.path.join(d.path(), 'transaction_test.gpkg')))
options.layerName = 'layer_b'
options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
err, _ = QgsVectorFileWriter.writeAsVectorFormatV2(ml, os.path.join(d.path(), 'transaction_test.gpkg'), QgsCoordinateTransformContext(), options)
def _test(autoTransaction):
layer_a = QgsVectorLayer(os.path.join(d.path(), 'transaction_test.gpkg|layername=layer_a'))
layer_b = QgsVectorLayer(os.path.join(d.path(), 'transaction_test.gpkg|layername=layer_b'))
self.assertTrue(layer_a.isValid())
self.assertTrue(layer_b.isValid())
project = QgsProject()
project.setAutoTransaction(autoTransaction)
project.addMapLayers([layer_a, layer_b])
self.assertFalse(layer_b.isEditable())
self.assertTrue(layer_a.startEditing())
if not autoTransaction:
self.assertTrue(layer_b.startEditing())
self.assertTrue(layer_b.isEditable())
f = QgsFeature(layer_a.fields())
f.setGeometry(QgsGeometry.fromWkt('point(7 45)'))
self.assertTrue(layer_a.addFeatures([f]))
buffer = layer_a.editBuffer()
self.assertTrue(len(buffer.addedFeatures()) > 0)
_test(False)
_test(True)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()