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();
192 mAttributeEditorFrame->setObjectName( QStringLiteral(
"referencing/" ) +
relation.
name() );
197 mAttributeEditorFrame->setTitle( mReferencedLayer->
name() );
199 mAttributeEditorLayout->addWidget( mReferencedAttributeForm );
204 updateAddEntryButton();
208 mInvalidLabel->show();
211 if ( mShown && isVisible() )
224 mFilterContainer->setEnabled( editable );
225 mComboBox->setEnabled( editable && !mReadOnlySelector );
226 mComboBox->setEditable(
true );
227 mMapIdentificationButton->setEnabled( editable );
228 mRemoveFKButton->setEnabled( editable );
229 mIsEditable = editable;
239 if ( values.isEmpty() )
249 if ( !mReferencedLayer )
254 if ( mEmbedForm || mChainFilters )
262 const int count = std::min( mFilterComboBoxes.size(), mFilterFields.size() );
263 for (
int i = 0; i < count; i++ )
265 QVariant v = mFeature.
attribute( mFilterFields[i] );
267 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
271 mRemoveFKButton->setEnabled( mIsEditable );
272 highlightFeature( mFeature );
273 updateAttributeEditorFrame( mFeature );
281 if ( mChainFilters && !mFilterComboBoxes.isEmpty() )
283 QComboBox *cb = mFilterComboBoxes.first();
284 cb->setCurrentIndex( 0 );
285 disableChainedComboBoxes( cb );
289 mRemoveFKButton->setEnabled(
false );
298 if ( mReferencedLayer )
308 mRemoveFKButton->setEnabled(
false );
315 if ( fkeys.isEmpty() )
316 return QVariant( QVariant::Int );
318 return fkeys.at( 0 );
328 mEditorContext = context;
330 mMessageBar = messageBar;
333 mMapToolIdentify->
setButton( mMapIdentificationButton );
338 mMapToolDigitize->
setButton( mAddEntryButton );
339 updateAddEntryButton();
347 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::MinimumExpanding );
348 mTopLayout->setAlignment( Qt::AlignTop );
351 mAttributeEditorFrame->setVisible( display );
352 mEmbedForm = display;
357 mComboBox->setEnabled( !readOnly );
358 mRemoveFKButton->setVisible( mAllowNull && readOnly );
359 mReadOnlySelector = readOnly;
371 mFilterFields = filterFields;
387 mFilterExpression = expression;
401 if ( mReferencedLayer )
403 QApplication::setOverrideCursor( Qt::WaitCursor );
405 QSet<QString> requestedAttrs;
407 if ( !mFilterFields.isEmpty() )
409 for (
const QString &fieldName : std::as_const( mFilterFields ) )
416 QComboBox *cb =
new QComboBox();
417 cb->setProperty(
"Field", fieldName );
419 mFilterComboBoxes << cb;
420 QVariantList uniqueValues = qgis::setToList( mReferencedLayer->
uniqueValues( idx ) );
423 cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->
fields().
at( idx ).
type() ) );
426 const auto constUniqueValues = uniqueValues;
427 for (
const QVariant &v : constUniqueValues )
429 cb->addItem( v.toString(), v );
432 connect( cb,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::filterChanged );
435 requestedAttrs << fieldName;
437 mFilterLayout->addWidget( cb );
447 : mReferencedLayer->
getFeatures( mFilterExpression );
450 const int count = std::min( mFilterComboBoxes.count(), mFilterFields.count() );
451 for (
int i = 0; i < count - 1; i++ )
453 QVariant cv = ft.
attribute( mFilterFields.at( i ) );
454 QVariant nv = ft.
attribute( mFilterFields.at( i + 1 ) );
457 mFilterCache[mFilterFields[i]][cf] << nf;
461 if ( !mFilterComboBoxes.isEmpty() )
463 QComboBox *cb = mFilterComboBoxes.first();
464 cb->setCurrentIndex( 0 );
465 disableChainedComboBoxes( cb );
471 mFilterContainer->hide();
480 if ( ! mFilterExpression.isEmpty() )
485 if ( mChainFilters && mFeature.
isValid() )
487 for (
int i = 0; i < mFilterFields.size(); i++ )
489 QVariant v = mFeature.
attribute( mFilterFields[i] );
491 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
498 QApplication::restoreOverrideCursor();
504void QgsRelationReferenceWidget::highlightActionTriggered( QAction *action )
506 if ( action == mHighlightFeatureAction )
510 else if ( action == mScaleHighlightFeatureAction )
514 else if ( action == mPanHighlightFeatureAction )
529 attributeDialog->
show();
532void QgsRelationReferenceWidget::highlightFeature(
QgsFeature f, CanvasExtent canvasExtent )
552 if ( canvasExtent ==
Scale )
565 else if ( canvasExtent ==
Pan )
575 mHighlight =
new QgsHighlight( mCanvas, f, mReferencedLayer );
579 QTimer *timer =
new QTimer(
this );
580 timer->setSingleShot(
true );
581 connect( timer, &QTimer::timeout,
this, &QgsRelationReferenceWidget::deleteHighlight );
582 timer->start( 3000 );
585void QgsRelationReferenceWidget::deleteHighlight()
592 mHighlight =
nullptr;
597 if ( !mAllowMapIdentification || !mReferencedLayer )
606 mMapToolIdentify->
setLayer( mReferencedLayer );
607 setMapTool( mMapToolIdentify );
613 QString title = tr(
"Relation %1 for %2." ).arg( mRelation.
name(), mReferencingLayer->
name() );
614 QString msg = tr(
"Identify a feature of %1 to be associated. Press <ESC> to cancel." ).arg( mReferencedLayer->
name() );
616 mMessageBar->
pushItem( mMessageBarItem );
620void QgsRelationReferenceWidget::comboReferenceChanged()
623 highlightFeature( mFeature );
624 updateAttributeEditorFrame( mFeature );
629void QgsRelationReferenceWidget::updateAttributeEditorFrame(
const QgsFeature &feature )
631 mOpenFormButton->setEnabled( feature.
isValid() );
633 if ( mAttributeEditorFrame && mReferencedAttributeForm )
635 mReferencedAttributeForm->
setFeature( feature );
641 return mAllowAddFeatures;
647 updateAddEntryButton();
655void QgsRelationReferenceWidget::featureIdentified(
const QgsFeature &feature )
660 mRemoveFKButton->setEnabled( mIsEditable );
661 highlightFeature( feature );
662 updateAttributeEditorFrame( feature );
668void QgsRelationReferenceWidget::setMapTool(
QgsMapTool *mapTool )
670 mCurrentMapTool = mapTool;
673 mWindowWidget = window();
675 mCanvas->window()->raise();
676 mCanvas->activateWindow();
681void QgsRelationReferenceWidget::unsetMapTool()
684 if ( mCurrentMapTool )
689 if ( mCurrentMapTool == mMapToolDigitize )
701void QgsRelationReferenceWidget::onKeyPressed( QKeyEvent *e )
703 if ( e->key() == Qt::Key_Escape )
709void QgsRelationReferenceWidget::mapToolDeactivated()
713 mWindowWidget->raise();
714 mWindowWidget->activateWindow();
717 if ( mMessageBar && mMessageBarItem )
719 mMessageBar->
popWidget( mMessageBarItem );
721 mMessageBarItem =
nullptr;
724void QgsRelationReferenceWidget::filterChanged()
728 QMap<QString, QString> filters;
731 QComboBox *scb = qobject_cast<QComboBox *>( sender() );
745 disableChainedComboBoxes( scb );
748 const auto constMFilterComboBoxes = mFilterComboBoxes;
749 for ( QComboBox *cb : constMFilterComboBoxes )
751 if ( cb->currentIndex() != 0 )
753 const QString fieldName = cb->property(
"Field" ).toString();
755 if ( cb->currentText() == nullValue.toString() )
757 filters[fieldName] = QStringLiteral(
"\"%1\" IS NULL" ).arg( fieldName );
769 QComboBox *ccb =
nullptr;
770 const auto constMFilterComboBoxes = mFilterComboBoxes;
771 for ( QComboBox *cb : constMFilterComboBoxes )
781 if ( ccb->currentIndex() != 0 )
783 const QString fieldName = cb->property(
"Field" ).toString();
785 cb->blockSignals(
true );
787 cb->addItem( cb->property(
"FieldAlias" ).toString() );
792 const auto txts { mFilterCache[ccb->property(
"Field" ).toString()][ccb->currentText()] };
793 for (
const QString &txt : txts )
795 QMap<QString, QString> filtersAttrs = filters;
801 expression += QLatin1String(
" AND " );
803 expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral(
" ( " );
805 expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral(
" ) " );
812 while ( it.nextFeature( f ) )
814 if ( !featureIds.contains( f.
id() ) )
815 featureIds << f.
id();
826 cb->addItems( texts );
828 cb->setEnabled(
true );
829 cb->blockSignals(
false );
839 filterExpression += filters.isEmpty() ? QString() : QStringLiteral(
" ( " );
841 filterExpression += filters.isEmpty() ? QString() : QStringLiteral(
" ) " );
846void QgsRelationReferenceWidget::addEntry()
848 if ( !mReferencedLayer )
865 mMapToolDigitize->
setLayer( mReferencedLayer );
866 setMapTool( mMapToolDigitize );
873 QString title = tr(
"Relation %1 for %2." ).arg( mRelation.
name(), mReferencingLayer->
name() );
876 QString msg = tr(
"Link feature to %1 \"%2\" : Digitize the geometry for the new feature on layer %3. Press <ESC> to cancel." )
877 .arg( mReferencingLayer->
name(), displayString, mReferencedLayer->
name() );
879 mMessageBar->
pushItem( mMessageBarItem );
884void QgsRelationReferenceWidget::entryAdded(
const QgsFeature &feat )
890 if ( mComboBox->itemText( mComboBox->currentIndex() ) != mComboBox->currentText() )
894 if ( fieldIdx != -1 )
896 attributes.insert( fieldIdx, mComboBox->currentText() );
903 for (
const QString &fieldName : std::as_const( mReferencedFields ) )
904 attrs << f.attribute( fieldName );
908 mAddEntryButton->setEnabled(
false );
914void QgsRelationReferenceWidget::updateAddEntryButton()
916 mAddEntryButton->setVisible( mAllowAddFeatures && mMapToolDigitize );
917 mAddEntryButton->setEnabled( mReferencedLayer && mReferencedLayer->
isEditable() );
920void QgsRelationReferenceWidget::disableChainedComboBoxes(
const QComboBox *scb )
922 QComboBox *ccb =
nullptr;
923 const auto constMFilterComboBoxes = mFilterComboBoxes;
924 for ( QComboBox *cb : constMFilterComboBoxes )
936 cb->setCurrentIndex( 0 );
937 if ( ccb->currentIndex() == 0 )
939 cb->setEnabled(
false );
946void QgsRelationReferenceWidget::emitForeignKeysChanged(
const QVariantList &foreignKeys,
bool force )
960 return mReferencedLayerName;
965 mReferencedLayerName = relationLayerName;
970 return mReferencedLayerId;
975 mReferencedLayerId = relationLayerId;
980 return mReferencedLayerProviderKey;
985 mReferencedLayerProviderKey = relationProviderKey;
990 return mReferencedLayerDataSource;
996 mReferencedLayerDataSource = resolver.writePath( relationDataSource );
1001 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.
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.
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.
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.
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.
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