18 #include <QPushButton>
20 #include <QHBoxLayout>
55 for (
int i = 0; i < list.size(); ++i )
57 if ( !list.at( i ).isNull() )
67 mTopLayout =
new QVBoxLayout(
this );
68 mTopLayout->setContentsMargins( 0, 0, 0, 0 );
70 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::Fixed );
72 setLayout( mTopLayout );
74 QHBoxLayout *editLayout =
new QHBoxLayout();
75 editLayout->setContentsMargins( 0, 0, 0, 0 );
76 editLayout->setSpacing( 2 );
79 mChooserContainer =
new QWidget;
80 editLayout->addWidget( mChooserContainer );
81 QHBoxLayout *chooserLayout =
new QHBoxLayout;
82 chooserLayout->setContentsMargins( 0, 0, 0, 0 );
83 mFilterLayout =
new QHBoxLayout;
84 mFilterLayout->setContentsMargins( 0, 0, 0, 0 );
85 mFilterContainer =
new QWidget;
86 mFilterContainer->setLayout( mFilterLayout );
87 mChooserContainer->setLayout( chooserLayout );
88 chooserLayout->addWidget( mFilterContainer );
91 mChooserContainer->layout()->addWidget( mComboBox );
94 mOpenFormButton =
new QToolButton();
96 mOpenFormButton->setText( tr(
"Open Related Feature Form" ) );
97 editLayout->addWidget( mOpenFormButton );
99 mAddEntryButton =
new QToolButton();
101 mAddEntryButton->setText( tr(
"Add New Entry" ) );
102 editLayout->addWidget( mAddEntryButton );
105 mHighlightFeatureButton =
new QToolButton(
this );
106 mHighlightFeatureButton->setPopupMode( QToolButton::MenuButtonPopup );
107 mHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionHighlightFeature.svg" ) ), tr(
"Highlight feature" ),
this );
108 mScaleHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionScaleHighlightFeature.svg" ) ), tr(
"Scale and highlight feature" ),
this );
109 mPanHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionPanHighlightFeature.svg" ) ), tr(
"Pan and highlight feature" ),
this );
110 mHighlightFeatureButton->addAction( mHighlightFeatureAction );
111 mHighlightFeatureButton->addAction( mScaleHighlightFeatureAction );
112 mHighlightFeatureButton->addAction( mPanHighlightFeatureAction );
113 mHighlightFeatureButton->setDefaultAction( mHighlightFeatureAction );
114 editLayout->addWidget( mHighlightFeatureButton );
117 mMapIdentificationButton =
new QToolButton(
this );
119 mMapIdentificationButton->setText( tr(
"Select on Map" ) );
120 mMapIdentificationButton->setCheckable(
true );
121 editLayout->addWidget( mMapIdentificationButton );
124 mRemoveFKButton =
new QToolButton(
this );
126 mRemoveFKButton->setText( tr(
"No Selection" ) );
127 editLayout->addWidget( mRemoveFKButton );
130 mTopLayout->addLayout( editLayout );
134 mAttributeEditorLayout =
new QVBoxLayout( mAttributeEditorFrame );
135 mAttributeEditorFrame->setLayout( mAttributeEditorLayout );
136 mAttributeEditorFrame->setSizePolicy( mAttributeEditorFrame->sizePolicy().horizontalPolicy(), QSizePolicy::Expanding );
137 mTopLayout->addWidget( mAttributeEditorFrame );
140 mInvalidLabel =
new QLabel( tr(
"The relation is not valid. Please make sure your relation definitions are OK." ) );
141 mInvalidLabel->setWordWrap(
true );
142 QFont font = mInvalidLabel->font();
143 font.setItalic(
true );
144 mInvalidLabel->setStyleSheet( QStringLiteral(
"QLabel { color: red; } " ) );
145 mInvalidLabel->setFont( font );
146 mTopLayout->addWidget( mInvalidLabel );
149 mMapIdentificationButton->hide();
150 mHighlightFeatureButton->hide();
151 mAttributeEditorFrame->hide();
152 mInvalidLabel->hide();
153 mAddEntryButton->hide();
157 connect( mHighlightFeatureButton, &QToolButton::triggered,
this, &QgsRelationReferenceWidget::highlightActionTriggered );
160 connect( mAddEntryButton, &QAbstractButton::clicked,
this, &QgsRelationReferenceWidget::addEntry );
161 connect( mComboBox, &QComboBox::editTextChanged,
this, &QgsRelationReferenceWidget::updateAddEntryButton );
172 mAllowNull = allowNullValue;
173 mRemoveFKButton->setVisible( allowNullValue && mReadOnlySelector );
181 mInvalidLabel->hide();
190 mReferencedFields << fieldPair.referencedField();
199 mAttributeEditorFrame->setObjectName( QStringLiteral(
"referencing/" ) +
relation.
name() );
204 mAttributeEditorFrame->setTitle( mReferencedLayer->
name() );
206 mAttributeEditorLayout->addWidget( mReferencedAttributeForm );
211 updateAddEntryButton();
215 mInvalidLabel->show();
218 if ( mShown && isVisible() )
231 mFilterContainer->setEnabled( editable );
232 mComboBox->setEnabled( editable && !mReadOnlySelector );
233 mComboBox->setEditable(
true );
234 mMapIdentificationButton->setEnabled( editable );
235 mRemoveFKButton->setEnabled( editable );
236 mIsEditable = editable;
246 if ( values.isEmpty() )
256 if ( !mReferencedLayer )
261 if ( mEmbedForm || mChainFilters )
269 const int count = std::min( mFilterComboBoxes.size(), mFilterFields.size() );
270 for (
int i = 0; i < count; i++ )
272 QVariant v = mFeature.
attribute( mFilterFields[i] );
273 QString f = v.isNull() ? nullValue.toString() : v.toString();
274 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
278 mRemoveFKButton->setEnabled( mIsEditable );
279 highlightFeature( mFeature );
280 updateAttributeEditorFrame( mFeature );
288 if ( mChainFilters && !mFilterComboBoxes.isEmpty() )
290 QComboBox *cb = mFilterComboBoxes.first();
291 cb->setCurrentIndex( 0 );
292 disableChainedComboBoxes( cb );
296 mRemoveFKButton->setEnabled(
false );
305 if ( mReferencedLayer )
315 mRemoveFKButton->setEnabled(
false );
322 if ( fkeys.isEmpty() )
323 return QVariant( QVariant::Int );
325 return fkeys.at( 0 );
335 mEditorContext = context;
337 mMessageBar = messageBar;
340 mMapToolIdentify->
setButton( mMapIdentificationButton );
345 mMapToolDigitize->
setButton( mAddEntryButton );
346 updateAddEntryButton();
354 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::MinimumExpanding );
355 mTopLayout->setAlignment( Qt::AlignTop );
358 mAttributeEditorFrame->setVisible( display );
359 mEmbedForm = display;
364 mComboBox->setEnabled( !readOnly );
365 mRemoveFKButton->setVisible( mAllowNull && readOnly );
366 mReadOnlySelector = readOnly;
383 mFilterFields = filterFields;
399 mFilterExpression = expression;
413 if ( mReferencedLayer )
415 QApplication::setOverrideCursor( Qt::WaitCursor );
417 QSet<QString> requestedAttrs;
419 if ( !mFilterFields.isEmpty() )
421 for (
const QString &fieldName : std::as_const( mFilterFields ) )
428 QComboBox *cb =
new QComboBox();
429 cb->setProperty(
"Field", fieldName );
431 mFilterComboBoxes << cb;
432 QVariantList uniqueValues = qgis::setToList( mReferencedLayer->
uniqueValues( idx ) );
435 cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->
fields().
at( idx ).
type() ) );
438 const auto constUniqueValues = uniqueValues;
439 for (
const QVariant &v : constUniqueValues )
441 cb->addItem( v.toString(), v );
444 connect( cb,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::filterChanged );
447 requestedAttrs << fieldName;
449 mFilterLayout->addWidget( cb );
459 : mReferencedLayer->
getFeatures( mFilterExpression );
462 const int count = std::min( mFilterComboBoxes.count(), mFilterFields.count() );
463 for (
int i = 0; i < count - 1; i++ )
465 QVariant cv = ft.
attribute( mFilterFields.at( i ) );
466 QVariant nv = ft.
attribute( mFilterFields.at( i + 1 ) );
467 QString cf = cv.isNull() ? nullValue.toString() : cv.toString();
468 QString nf = nv.isNull() ? nullValue.toString() : nv.toString();
469 mFilterCache[mFilterFields[i]][cf] << nf;
473 if ( !mFilterComboBoxes.isEmpty() )
475 QComboBox *cb = mFilterComboBoxes.first();
476 cb->setCurrentIndex( 0 );
477 disableChainedComboBoxes( cb );
483 mFilterContainer->hide();
491 if ( ! mFilterExpression.isEmpty() )
496 if ( mChainFilters && mFeature.
isValid() )
498 for (
int i = 0; i < mFilterFields.size(); i++ )
500 QVariant v = mFeature.
attribute( mFilterFields[i] );
501 QString f = v.isNull() ? nullValue.toString() : v.toString();
502 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
509 QApplication::restoreOverrideCursor();
515 void QgsRelationReferenceWidget::highlightActionTriggered( QAction *action )
517 if ( action == mHighlightFeatureAction )
521 else if ( action == mScaleHighlightFeatureAction )
525 else if ( action == mPanHighlightFeatureAction )
540 attributeDialog.exec();
543 void QgsRelationReferenceWidget::highlightFeature(
QgsFeature f, CanvasExtent canvasExtent )
563 if ( canvasExtent ==
Scale )
576 else if ( canvasExtent ==
Pan )
586 mHighlight =
new QgsHighlight( mCanvas, f, mReferencedLayer );
590 QTimer *timer =
new QTimer(
this );
591 timer->setSingleShot(
true );
592 connect( timer, &QTimer::timeout,
this, &QgsRelationReferenceWidget::deleteHighlight );
593 timer->start( 3000 );
596 void QgsRelationReferenceWidget::deleteHighlight()
603 mHighlight =
nullptr;
608 if ( !mAllowMapIdentification || !mReferencedLayer )
617 mMapToolIdentify->
setLayer( mReferencedLayer );
618 setMapTool( mMapToolIdentify );
624 QString title = tr(
"Relation %1 for %2." ).arg( mRelation.
name(), mReferencingLayer->
name() );
625 QString msg = tr(
"Identify a feature of %1 to be associated. Press <ESC> to cancel." ).arg( mReferencedLayer->
name() );
627 mMessageBar->
pushItem( mMessageBarItem );
631 void QgsRelationReferenceWidget::comboReferenceChanged()
634 highlightFeature( mFeature );
635 updateAttributeEditorFrame( mFeature );
640 void QgsRelationReferenceWidget::updateAttributeEditorFrame(
const QgsFeature &feature )
642 mOpenFormButton->setEnabled( feature.
isValid() );
644 if ( mAttributeEditorFrame && mReferencedAttributeForm )
646 mReferencedAttributeForm->
setFeature( feature );
652 return mAllowAddFeatures;
658 updateAddEntryButton();
666 void QgsRelationReferenceWidget::featureIdentified(
const QgsFeature &feature )
671 mRemoveFKButton->setEnabled( mIsEditable );
672 highlightFeature( feature );
673 updateAttributeEditorFrame( feature );
679 void QgsRelationReferenceWidget::setMapTool(
QgsMapTool *mapTool )
681 mCurrentMapTool = mapTool;
684 mWindowWidget = window();
686 mCanvas->window()->raise();
687 mCanvas->activateWindow();
692 void QgsRelationReferenceWidget::unsetMapTool()
695 if ( mCurrentMapTool )
700 if ( mCurrentMapTool == mMapToolDigitize )
712 void QgsRelationReferenceWidget::onKeyPressed( QKeyEvent *e )
714 if ( e->key() == Qt::Key_Escape )
720 void QgsRelationReferenceWidget::mapToolDeactivated()
724 mWindowWidget->raise();
725 mWindowWidget->activateWindow();
728 if ( mMessageBar && mMessageBarItem )
730 mMessageBar->
popWidget( mMessageBarItem );
732 mMessageBarItem =
nullptr;
735 void QgsRelationReferenceWidget::filterChanged()
739 QMap<QString, QString> filters;
742 QComboBox *scb = qobject_cast<QComboBox *>( sender() );
756 disableChainedComboBoxes( scb );
759 const auto constMFilterComboBoxes = mFilterComboBoxes;
760 for ( QComboBox *cb : constMFilterComboBoxes )
762 if ( cb->currentIndex() != 0 )
764 const QString fieldName = cb->property(
"Field" ).toString();
766 if ( cb->currentText() == nullValue.toString() )
768 filters[fieldName] = QStringLiteral(
"\"%1\" IS NULL" ).arg( fieldName );
780 QComboBox *ccb =
nullptr;
781 const auto constMFilterComboBoxes = mFilterComboBoxes;
782 for ( QComboBox *cb : constMFilterComboBoxes )
792 if ( ccb->currentIndex() != 0 )
794 const QString fieldName = cb->property(
"Field" ).toString();
796 cb->blockSignals(
true );
798 cb->addItem( cb->property(
"FieldAlias" ).toString() );
803 const auto txts { mFilterCache[ccb->property(
"Field" ).toString()][ccb->currentText()] };
804 for (
const QString &txt : txts )
806 QMap<QString, QString> filtersAttrs = filters;
812 expression += QLatin1String(
" AND " );
814 expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral(
" ( " );
815 expression += filtersAttrs.values().join( QLatin1String(
" AND " ) );
816 expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral(
" ) " );
823 while ( it.nextFeature( f ) )
825 if ( !featureIds.contains( f.
id() ) )
826 featureIds << f.
id();
837 cb->addItems( texts );
839 cb->setEnabled(
true );
840 cb->blockSignals(
false );
850 filterExpression += filters.isEmpty() ? QString() : QStringLiteral(
" ( " );
852 filterExpression += filters.isEmpty() ? QString() : QStringLiteral(
" ) " );
857 void QgsRelationReferenceWidget::addEntry()
859 if ( !mReferencedLayer )
876 mMapToolDigitize->
setLayer( mReferencedLayer );
877 setMapTool( mMapToolDigitize );
884 QString title = tr(
"Relation %1 for %2." ).arg( mRelation.
name(), mReferencingLayer->
name() );
887 QString msg = tr(
"Link feature to %1 \"%2\" : Digitize the geometry for the new feature on layer %3. Press <ESC> to cancel." )
888 .arg( mReferencingLayer->
name(), displayString, mReferencedLayer->
name() );
890 mMessageBar->
pushItem( mMessageBarItem );
895 void QgsRelationReferenceWidget::entryAdded(
const QgsFeature &feat )
901 if ( mComboBox->itemText( mComboBox->currentIndex() ) != mComboBox->currentText() )
905 if ( fieldIdx != -1 )
907 attributes.insert( fieldIdx, mComboBox->currentText() );
914 for (
const QString &fieldName : std::as_const( mReferencedFields ) )
919 mAddEntryButton->setEnabled(
false );
925 void QgsRelationReferenceWidget::updateAddEntryButton()
927 mAddEntryButton->setVisible( mAllowAddFeatures && mMapToolDigitize );
928 mAddEntryButton->setEnabled( mReferencedLayer && mReferencedLayer->
isEditable() );
931 void QgsRelationReferenceWidget::disableChainedComboBoxes(
const QComboBox *scb )
933 QComboBox *ccb =
nullptr;
934 const auto constMFilterComboBoxes = mFilterComboBoxes;
935 for ( QComboBox *cb : constMFilterComboBoxes )
947 cb->setCurrentIndex( 0 );
948 if ( ccb->currentIndex() == 0 )
950 cb->setEnabled(
false );
957 void QgsRelationReferenceWidget::emitForeignKeysChanged(
const QVariantList &foreignKeys,
bool force )
971 return mReferencedLayerName;
976 mReferencedLayerName = relationLayerName;
981 return mReferencedLayerId;
986 mReferencedLayerId = relationLayerId;
991 return mReferencedLayerProviderKey;
996 mReferencedLayerProviderKey = relationProviderKey;
1001 return mReferencedLayerDataSource;
1007 mReferencedLayerDataSource = resolver.writePath( relationDataSource );
1012 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.
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)
@ Embed
A form was embedded as a widget on another form.
@ StandaloneDialog
A form was opened as a new dialog.
const QgsVectorLayerTools * vectorLayerTools() const
Returns the associated vector layer tools.
A groupbox that collapses/expands when toggled and can save its collapsed and checked states.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
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.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
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.
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.
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,...
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 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.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
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.
void editingStarted()
Emitted when editing on this layer has started.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
QString displayExpression
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
#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