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() )
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 ) );
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 ) );
500 QApplication::restoreOverrideCursor();
506void QgsRelationReferenceWidget::highlightActionTriggered( QAction *action )
508 if ( action == mHighlightFeatureAction )
512 else if ( action == mScaleHighlightFeatureAction )
516 else if ( action == mPanHighlightFeatureAction )
531 attributeDialog->
show();
534void QgsRelationReferenceWidget::highlightFeature(
QgsFeature f, CanvasExtent canvasExtent )
554 if ( canvasExtent ==
Scale )
567 else if ( canvasExtent ==
Pan )
577 mHighlight =
new QgsHighlight( mCanvas, f, mReferencedLayer );
581 QTimer *timer =
new QTimer(
this );
582 timer->setSingleShot(
true );
583 connect( timer, &QTimer::timeout,
this, &QgsRelationReferenceWidget::deleteHighlight );
584 timer->start( 3000 );
587void QgsRelationReferenceWidget::deleteHighlight()
594 mHighlight =
nullptr;
599 if ( !mAllowMapIdentification || !mReferencedLayer )
608 mMapToolIdentify->
setLayer( mReferencedLayer );
609 setMapTool( mMapToolIdentify );
615 QString title = tr(
"Relation %1 for %2." ).arg( mRelation.
name(), mReferencingLayer->
name() );
616 QString msg = tr(
"Identify a feature of %1 to be associated. Press <ESC> to cancel." ).arg( mReferencedLayer->
name() );
618 mMessageBar->
pushItem( mMessageBarItem );
622void QgsRelationReferenceWidget::comboReferenceChanged()
625 highlightFeature( mFeature );
626 updateAttributeEditorFrame( mFeature );
631void QgsRelationReferenceWidget::comboReferenceFoundChanged(
bool )
634 highlightFeature( mFeature );
635 updateAttributeEditorFrame( mFeature );
638void QgsRelationReferenceWidget::updateAttributeEditorFrame(
const QgsFeature &feature )
640 mOpenFormButton->setEnabled( feature.
isValid() );
642 if ( mAttributeEditorFrame && mReferencedAttributeForm )
644 mReferencedAttributeForm->
setFeature( feature );
650 return mAllowAddFeatures;
656 updateAddEntryButton();
664void QgsRelationReferenceWidget::featureIdentified(
const QgsFeature &feature )
669 mRemoveFKButton->setEnabled( mIsEditable );
670 highlightFeature( feature );
671 updateAttributeEditorFrame( feature );
677void QgsRelationReferenceWidget::setMapTool(
QgsMapTool *mapTool )
679 mCurrentMapTool = mapTool;
682 mWindowWidget = window();
684 mCanvas->window()->raise();
685 mCanvas->activateWindow();
690void QgsRelationReferenceWidget::unsetMapTool()
693 if ( mCurrentMapTool )
698 if ( mCurrentMapTool == mMapToolDigitize )
710void QgsRelationReferenceWidget::onKeyPressed( QKeyEvent *e )
712 if ( e->key() == Qt::Key_Escape )
718void QgsRelationReferenceWidget::mapToolDeactivated()
722 mWindowWidget->raise();
723 mWindowWidget->activateWindow();
726 if ( mMessageBar && mMessageBarItem )
728 mMessageBar->
popWidget( mMessageBarItem );
730 mMessageBarItem =
nullptr;
733void QgsRelationReferenceWidget::filterChanged()
737 QMap<QString, QString> filters;
740 QComboBox *scb = qobject_cast<QComboBox *>( sender() );
754 disableChainedComboBoxes( scb );
757 const auto constMFilterComboBoxes = mFilterComboBoxes;
758 for ( QComboBox *cb : constMFilterComboBoxes )
760 if ( cb->currentIndex() != 0 )
762 const QString fieldName = cb->property(
"Field" ).toString();
764 if ( cb->currentText() == nullValue.toString() )
766 filters[fieldName] = QStringLiteral(
"\"%1\" IS NULL" ).arg( fieldName );
778 QComboBox *ccb =
nullptr;
779 const auto constMFilterComboBoxes = mFilterComboBoxes;
780 for ( QComboBox *cb : constMFilterComboBoxes )
790 if ( ccb->currentIndex() != 0 )
792 const QString fieldName = cb->property(
"Field" ).toString();
794 cb->blockSignals(
true );
796 cb->addItem( cb->property(
"FieldAlias" ).toString() );
801 const auto txts { mFilterCache[ccb->property(
"Field" ).toString()][ccb->currentText()] };
802 for (
const QString &txt : txts )
804 QMap<QString, QString> filtersAttrs = filters;
810 expression += QLatin1String(
" AND " );
812 expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral(
" ( " );
814 expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral(
" ) " );
821 while ( it.nextFeature( f ) )
823 if ( !featureIds.contains( f.
id() ) )
824 featureIds << f.
id();
835 cb->addItems( texts );
837 cb->setEnabled(
true );
838 cb->blockSignals(
false );
848 filterExpression += filters.isEmpty() ? QString() : QStringLiteral(
" ( " );
850 filterExpression += filters.isEmpty() ? QString() : QStringLiteral(
" ) " );
855void QgsRelationReferenceWidget::addEntry()
857 if ( !mReferencedLayer )
874 mMapToolDigitize->
setLayer( mReferencedLayer );
875 setMapTool( mMapToolDigitize );
882 QString title = tr(
"Relation %1 for %2." ).arg( mRelation.
name(), mReferencingLayer->
name() );
885 QString msg = tr(
"Link feature to %1 \"%2\" : Digitize the geometry for the new feature on layer %3. Press <ESC> to cancel." )
886 .arg( mReferencingLayer->
name(), displayString, mReferencedLayer->
name() );
888 mMessageBar->
pushItem( mMessageBarItem );
893void QgsRelationReferenceWidget::entryAdded(
const QgsFeature &feat )
899 if ( mComboBox->itemText( mComboBox->currentIndex() ) != mComboBox->currentText() )
903 if ( fieldIdx != -1 )
905 attributes.insert( fieldIdx, mComboBox->currentText() );
912 for (
const QString &fieldName : std::as_const( mReferencedFields ) )
913 attrs << f.attribute( fieldName );
917 mAddEntryButton->setEnabled(
false );
923void QgsRelationReferenceWidget::updateAddEntryButton()
925 mAddEntryButton->setVisible( mAllowAddFeatures && mMapToolDigitize );
926 mAddEntryButton->setEnabled( mReferencedLayer && mReferencedLayer->
isEditable() );
929void QgsRelationReferenceWidget::disableChainedComboBoxes(
const QComboBox *scb )
931 QComboBox *ccb =
nullptr;
932 const auto constMFilterComboBoxes = mFilterComboBoxes;
933 for ( QComboBox *cb : constMFilterComboBoxes )
945 cb->setCurrentIndex( 0 );
946 if ( ccb->currentIndex() == 0 )
948 cb->setEnabled(
false );
955void QgsRelationReferenceWidget::emitForeignKeysChanged(
const QVariantList &foreignKeys,
bool force )
969 return mReferencedLayerName;
974 mReferencedLayerName = relationLayerName;
979 return mReferencedLayerId;
984 mReferencedLayerId = relationLayerId;
989 return mReferencedLayerProviderKey;
994 mReferencedLayerProviderKey = relationProviderKey;
999 return mReferencedLayerDataSource;
1005 mReferencedLayerDataSource = resolver.writePath( relationDataSource );
1010 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