Skip to content

Commit

Permalink
Create child feature with geometry from the relation editor (#32528)
Browse files Browse the repository at this point in the history
* [FEATURE][needs-docs] Create geometric child feature from relation editor
  • Loading branch information
troopa81 authored and Hugo Mercier committed Dec 4, 2019
1 parent b5dd082 commit ba925bb
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 8 deletions.
8 changes: 8 additions & 0 deletions python/core/auto_generated/qgsvectorlayerutils.sip.in
Expand Up @@ -287,6 +287,14 @@ The following operations will be performed to convert the input features:
%End



static QString getFeatureDisplayString( const QgsVectorLayer *layer, const QgsFeature &feature );
%Docstring

:return: the ``layer`` ``feature`` display string

.. versionadded:: 3.12
%End
};


Expand Down
14 changes: 14 additions & 0 deletions python/gui/auto_generated/qgsattributeeditorcontext.sip.in
Expand Up @@ -256,6 +256,20 @@ Set ``attributeFormMode`` for the edited form
Returns given ``attributeFormMode`` as string

.. versionadded:: 3.4
%End

void setMainMessageBar( QgsMessageBar *messageBar );
%Docstring
Set current ``messageBar`` as the main message bar

.. versionadded:: 3.12
%End

QgsMessageBar *mainMessageBar();
%Docstring
Returns the main message bar

.. versionadded:: 3.12
%End

};
Expand Down
1 change: 1 addition & 0 deletions src/app/qgsattributetabledialog.cpp
Expand Up @@ -158,6 +158,7 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *layer, QgsAttr

mEditorContext.setVectorLayerTools( QgisApp::instance()->vectorLayerTools() );
mEditorContext.setMapCanvas( QgisApp::instance()->mapCanvas() );
mEditorContext.setMainMessageBar( QgisApp::instance()->messageBar() );
mEditorContext.setCadDockWidget( QgisApp::instance()->cadDockWidget() );

QgsFeatureRequest r;
Expand Down
12 changes: 12 additions & 0 deletions src/core/qgsvectorlayerutils.cpp
Expand Up @@ -1029,3 +1029,15 @@ QHash<QString, QSet<QgsSymbolLayerId>> QgsVectorLayerUtils::symbolLayerMasks( co
layer->renderer()->accept( &visitor );
return visitor.masks;
}

QString QgsVectorLayerUtils::getFeatureDisplayString( const QgsVectorLayer *layer, const QgsFeature &feature )
{
QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );

QgsExpression exp( layer->displayExpression() );
context.setFeature( feature );
exp.prepare( &context );
QString displayString = exp.evaluate( &context ).toString();

return displayString;
}
6 changes: 6 additions & 0 deletions src/core/qgsvectorlayerutils.h
Expand Up @@ -312,6 +312,12 @@ class CORE_EXPORT QgsVectorLayerUtils
* \since QGIS 3.12
*/
static QHash<QString, QSet<QgsSymbolLayerId>> symbolLayerMasks( const QgsVectorLayer * ) SIP_SKIP;

/**
* \return the \a layer \a feature display string
* \since QGIS 3.12
*/
static QString getFeatureDisplayString( const QgsVectorLayer *layer, const QgsFeature &feature );
};


Expand Down
16 changes: 16 additions & 0 deletions src/gui/qgsattributeeditorcontext.h
Expand Up @@ -28,6 +28,7 @@

class QgsMapCanvas;
class QgsAdvancedDigitizingDockWidget;
class QgsMessageBar;

/**
* \ingroup gui
Expand Down Expand Up @@ -79,6 +80,7 @@ class GUI_EXPORT QgsAttributeEditorContext
: mParentContext( &parentContext )
, mVectorLayerTools( parentContext.mVectorLayerTools )
, mMapCanvas( parentContext.mMapCanvas )
, mMainMessageBar( parentContext.mMainMessageBar )
, mCadDockWidget( parentContext.mCadDockWidget )
, mDistanceArea( parentContext.mDistanceArea )
, mFormFeature( parentContext.mFormFeature )
Expand All @@ -91,6 +93,7 @@ class GUI_EXPORT QgsAttributeEditorContext
: mParentContext( &parentContext )
, mVectorLayerTools( parentContext.mVectorLayerTools )
, mMapCanvas( parentContext.mMapCanvas )
, mMainMessageBar( parentContext.mMainMessageBar )
, mCadDockWidget( parentContext.mCadDockWidget )
, mDistanceArea( parentContext.mDistanceArea )
, mRelation( relation )
Expand Down Expand Up @@ -261,11 +264,24 @@ class GUI_EXPORT QgsAttributeEditorContext
return metaEnum.valueToKey( static_cast<int>( mAttributeFormMode ) );
}

/**
* Set current \a messageBar as the main message bar
* \since QGIS 3.12
*/
void setMainMessageBar( QgsMessageBar *messageBar ) { mMainMessageBar = messageBar; }

/**
* Returns the main message bar
* \since QGIS 3.12
*/
QgsMessageBar *mainMessageBar() { return mMainMessageBar; }

private:
const QgsAttributeEditorContext *mParentContext = nullptr;
QgsVectorLayer *mLayer = nullptr;
QgsVectorLayerTools *mVectorLayerTools = nullptr;
QgsMapCanvas *mMapCanvas = nullptr;
QgsMessageBar *mMainMessageBar = nullptr;
QgsAdvancedDigitizingDockWidget *mCadDockWidget = nullptr;
QgsDistanceArea mDistanceArea;
QgsRelation mRelation;
Expand Down
127 changes: 123 additions & 4 deletions src/gui/qgsrelationeditorwidget.cpp
Expand Up @@ -30,6 +30,11 @@
#include "qgslogger.h"
#include "qgsvectorlayerutils.h"
#include "qgsmapcanvas.h"
#include "qgsvectorlayerselectionmanager.h"
#include "qgsmaptooldigitizefeature.h"
#include "qgsexpressioncontextutils.h"
#include "qgsmessagebar.h"
#include "qgsmessagebaritem.h"

#include <QHBoxLayout>
#include <QLabel>
Expand Down Expand Up @@ -108,6 +113,10 @@ QgsRelationEditorWidget::QgsRelationEditorWidget( QWidget *parent )
mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) );
mSaveEditsButton->setEnabled( true );
buttonLayout->addWidget( mSaveEditsButton );
// add feature with geometry
mAddFeatureGeometryButton = new QToolButton( this );
mAddFeatureGeometryButton->setObjectName( QStringLiteral( "mAddFeatureGeometryButton" ) );
buttonLayout->addWidget( mAddFeatureGeometryButton );
// add feature
mAddFeatureButton = new QToolButton( this );
mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewTableRow.svg" ) ) );
Expand Down Expand Up @@ -190,7 +199,8 @@ QgsRelationEditorWidget::QgsRelationEditorWidget( QWidget *parent )
this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::toggleEditing );
connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::saveEdits );
connect( mAddFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeature );
connect( mAddFeatureButton, &QAbstractButton::clicked, this, [this]() { addFeature(); } );
connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeatureGeometry );
connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::duplicateFeature );
connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::deleteSelectedFeatures );
connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::linkFeature );
Expand Down Expand Up @@ -252,6 +262,36 @@ void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeat
mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );

QIcon icon;
QString text;
if ( layer->geometryType() == QgsWkbTypes::PointGeometry )
{
icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePoint.svg" ) );
text = tr( "Add Point child Feature" );
}
else if ( layer->geometryType() == QgsWkbTypes::LineGeometry )
{
icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCaptureLine.svg" ) );
text = tr( "Add Line child Feature" );
}
else if ( layer->geometryType() == QgsWkbTypes::PolygonGeometry )
{
icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePolygon.svg" ) );
text = tr( "Add Polygon Feature" );
}

if ( text.isEmpty() )
{
mAddFeatureGeometryButton->setVisible( false );
}
else
{
mAddFeatureGeometryButton->setIcon( icon );
mAddFeatureGeometryButton->setText( text );
mAddFeatureGeometryButton->setToolTip( text );
}

updateButtons();
}

Expand Down Expand Up @@ -324,6 +364,8 @@ void QgsRelationEditorWidget::setRelations( const QgsRelation &relation, const Q
void QgsRelationEditorWidget::setEditorContext( const QgsAttributeEditorContext &context )
{
mEditorContext = context;
mMapToolDigitize.reset( new QgsMapToolDigitizeFeature( context.mapCanvas(), context.cadDockWidget() ) );
mMapToolDigitize->setButton( mAddFeatureGeometryButton );
}

QgsIFeatureSelectionManager *QgsRelationEditorWidget::featureSelectionManager()
Expand Down Expand Up @@ -362,6 +404,7 @@ void QgsRelationEditorWidget::updateButtons()
}

mAddFeatureButton->setEnabled( editable );
mAddFeatureGeometryButton->setEnabled( editable );
mDuplicateFeatureButton->setEnabled( editable && selectionNotEmpty );
mLinkFeatureButton->setEnabled( linkable );
mDeleteFeatureButton->setEnabled( editable && selectionNotEmpty );
Expand Down Expand Up @@ -389,7 +432,34 @@ void QgsRelationEditorWidget::updateButtons()
mSaveEditsButton->setEnabled( editable );
}

void QgsRelationEditorWidget::addFeature()
void QgsRelationEditorWidget::addFeatureGeometry()
{
QgsVectorLayer *layer = nullptr;
if ( mNmRelation.isValid() )
layer = mNmRelation.referencedLayer();
else
layer = mRelation.referencingLayer();

mMapToolDigitize->setLayer( layer );
setMapTool( mMapToolDigitize );

connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
connect( mEditorContext.mapCanvas(), &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );

if ( mEditorContext.mainMessageBar() )
{
QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( layer, mFeature );

QString title = tr( "Create child feature for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString );
QString msg = tr( "Digitize the geometry for the new feature on layer %1. Press &lt;ESC&gt; to cancel." )
.arg( layer->name() );
mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
mEditorContext.mainMessageBar()->pushItem( mMessageBarItem );
}

}

void QgsRelationEditorWidget::addFeature( const QgsGeometry &geometry )
{
QgsAttributeMap keyAttrs;

Expand All @@ -400,7 +470,7 @@ void QgsRelationEditorWidget::addFeature()
// n:m Relation: first let the user create a new feature on the other table
// and autocreate a new linking feature.
QgsFeature f;
if ( vlTools->addFeature( mNmRelation.referencedLayer(), QgsAttributeMap(), QgsGeometry(), &f ) )
if ( vlTools->addFeature( mNmRelation.referencedLayer(), QgsAttributeMap(), geometry, &f ) )
{
// Fields of the linking table
const QgsFields fields = mRelation.referencingLayer()->fields();
Expand Down Expand Up @@ -439,10 +509,17 @@ void QgsRelationEditorWidget::addFeature()
keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeature.attribute( fieldPair.referencedField() ) );
}

vlTools->addFeature( mDualView->masterModel()->layer(), keyAttrs );
vlTools->addFeature( mDualView->masterModel()->layer(), keyAttrs, geometry );
}
}

void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature )
{
addFeature( feature.geometry() );

unsetMapTool();
}

void QgsRelationEditorWidget::linkFeature()
{
QgsVectorLayer *layer = nullptr;
Expand Down Expand Up @@ -839,3 +916,45 @@ void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFea
connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } );
}
}

void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool )
{
QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();

mapCanvas->setMapTool( mapTool );
mapCanvas->window()->raise();
mapCanvas->activateWindow();
mapCanvas->setFocus();
connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated );
}

void QgsRelationEditorWidget::unsetMapTool()
{
QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();

// this will call mapToolDeactivated
mapCanvas->unsetMapTool( mMapToolDigitize );

disconnect( mapCanvas, &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
}

void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e )
{
if ( e->key() == Qt::Key_Escape )
{
unsetMapTool();
}
}

void QgsRelationEditorWidget::mapToolDeactivated()
{
window()->raise();
window()->activateWindow();

if ( mEditorContext.mainMessageBar() && mMessageBarItem )
{
mEditorContext.mainMessageBar()->popWidget( mMessageBarItem );
}
mMessageBarItem = nullptr;
}
15 changes: 14 additions & 1 deletion src/gui/qgsrelationeditorwidget.h
Expand Up @@ -20,7 +20,9 @@
#include <QToolButton>
#include <QButtonGroup>
#include <QGridLayout>
#include "qobjectuniqueptr.h"

#include "qobjectuniqueptr.h"
#include "qgsattributeeditorcontext.h"
#include "qgscollapsiblegroupbox.h"
#include "qgsdualview.h"
Expand All @@ -31,6 +33,8 @@
class QgsFeature;
class QgsVectorLayer;
class QgsVectorLayerTools;
class QgsMapTool;
class QgsMapToolDigitizeFeature;

#ifdef SIP_RUN
% ModuleHeaderCode
Expand Down Expand Up @@ -176,7 +180,8 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsCollapsibleGroupBox
void setViewMode( int mode ) {setViewMode( static_cast<QgsDualView::ViewMode>( mode ) );}
void updateButtons();

void addFeature();
void addFeature( const QgsGeometry &geometry = QgsGeometry() );
void addFeatureGeometry();
void duplicateFeature();
void linkFeature();
void deleteFeature( QgsFeatureId featureid = QgsFeatureId() );
Expand All @@ -188,12 +193,18 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsCollapsibleGroupBox
void toggleEditing( bool state );
void onCollapsedStateChanged( bool collapsed );
void showContextMenu( QgsActionMenu *menu, QgsFeatureId fid );
void mapToolDeactivated();
void onKeyPressed( QKeyEvent *e );
void onDigitizingCompleted( const QgsFeature &feature );

private:
void updateUi();
void initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request );
void setMapTool( QgsMapTool *mapTool );
void unsetMapTool();

QgsDualView *mDualView = nullptr;
QPointer<QgsMessageBarItem> mMessageBarItem;
QgsDualView::ViewMode mViewMode = QgsDualView::AttributeEditor;
QgsVectorLayerSelectionManager *mFeatureSelectionMgr = nullptr;
QgsAttributeEditorContext mEditorContext;
Expand All @@ -211,7 +222,9 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsCollapsibleGroupBox
QToolButton *mZoomToFeatureButton = nullptr;
QToolButton *mFormViewButton = nullptr;
QToolButton *mTableViewButton = nullptr;
QToolButton *mAddFeatureGeometryButton = nullptr;
QGridLayout *mRelationLayout = nullptr;
QObjectUniquePtr<QgsMapToolDigitizeFeature> mMapToolDigitize;
QButtonGroup *mViewModeButtonGroup = nullptr;

bool mShowLabel = true;
Expand Down

0 comments on commit ba925bb

Please sign in to comment.