47 for (
int i = 0; i < list.size(); ++i )
59 mTopLayout =
new QVBoxLayout(
this );
60 mTopLayout->setContentsMargins( 0, 0, 0, 0 );
62 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::Fixed );
64 setLayout( mTopLayout );
66 QHBoxLayout *editLayout =
new QHBoxLayout();
67 editLayout->setContentsMargins( 0, 0, 0, 0 );
68 editLayout->setSpacing( 2 );
71 mChooserContainer =
new QWidget;
72 editLayout->addWidget( mChooserContainer );
73 QHBoxLayout *chooserLayout =
new QHBoxLayout;
74 chooserLayout->setContentsMargins( 0, 0, 0, 0 );
75 mFilterLayout =
new QHBoxLayout;
76 mFilterLayout->setContentsMargins( 0, 0, 0, 0 );
77 mFilterContainer =
new QWidget;
78 mFilterContainer->setLayout( mFilterLayout );
79 mChooserContainer->setLayout( chooserLayout );
80 chooserLayout->addWidget( mFilterContainer );
83 mChooserContainer->layout()->addWidget( mComboBox );
86 mOpenFormButton =
new QToolButton();
88 mOpenFormButton->setText( tr(
"Open Related Feature Form" ) );
89 editLayout->addWidget( mOpenFormButton );
91 mAddEntryButton =
new QToolButton();
93 mAddEntryButton->setText( tr(
"Add New Entry" ) );
94 editLayout->addWidget( mAddEntryButton );
97 mHighlightFeatureButton =
new QToolButton(
this );
98 mHighlightFeatureButton->setPopupMode( QToolButton::MenuButtonPopup );
99 mHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionHighlightFeature.svg" ) ), tr(
"Highlight feature" ),
this );
100 mScaleHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionScaleHighlightFeature.svg" ) ), tr(
"Scale and highlight feature" ),
this );
101 mPanHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionPanHighlightFeature.svg" ) ), tr(
"Pan and highlight feature" ),
this );
102 mHighlightFeatureButton->addAction( mHighlightFeatureAction );
103 mHighlightFeatureButton->addAction( mScaleHighlightFeatureAction );
104 mHighlightFeatureButton->addAction( mPanHighlightFeatureAction );
105 mHighlightFeatureButton->setDefaultAction( mHighlightFeatureAction );
106 editLayout->addWidget( mHighlightFeatureButton );
109 mMapIdentificationButton =
new QToolButton(
this );
111 mMapIdentificationButton->setText( tr(
"Select on Map" ) );
112 mMapIdentificationButton->setCheckable(
true );
113 editLayout->addWidget( mMapIdentificationButton );
116 mRemoveFKButton =
new QToolButton(
this );
118 mRemoveFKButton->setText( tr(
"No Selection" ) );
119 editLayout->addWidget( mRemoveFKButton );
122 mTopLayout->addLayout( editLayout );
126 mAttributeEditorLayout =
new QVBoxLayout( mAttributeEditorFrame );
127 mAttributeEditorFrame->setLayout( mAttributeEditorLayout );
128 mAttributeEditorFrame->setSizePolicy( mAttributeEditorFrame->sizePolicy().horizontalPolicy(), QSizePolicy::Expanding );
129 mTopLayout->addWidget( mAttributeEditorFrame );
132 mInvalidLabel =
new QLabel( tr(
"The relation is not valid. Please make sure your relation definitions are OK." ) );
133 mInvalidLabel->setWordWrap(
true );
134 QFont font = mInvalidLabel->font();
135 font.setItalic(
true );
136 mInvalidLabel->setStyleSheet( QStringLiteral(
"QLabel { color: red; } " ) );
137 mInvalidLabel->setFont( font );
138 mTopLayout->addWidget( mInvalidLabel );
141 mMapIdentificationButton->hide();
142 mHighlightFeatureButton->hide();
143 mAttributeEditorFrame->hide();
144 mInvalidLabel->hide();
145 mAddEntryButton->hide();
149 connect( mHighlightFeatureButton, &QToolButton::triggered,
this, &QgsRelationReferenceWidget::highlightActionTriggered );
152 connect( mAddEntryButton, &QAbstractButton::clicked,
this, &QgsRelationReferenceWidget::addEntry );
153 connect( mComboBox, &QComboBox::editTextChanged,
this, &QgsRelationReferenceWidget::updateAddEntryButton );
164 mAllowNull = allowNullValue;
165 mRemoveFKButton->setVisible( allowNullValue && mReadOnlySelector );
173 mInvalidLabel->hide();
182 mReferencedFields << fieldPair.referencedField();
191 mAttributeEditorFrame->setObjectName( QStringLiteral(
"referencing/" ) +
relation.
name() );
196 mAttributeEditorFrame->setTitle( mReferencedLayer->
name() );
198 mAttributeEditorLayout->addWidget( mReferencedAttributeForm );
203 updateAddEntryButton();
207 mInvalidLabel->show();
210 if ( mShown && isVisible() )
223 mFilterContainer->setEnabled( editable );
224 mComboBox->setEnabled( editable && !mReadOnlySelector );
225 mComboBox->setEditable(
true );
226 mMapIdentificationButton->setEnabled( editable );
227 mRemoveFKButton->setEnabled( editable );
228 mIsEditable = editable;
238 if ( values.isEmpty() )
248 if ( !mReferencedLayer )
253 if ( mEmbedForm || mChainFilters )
261 const int count = std::min( mFilterComboBoxes.size(), mFilterFields.size() );
262 for (
int i = 0; i < count; i++ )
264 QVariant v = mFeature.
attribute( mFilterFields[i] );
266 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
270 mRemoveFKButton->setEnabled( mIsEditable );
271 highlightFeature( mFeature );
272 updateAttributeEditorFrame( mFeature );
280 if ( mChainFilters && !mFilterComboBoxes.isEmpty() )
282 QComboBox *cb = mFilterComboBoxes.first();
283 cb->setCurrentIndex( 0 );
284 disableChainedComboBoxes( cb );
288 mRemoveFKButton->setEnabled(
false );
297 if ( mReferencedLayer )
307 mRemoveFKButton->setEnabled(
false );
314 if ( fkeys.isEmpty() )
315 return QVariant( QVariant::Int );
317 return fkeys.at( 0 );
327 mEditorContext = context;
329 mMessageBar = messageBar;
332 mMapToolIdentify->
setButton( mMapIdentificationButton );
337 mMapToolDigitize->
setButton( mAddEntryButton );
338 updateAddEntryButton();
346 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::MinimumExpanding );
347 mTopLayout->setAlignment( Qt::AlignTop );
350 mAttributeEditorFrame->setVisible( display );
351 mEmbedForm = display;
356 mComboBox->setEnabled( !readOnly );
357 mRemoveFKButton->setVisible( mAllowNull && readOnly );
358 mReadOnlySelector = readOnly;
375 mFilterFields = filterFields;
391 mFilterExpression = expression;
405 if ( mReferencedLayer )
407 QApplication::setOverrideCursor( Qt::WaitCursor );
409 QSet<QString> requestedAttrs;
411 if ( !mFilterFields.isEmpty() )
413 for (
const QString &fieldName : std::as_const( mFilterFields ) )
420 QComboBox *cb =
new QComboBox();
421 cb->setProperty(
"Field", fieldName );
423 mFilterComboBoxes << cb;
424 QVariantList uniqueValues = qgis::setToList( mReferencedLayer->
uniqueValues( idx ) );
427 cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->
fields().
at( idx ).
type() ) );
430 const auto constUniqueValues = uniqueValues;
431 for (
const QVariant &v : constUniqueValues )
433 cb->addItem( v.toString(), v );
436 connect( cb,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::filterChanged );
439 requestedAttrs << fieldName;
441 mFilterLayout->addWidget( cb );
451 : mReferencedLayer->
getFeatures( mFilterExpression );
454 const int count = std::min( mFilterComboBoxes.count(), mFilterFields.count() );
455 for (
int i = 0; i < count - 1; i++ )
457 QVariant cv = ft.
attribute( mFilterFields.at( i ) );
458 QVariant nv = ft.
attribute( mFilterFields.at( i + 1 ) );
461 mFilterCache[mFilterFields[i]][cf] << nf;
465 if ( !mFilterComboBoxes.isEmpty() )
467 QComboBox *cb = mFilterComboBoxes.first();
468 cb->setCurrentIndex( 0 );
469 disableChainedComboBoxes( cb );
475 mFilterContainer->hide();
483 if ( ! mFilterExpression.isEmpty() )
488 if ( mChainFilters && mFeature.
isValid() )
490 for (
int i = 0; i < mFilterFields.size(); i++ )
492 QVariant v = mFeature.
attribute( mFilterFields[i] );
494 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::updateAttributeEditorFrame(
const QgsFeature &feature )
634 mOpenFormButton->setEnabled( feature.
isValid() );
636 if ( mAttributeEditorFrame && mReferencedAttributeForm )
638 mReferencedAttributeForm->
setFeature( feature );
644 return mAllowAddFeatures;
650 updateAddEntryButton();
658void QgsRelationReferenceWidget::featureIdentified(
const QgsFeature &feature )
663 mRemoveFKButton->setEnabled( mIsEditable );
664 highlightFeature( feature );
665 updateAttributeEditorFrame( feature );
671void QgsRelationReferenceWidget::setMapTool(
QgsMapTool *mapTool )
673 mCurrentMapTool = mapTool;
676 mWindowWidget = window();
678 mCanvas->window()->raise();
679 mCanvas->activateWindow();
684void QgsRelationReferenceWidget::unsetMapTool()
687 if ( mCurrentMapTool )
692 if ( mCurrentMapTool == mMapToolDigitize )
704void QgsRelationReferenceWidget::onKeyPressed( QKeyEvent *e )
706 if ( e->key() == Qt::Key_Escape )
712void QgsRelationReferenceWidget::mapToolDeactivated()
716 mWindowWidget->raise();
717 mWindowWidget->activateWindow();
720 if ( mMessageBar && mMessageBarItem )
722 mMessageBar->
popWidget( mMessageBarItem );
724 mMessageBarItem =
nullptr;
727void QgsRelationReferenceWidget::filterChanged()
731 QMap<QString, QString> filters;
734 QComboBox *scb = qobject_cast<QComboBox *>( sender() );
748 disableChainedComboBoxes( scb );
751 const auto constMFilterComboBoxes = mFilterComboBoxes;
752 for ( QComboBox *cb : constMFilterComboBoxes )
754 if ( cb->currentIndex() != 0 )
756 const QString fieldName = cb->property(
"Field" ).toString();
758 if ( cb->currentText() == nullValue.toString() )
760 filters[fieldName] = QStringLiteral(
"\"%1\" IS NULL" ).arg( fieldName );
772 QComboBox *ccb =
nullptr;
773 const auto constMFilterComboBoxes = mFilterComboBoxes;
774 for ( QComboBox *cb : constMFilterComboBoxes )
784 if ( ccb->currentIndex() != 0 )
786 const QString fieldName = cb->property(
"Field" ).toString();
788 cb->blockSignals(
true );
790 cb->addItem( cb->property(
"FieldAlias" ).toString() );
795 const auto txts { mFilterCache[ccb->property(
"Field" ).toString()][ccb->currentText()] };
796 for (
const QString &txt : txts )
798 QMap<QString, QString> filtersAttrs = filters;
804 expression += QLatin1String(
" AND " );
806 expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral(
" ( " );
808 expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral(
" ) " );
815 while ( it.nextFeature( f ) )
817 if ( !featureIds.contains( f.
id() ) )
818 featureIds << f.
id();
829 cb->addItems( texts );
831 cb->setEnabled(
true );
832 cb->blockSignals(
false );
842 filterExpression += filters.isEmpty() ? QString() : QStringLiteral(
" ( " );
844 filterExpression += filters.isEmpty() ? QString() : QStringLiteral(
" ) " );
849void QgsRelationReferenceWidget::addEntry()
851 if ( !mReferencedLayer )
861 if ( mReferencedLayer->
geometryType() == Qgis::GeometryType::Unknown || mReferencedLayer->
geometryType() == Qgis::GeometryType::Null )
868 mMapToolDigitize->
setLayer( mReferencedLayer );
869 setMapTool( mMapToolDigitize );
876 QString title = tr(
"Relation %1 for %2." ).arg( mRelation.
name(), mReferencingLayer->
name() );
879 QString msg = tr(
"Link feature to %1 \"%2\" : Digitize the geometry for the new feature on layer %3. Press <ESC> to cancel." )
880 .arg( mReferencingLayer->
name(), displayString, mReferencedLayer->
name() );
882 mMessageBar->
pushItem( mMessageBarItem );
887void QgsRelationReferenceWidget::entryAdded(
const QgsFeature &feat )
893 if ( mComboBox->itemText( mComboBox->currentIndex() ) != mComboBox->currentText() )
897 if ( fieldIdx != -1 )
899 attributes.insert( fieldIdx, mComboBox->currentText() );
906 for (
const QString &fieldName : std::as_const( mReferencedFields ) )
911 mAddEntryButton->setEnabled(
false );
917void QgsRelationReferenceWidget::updateAddEntryButton()
919 mAddEntryButton->setVisible( mAllowAddFeatures && mMapToolDigitize );
920 mAddEntryButton->setEnabled( mReferencedLayer && mReferencedLayer->
isEditable() );
923void QgsRelationReferenceWidget::disableChainedComboBoxes(
const QComboBox *scb )
925 QComboBox *ccb =
nullptr;
926 const auto constMFilterComboBoxes = mFilterComboBoxes;
927 for ( QComboBox *cb : constMFilterComboBoxes )
939 cb->setCurrentIndex( 0 );
940 if ( ccb->currentIndex() == 0 )
942 cb->setEnabled(
false );
949void QgsRelationReferenceWidget::emitForeignKeysChanged(
const QVariantList &foreignKeys,
bool force )
963 return mReferencedLayerName;
968 mReferencedLayerName = relationLayerName;
973 return mReferencedLayerId;
978 mReferencedLayerId = relationLayerId;
983 return mReferencedLayerProviderKey;
988 mReferencedLayerProviderKey = relationProviderKey;
993 return mReferencedLayerDataSource;
999 mReferencedLayerDataSource = resolver.writePath( relationDataSource );
1004 mFormFeature = formFeature;
void reset(T *p=nullptr)
Will reset the managed pointer to p.
static QString nullRepresentation()
This string is 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, QVariant::Type fieldType=QVariant::Type::Invalid)
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)
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 currentFeatureChanged()
Emitted when the current feature changes.
void setAllowNull(bool allowNull)
Determines if a NULL value should be available in the list.
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.
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).
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.
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
void editingStarted()
Emitted when editing on this layer has started.
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.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
bool contains(const QgsRectangle &rect) const SIP_HOLDGIL
Returns true when rectangle contains other rectangle.
Defines a relation between matching fields of the two involved tables of a relation.
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)
Returns true if the specified variant should be considered a NULL value.
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.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
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.
CORE_EXPORT QgsMeshVertex centroid(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns the centroid of the face.
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