17#include "moc_qgsrelationreferencewidget.cpp"
48 for (
int i = 0; i < list.size(); ++i )
60 mTopLayout =
new QVBoxLayout(
this );
61 mTopLayout->setContentsMargins( 0, 0, 0, 0 );
63 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::Fixed );
65 setLayout( mTopLayout );
67 QHBoxLayout *editLayout =
new QHBoxLayout();
68 editLayout->setContentsMargins( 0, 0, 0, 0 );
69 editLayout->setSpacing( 2 );
72 mChooserContainer =
new QWidget;
73 editLayout->addWidget( mChooserContainer );
74 QHBoxLayout *chooserLayout =
new QHBoxLayout;
75 chooserLayout->setContentsMargins( 0, 0, 0, 0 );
76 mFilterLayout =
new QHBoxLayout;
77 mFilterLayout->setContentsMargins( 0, 0, 0, 0 );
78 mFilterContainer =
new QWidget;
79 mFilterContainer->setLayout( mFilterLayout );
80 mChooserContainer->setLayout( chooserLayout );
81 chooserLayout->addWidget( mFilterContainer );
84 mChooserContainer->layout()->addWidget( mComboBox );
87 mOpenFormButton =
new QToolButton();
89 mOpenFormButton->setText( tr(
"Open Related Feature Form" ) );
90 editLayout->addWidget( mOpenFormButton );
92 mAddEntryButton =
new QToolButton();
94 mAddEntryButton->setText( tr(
"Add New Entry" ) );
95 editLayout->addWidget( mAddEntryButton );
98 mHighlightFeatureButton =
new QToolButton(
this );
99 mHighlightFeatureButton->setPopupMode( QToolButton::MenuButtonPopup );
100 mHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionHighlightFeature.svg" ) ), tr(
"Highlight feature" ),
this );
101 mScaleHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionScaleHighlightFeature.svg" ) ), tr(
"Scale and highlight feature" ),
this );
102 mPanHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionPanHighlightFeature.svg" ) ), tr(
"Pan and highlight feature" ),
this );
103 mHighlightFeatureButton->addAction( mHighlightFeatureAction );
104 mHighlightFeatureButton->addAction( mScaleHighlightFeatureAction );
105 mHighlightFeatureButton->addAction( mPanHighlightFeatureAction );
106 mHighlightFeatureButton->setDefaultAction( mHighlightFeatureAction );
107 editLayout->addWidget( mHighlightFeatureButton );
110 mMapIdentificationButton =
new QToolButton(
this );
112 mMapIdentificationButton->setText( tr(
"Select on Map" ) );
113 mMapIdentificationButton->setCheckable(
true );
114 editLayout->addWidget( mMapIdentificationButton );
117 mRemoveFKButton =
new QToolButton(
this );
119 mRemoveFKButton->setText( tr(
"No Selection" ) );
120 editLayout->addWidget( mRemoveFKButton );
123 mTopLayout->addLayout( editLayout );
127 mAttributeEditorLayout =
new QVBoxLayout( mAttributeEditorFrame );
128 mAttributeEditorFrame->setLayout( mAttributeEditorLayout );
129 mAttributeEditorFrame->setSizePolicy( mAttributeEditorFrame->sizePolicy().horizontalPolicy(), QSizePolicy::Expanding );
130 mTopLayout->addWidget( mAttributeEditorFrame );
133 mInvalidLabel =
new QLabel( tr(
"The relation is not valid. Please make sure your relation definitions are OK." ) );
134 mInvalidLabel->setWordWrap(
true );
135 QFont font = mInvalidLabel->font();
136 font.setItalic(
true );
137 mInvalidLabel->setStyleSheet( QStringLiteral(
"QLabel { color: red; } " ) );
138 mInvalidLabel->setFont( font );
139 mTopLayout->addWidget( mInvalidLabel );
142 mMapIdentificationButton->hide();
143 mHighlightFeatureButton->hide();
144 mAttributeEditorFrame->hide();
145 mInvalidLabel->hide();
146 mAddEntryButton->hide();
150 connect( mHighlightFeatureButton, &QToolButton::triggered,
this, &QgsRelationReferenceWidget::highlightActionTriggered );
153 connect( mAddEntryButton, &QAbstractButton::clicked,
this, &QgsRelationReferenceWidget::addEntry );
154 connect( mComboBox, &QComboBox::editTextChanged,
this, &QgsRelationReferenceWidget::updateAddEntryButton );
165 mAllowNull = allowNullValue;
166 mRemoveFKButton->setVisible( allowNullValue && mReadOnlySelector );
174 mInvalidLabel->hide();
183 mReferencedFields << fieldPair.referencedField();
193 mAttributeEditorFrame->setObjectName( QStringLiteral(
"referencing/" ) +
relation.
name() );
198 mAttributeEditorFrame->setTitle( mReferencedLayer->
name() );
200 mAttributeEditorLayout->addWidget( mReferencedAttributeForm );
205 updateAddEntryButton();
209 mInvalidLabel->show();
212 if ( mShown && isVisible() )
225 mFilterContainer->setEnabled( editable );
226 mComboBox->setEnabled( editable && !mReadOnlySelector );
227 mComboBox->setEditable(
true );
228 mMapIdentificationButton->setEnabled( editable );
229 mRemoveFKButton->setEnabled( editable );
230 mIsEditable = editable;
240 if ( values.isEmpty() )
250 if ( !mReferencedLayer )
255 if ( mEmbedForm || mChainFilters )
263 const int count = std::min( mFilterComboBoxes.size(), mFilterFields.size() );
264 for (
int i = 0; i < count; i++ )
266 QVariant v = mFeature.
attribute( mFilterFields[i] );
268 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
272 mRemoveFKButton->setEnabled( mIsEditable );
273 highlightFeature( mFeature );
274 updateAttributeEditorFrame( mFeature );
282 if ( mChainFilters && !mFilterComboBoxes.isEmpty() )
284 QComboBox *cb = mFilterComboBoxes.first();
285 cb->setCurrentIndex( 0 );
286 disableChainedComboBoxes( cb );
290 mRemoveFKButton->setEnabled(
false );
299 if ( mReferencedLayer )
309 mRemoveFKButton->setEnabled(
false );
316 if ( fkeys.isEmpty() )
319 return fkeys.at( 0 );
329 mEditorContext = context;
331 mMessageBar = messageBar;
334 mMapToolIdentify->
setButton( mMapIdentificationButton );
339 mMapToolDigitize->
setButton( mAddEntryButton );
340 updateAddEntryButton();
348 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::MinimumExpanding );
349 mTopLayout->setAlignment( Qt::AlignTop );
352 mAttributeEditorFrame->setVisible( display );
353 mEmbedForm = display;
358 mComboBox->setEnabled( !readOnly );
359 mRemoveFKButton->setVisible( mAllowNull && readOnly );
360 mReadOnlySelector = readOnly;
372 mFilterFields = filterFields;
388 mFilterExpression = expression;
402 if ( mReferencedLayer )
404 QApplication::setOverrideCursor( Qt::WaitCursor );
406 QSet<QString> requestedAttrs;
408 if ( !mFilterFields.isEmpty() )
410 for (
const QString &fieldName : std::as_const( mFilterFields ) )
417 QComboBox *cb =
new QComboBox();
418 cb->setProperty(
"Field", fieldName );
420 mFilterComboBoxes << cb;
421 QVariantList uniqueValues = qgis::setToList( mReferencedLayer->
uniqueValues( idx ) );
427 const auto constUniqueValues = uniqueValues;
428 for (
const QVariant &v : constUniqueValues )
430 cb->addItem( v.toString(), v );
433 connect( cb,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::filterChanged );
436 requestedAttrs << fieldName;
438 mFilterLayout->addWidget( cb );
448 : mReferencedLayer->
getFeatures( mFilterExpression );
451 const int count = std::min( mFilterComboBoxes.count(), mFilterFields.count() );
452 for (
int i = 0; i < count - 1; i++ )
454 QVariant cv = ft.
attribute( mFilterFields.at( i ) );
455 QVariant nv = ft.
attribute( mFilterFields.at( i + 1 ) );
458 mFilterCache[mFilterFields[i]][cf] << nf;
462 if ( !mFilterComboBoxes.isEmpty() )
464 QComboBox *cb = mFilterComboBoxes.first();
465 cb->setCurrentIndex( 0 );
466 disableChainedComboBoxes( cb );
472 mFilterContainer->hide();
481 if ( ! mFilterExpression.isEmpty() )
486 if ( mChainFilters && mFeature.
isValid() )
488 for (
int i = 0; i < mFilterFields.size(); i++ )
490 QVariant v = mFeature.
attribute( mFilterFields[i] );
492 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
501 QApplication::restoreOverrideCursor();
507void QgsRelationReferenceWidget::highlightActionTriggered( QAction *action )
509 if ( action == mHighlightFeatureAction )
513 else if ( action == mScaleHighlightFeatureAction )
517 else if ( action == mPanHighlightFeatureAction )
532 attributeDialog->
show();
535void QgsRelationReferenceWidget::highlightFeature(
QgsFeature f, CanvasExtent canvasExtent )
555 if ( canvasExtent ==
Scale )
568 else if ( canvasExtent ==
Pan )
578 mHighlight =
new QgsHighlight( mCanvas, f, mReferencedLayer );
582 QTimer *timer =
new QTimer(
this );
583 timer->setSingleShot(
true );
584 connect( timer, &QTimer::timeout,
this, &QgsRelationReferenceWidget::deleteHighlight );
585 timer->start( 3000 );
588void QgsRelationReferenceWidget::deleteHighlight()
595 mHighlight =
nullptr;
600 if ( !mAllowMapIdentification || !mReferencedLayer )
609 mMapToolIdentify->
setLayer( mReferencedLayer );
610 setMapTool( mMapToolIdentify );
616 QString title = tr(
"Relation %1 for %2." ).arg( mRelation.
name(), mReferencingLayer->
name() );
617 QString msg = tr(
"Identify a feature of %1 to be associated. Press <ESC> to cancel." ).arg( mReferencedLayer->
name() );
619 mMessageBar->
pushItem( mMessageBarItem );
623void QgsRelationReferenceWidget::comboReferenceChanged()
626 highlightFeature( mFeature );
627 updateAttributeEditorFrame( mFeature );
632void QgsRelationReferenceWidget::comboReferenceFoundChanged(
bool )
635 highlightFeature( mFeature );
636 updateAttributeEditorFrame( mFeature );
639void QgsRelationReferenceWidget::updateAttributeEditorFrame(
const QgsFeature &feature )
641 mOpenFormButton->setEnabled( feature.
isValid() );
643 if ( mAttributeEditorFrame && mReferencedAttributeForm )
645 mReferencedAttributeForm->
setFeature( feature );
651 return mAllowAddFeatures;
657 updateAddEntryButton();
665void QgsRelationReferenceWidget::featureIdentified(
const QgsFeature &feature )
670 mRemoveFKButton->setEnabled( mIsEditable );
671 highlightFeature( feature );
672 updateAttributeEditorFrame( feature );
678void QgsRelationReferenceWidget::setMapTool(
QgsMapTool *mapTool )
680 mCurrentMapTool = mapTool;
683 mWindowWidget = window();
685 mCanvas->window()->raise();
686 mCanvas->activateWindow();
691void QgsRelationReferenceWidget::unsetMapTool()
694 if ( mCurrentMapTool )
699 if ( mCurrentMapTool == mMapToolDigitize )
711void QgsRelationReferenceWidget::onKeyPressed( QKeyEvent *e )
713 if ( e->key() == Qt::Key_Escape )
719void QgsRelationReferenceWidget::mapToolDeactivated()
723 mWindowWidget->raise();
724 mWindowWidget->activateWindow();
727 if ( mMessageBar && mMessageBarItem )
729 mMessageBar->
popWidget( mMessageBarItem );
731 mMessageBarItem =
nullptr;
734void QgsRelationReferenceWidget::filterChanged()
738 QMap<QString, QString> filters;
741 QComboBox *scb = qobject_cast<QComboBox *>( sender() );
755 disableChainedComboBoxes( scb );
758 const auto constMFilterComboBoxes = mFilterComboBoxes;
759 for ( QComboBox *cb : constMFilterComboBoxes )
761 if ( cb->currentIndex() != 0 )
763 const QString fieldName = cb->property(
"Field" ).toString();
765 if ( cb->currentText() == nullValue.toString() )
767 filters[fieldName] = QStringLiteral(
"\"%1\" IS NULL" ).arg( fieldName );
779 QComboBox *ccb =
nullptr;
780 const auto constMFilterComboBoxes = mFilterComboBoxes;
781 for ( QComboBox *cb : constMFilterComboBoxes )
791 if ( ccb->currentIndex() != 0 )
793 const QString fieldName = cb->property(
"Field" ).toString();
795 cb->blockSignals(
true );
797 cb->addItem( cb->property(
"FieldAlias" ).toString() );
802 const auto txts { mFilterCache[ccb->property(
"Field" ).toString()][ccb->currentText()] };
803 for (
const QString &txt : txts )
805 QMap<QString, QString> filtersAttrs = filters;
810 if ( ! expression.isEmpty() && ! filtersAttrs.isEmpty() )
811 expression += QLatin1String(
" AND " );
813 expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral(
" ( " );
815 expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral(
" ) " );
822 while ( it.nextFeature( f ) )
824 if ( !featureIds.contains( f.
id() ) )
825 featureIds << f.
id();
836 cb->addItems( texts );
838 cb->setEnabled(
true );
839 cb->blockSignals(
false );
849 filterExpression += filters.isEmpty() ? QString() : QStringLiteral(
" ( " );
851 filterExpression += filters.isEmpty() ? QString() : QStringLiteral(
" ) " );
856void QgsRelationReferenceWidget::addEntry()
858 if ( !mReferencedLayer )
875 mMapToolDigitize->
setLayer( mReferencedLayer );
876 setMapTool( mMapToolDigitize );
883 QString title = tr(
"Relation %1 for %2." ).arg( mRelation.
name(), mReferencingLayer->
name() );
886 QString msg = tr(
"Link feature to %1 \"%2\" : Digitize the geometry for the new feature on layer %3. Press <ESC> to cancel." )
887 .arg( mReferencingLayer->
name(), displayString, mReferencedLayer->
name() );
889 mMessageBar->
pushItem( mMessageBarItem );
894void QgsRelationReferenceWidget::entryAdded(
const QgsFeature &feat )
900 if ( mComboBox->itemText( mComboBox->currentIndex() ) != mComboBox->currentText() )
904 if ( fieldIdx != -1 )
906 attributes.insert( fieldIdx, mComboBox->currentText() );
913 for (
const QString &fieldName : std::as_const( mReferencedFields ) )
914 attrs << f.attribute( fieldName );
918 mAddEntryButton->setEnabled(
false );
924void QgsRelationReferenceWidget::updateAddEntryButton()
926 mAddEntryButton->setVisible( mAllowAddFeatures && mMapToolDigitize );
927 mAddEntryButton->setEnabled( mReferencedLayer && mReferencedLayer->
isEditable() );
930void QgsRelationReferenceWidget::disableChainedComboBoxes(
const QComboBox *scb )
932 QComboBox *ccb =
nullptr;
933 const auto constMFilterComboBoxes = mFilterComboBoxes;
934 for ( QComboBox *cb : constMFilterComboBoxes )
946 cb->setCurrentIndex( 0 );
947 if ( ccb->currentIndex() == 0 )
949 cb->setEnabled(
false );
956void QgsRelationReferenceWidget::emitForeignKeysChanged(
const QVariantList &foreignKeys,
bool force )
970 return mReferencedLayerName;
975 mReferencedLayerName = relationLayerName;
980 return mReferencedLayerId;
985 mReferencedLayerId = relationLayerId;
990 return mReferencedLayerProviderKey;
995 mReferencedLayerProviderKey = relationProviderKey;
1000 return mReferencedLayerDataSource;
1006 mReferencedLayerDataSource = resolver.writePath( relationDataSource );
1011 mFormFeature = formFeature;
void reset(T *p=nullptr)
Will reset the managed pointer to p.
static QString nullRepresentation()
Returns the string used to represent the value NULL throughout QGIS.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
void show()
Show the dialog non-blocking. Reparents this dialog to be a child of the dialog form.
This class contains context information for attribute editor widgets.
QgsAdvancedDigitizingDockWidget * cadDockWidget() const
Returns the associated CAD dock widget (e.g.
@ Single
When showing a single feature (e.g. district information when looking at the form of a house)
const QgsVectorLayerTools * vectorLayerTools() const
Returns the associated vector layer tools.
@ Embed
A form was embedded as a widget on another form.
@ StandaloneDialog
A form was opened as a new dialog.
A groupbox that collapses/expands when toggled and can save its collapsed and checked states.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
This offers a combobox with autocompleter that allows selecting features from a layer.
void setIdentifierValues(const QVariantList &identifierValues)
The identifier values of the currently selected feature.
void setDisplayExpression(const QString &displayExpression)
The display expression will be used to display features as well as the value to match the typed text ...
void setFilterExpression(const QString &filterExpression)
An additional expression to further restrict the available features.
void setIdentifierFields(const QStringList &identifierFields)
Field name that will be used to uniquely identify the current feature.
void setSourceLayer(QgsVectorLayer *sourceLayer)
The layer from which features should be listed.
QgsFeatureRequest currentFeatureRequest() const
Shorthand for getting a feature request to query the currently selected feature.
void setIdentifierValuesToNull()
Sets the identifier values of the currently selected feature to NULL value(s).
QVariantList identifierValues
void setCurrentFeature(const QgsFeature &feature)
Sets the current index by using the given feature.
void currentFeatureFoundChanged(bool found)
Emitted when the feature picker model changes its feature found state.
void currentFeatureChanged()
Emitted when the current feature changes.
void setAllowNull(bool allowNull)
Determines if a NULL value should be available in the list.
void setFetchLimit(int fetchLimit)
Defines the feature request fetch limit If set to 0, no limit is applied when fetching.
This class wraps a request for features to a vector layer (or directly its vector data provider).
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
A geometry is the spatial representation of a feature.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
A class for highlight features on the map.
void applyDefaultStyle()
Applies the default style from the user settings to the highlight.
Map canvas is a class for displaying all GIS data types on a canvas.
void setExtent(const QgsRectangle &r, bool magnified=false)
Sets the extent of the map canvas to the specified rectangle.
void zoomByFactor(double scaleFactor, const QgsPointXY *center=nullptr, bool ignoreScaleLock=false)
Zoom with the factor supplied.
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
void keyPressed(QKeyEvent *e)
Emit key press event.
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
void refresh()
Repaints the canvas map.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
QString providerType() const
Returns the provider type (provider key) for this layer.
void editingStarted()
Emitted when editing on this layer has started.
QString publicSource(bool hidePassword=false) const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer's CRS to output CRS
A bar for displaying non-blocking messages to the user.
bool popWidget(QgsMessageBarItem *item)
Remove the specified item from the bar, and display the next most recent one in the stack.
void pushItem(QgsMessageBarItem *item)
Display a message item on the bar, after hiding the currently visible one and putting it in a stack.
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=nullptr)
Creates message bar item widget containing a message text to be displayed on the bar.
Resolves relative paths into absolute paths and vice versa.
A class to represent a 2D point.
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsPathResolver pathResolver() const
Returns path resolver object with considering whether the project uses absolute or relative paths and...
A rectangle specified with double values.
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Defines a relation between matching fields of the two involved tables of a relation.
Represents a relationship between two vector layers.
QgsVectorLayer * referencedLayer
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
QgsVectorLayer * referencingLayer
QString referencedLayerId() const
Access the referenced (parent) layer's id.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
static QString getFeatureDisplayString(const QgsVectorLayer *layer, const QgsFeature &feature)
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
QString displayExpression
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
#define Q_NOWARN_DEPRECATED_POP
QString qgsMapJoinValues(const QMap< Key, Value > &map, const QString &separator)
Joins all the map values into a single string with each element separated by the given separator.
#define Q_NOWARN_DEPRECATED_PUSH
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
QMap< int, QVariant > QgsAttributeMap
QSet< QgsFeatureId > QgsFeatureIds
QList< int > QgsAttributeList