18 #include <QPushButton> 20 #include <QHBoxLayout> 30 #include "qgsexpression.h" 48 mTopLayout =
new QVBoxLayout(
this );
49 mTopLayout->setContentsMargins( 0, 0, 0, 0 );
51 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::Fixed );
53 setLayout( mTopLayout );
55 QHBoxLayout *editLayout =
new QHBoxLayout();
56 editLayout->setContentsMargins( 0, 0, 0, 0 );
57 editLayout->setSpacing( 2 );
60 mChooserContainer =
new QWidget;
61 editLayout->addWidget( mChooserContainer );
62 QHBoxLayout *chooserLayout =
new QHBoxLayout;
63 chooserLayout->setContentsMargins( 0, 0, 0, 0 );
64 mFilterLayout =
new QHBoxLayout;
65 mFilterLayout->setContentsMargins( 0, 0, 0, 0 );
66 mFilterContainer =
new QWidget;
67 mFilterContainer->setLayout( mFilterLayout );
68 mChooserContainer->setLayout( chooserLayout );
69 chooserLayout->addWidget( mFilterContainer );
72 mChooserContainer->layout()->addWidget( mComboBox );
75 mLineEdit =
new QLineEdit();
76 mLineEdit->setReadOnly(
true );
77 editLayout->addWidget( mLineEdit );
80 mOpenFormButton =
new QToolButton();
82 mOpenFormButton->setText( tr(
"Open related feature form" ) );
83 editLayout->addWidget( mOpenFormButton );
85 mAddEntryButton =
new QToolButton();
87 mAddEntryButton->setText( tr(
"Add new entry" ) );
88 editLayout->addWidget( mAddEntryButton );
91 mHighlightFeatureButton =
new QToolButton(
this );
92 mHighlightFeatureButton->setPopupMode( QToolButton::MenuButtonPopup );
93 mHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionHighlightFeature.svg" ) ), tr(
"Highlight feature" ),
this );
94 mScaleHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionScaleHighlightFeature.svg" ) ), tr(
"Scale and highlight feature" ),
this );
95 mPanHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionPanHighlightFeature.svg" ) ), tr(
"Pan and highlight feature" ),
this );
96 mHighlightFeatureButton->addAction( mHighlightFeatureAction );
97 mHighlightFeatureButton->addAction( mScaleHighlightFeatureAction );
98 mHighlightFeatureButton->addAction( mPanHighlightFeatureAction );
99 mHighlightFeatureButton->setDefaultAction( mHighlightFeatureAction );
100 editLayout->addWidget( mHighlightFeatureButton );
103 mMapIdentificationButton =
new QToolButton(
this );
105 mMapIdentificationButton->setText( tr(
"Select on map" ) );
106 mMapIdentificationButton->setCheckable(
true );
107 editLayout->addWidget( mMapIdentificationButton );
110 mRemoveFKButton =
new QToolButton(
this );
112 mRemoveFKButton->setText( tr(
"No selection" ) );
113 editLayout->addWidget( mRemoveFKButton );
116 mTopLayout->addLayout( editLayout );
120 mAttributeEditorLayout =
new QVBoxLayout( mAttributeEditorFrame );
121 mAttributeEditorFrame->setLayout( mAttributeEditorLayout );
122 mAttributeEditorFrame->setSizePolicy( mAttributeEditorFrame->sizePolicy().horizontalPolicy(), QSizePolicy::Expanding );
123 mTopLayout->addWidget( mAttributeEditorFrame );
126 mInvalidLabel =
new QLabel( tr(
"The relation is not valid. Please make sure your relation definitions are OK." ) );
127 mInvalidLabel->setWordWrap(
true );
128 QFont font = mInvalidLabel->font();
129 font.setItalic(
true );
130 mInvalidLabel->setStyleSheet( QStringLiteral(
"QLabel { color: red; } " ) );
131 mInvalidLabel->setFont( font );
132 mTopLayout->addWidget( mInvalidLabel );
136 mMapIdentificationButton->hide();
137 mHighlightFeatureButton->hide();
138 mAttributeEditorFrame->hide();
139 mInvalidLabel->hide();
143 connect( mHighlightFeatureButton, &QToolButton::triggered,
this, &QgsRelationReferenceWidget::highlightActionTriggered );
146 connect( mAddEntryButton, &QAbstractButton::clicked,
this, &QgsRelationReferenceWidget::addEntry );
147 connect( mComboBox, &QComboBox::editTextChanged,
this, &QgsRelationReferenceWidget::updateAddEntryButton );
160 mAllowNull = allowNullValue;
161 mRemoveFKButton->setVisible( allowNullValue && mReadOnlySelector );
165 mInvalidLabel->hide();
167 mRelation = relation;
169 mRelationName = relation.
name();
171 mReferencedField = relation.
fieldPairs().at( 0 ).second;
177 mAttributeEditorFrame->setObjectName( QStringLiteral(
"referencing/" ) + relation.
name() );
183 mAttributeEditorFrame->setTitle( mReferencedLayer->
name() );
185 mAttributeEditorLayout->addWidget( mReferencedAttributeForm );
190 updateAddEntryButton();
194 mInvalidLabel->show();
197 if ( mShown && isVisible() )
208 mFilterContainer->setEnabled( editable );
209 mComboBox->setEnabled( editable );
210 mComboBox->setEditable(
true );
211 mMapIdentificationButton->setEnabled( editable );
212 mRemoveFKButton->setEnabled( editable );
213 mIsEditable = editable;
218 if ( !value.isValid() )
222 if ( value.isNull() )
228 if ( !mReferencedLayer )
231 if ( mReadOnlySelector )
247 mForeignKey = mFeature.
attribute( mReferencedFieldIdx );
251 context.setFeature( mFeature );
252 QString title = expr.evaluate( &context ).toString();
253 if ( expr.hasEvalError() )
255 title = mFeature.
attribute( mReferencedFieldIdx ).toString();
257 mLineEdit->setText( title );
271 const int count = std::min( mFilterComboBoxes.size(), mFilterFields.size() );
272 for (
int i = 0; i < count; i++ )
274 QVariant v = mFeature.
attribute( mFilterFields[i] );
275 QString
f = v.isNull() ? nullValue.toString() : v.toString();
276 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
281 mRemoveFKButton->setEnabled( mIsEditable );
282 highlightFeature( mFeature );
283 updateAttributeEditorFrame( mFeature );
290 if ( mChainFilters && !mFilterComboBoxes.isEmpty() )
292 QComboBox *cb = mFilterComboBoxes.first();
293 cb->setCurrentIndex( 0 );
294 disableChainedComboBoxes( cb );
297 if ( mReadOnlySelector )
304 nullText = tr(
"%1 (no selection)" ).arg( nullValue );
306 mLineEdit->setText( nullText );
307 mForeignKey = QVariant();
316 mRemoveFKButton->setEnabled(
false );
324 if ( mReferencedLayer )
327 if ( mReadOnlySelector )
342 if ( mReadOnlySelector )
348 whileBlocking( mComboBox )->setIdentifierValue( QVariant() );
350 mRemoveFKButton->setEnabled(
false );
356 if ( mReadOnlySelector )
368 mEditorContext = context;
370 mMessageBar = messageBar;
375 mMapTool->
setButton( mMapIdentificationButton );
382 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::MinimumExpanding );
383 mTopLayout->setAlignment( Qt::AlignTop );
386 mAttributeEditorFrame->setVisible( display );
387 mEmbedForm = display;
392 mChooserContainer->setHidden( readOnly );
393 mLineEdit->setVisible( readOnly );
394 mRemoveFKButton->setVisible( mAllowNull && readOnly );
395 mReadOnlySelector = readOnly;
400 mHighlightFeatureButton->setVisible( allowMapIdentification );
401 mMapIdentificationButton->setVisible( allowMapIdentification );
412 mFilterFields = filterFields;
417 mOpenFormButton->setVisible( openFormButtonVisible );
437 if ( !mReadOnlySelector && mReferencedLayer )
439 QApplication::setOverrideCursor( Qt::WaitCursor );
441 QSet<QString> requestedAttrs;
443 if ( !mFilterFields.isEmpty() )
445 for (
const QString &fieldName : qgis::as_const( mFilterFields ) )
452 QComboBox *cb =
new QComboBox();
453 cb->setProperty(
"Field", fieldName );
455 mFilterComboBoxes << cb;
456 QVariantList uniqueValues = mReferencedLayer->
uniqueValues( idx ).toList();
459 cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->
fields().
at( idx ).
type() ) );
462 Q_FOREACH (
const QVariant &v, uniqueValues )
464 cb->addItem( v.toString(), v );
467 connect( cb,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::filterChanged );
470 requestedAttrs << fieldName;
472 mFilterLayout->addWidget( cb );
483 const int count = std::min( mFilterComboBoxes.count(), mFilterFields.count() );
484 for (
int i = 0; i < count - 1; i++ )
486 QVariant cv = ft.
attribute( mFilterFields.at( i ) );
487 QVariant nv = ft.
attribute( mFilterFields.at( i + 1 ) );
488 QString cf = cv.isNull() ? nullValue.toString() : cv.toString();
489 QString nf = nv.isNull() ? nullValue.toString() : nv.toString();
490 mFilterCache[mFilterFields[i]][cf] << nf;
494 if ( !mFilterComboBoxes.isEmpty() )
496 QComboBox *cb = mFilterComboBoxes.first();
497 cb->setCurrentIndex( 0 );
498 disableChainedComboBoxes( cb );
504 mFilterContainer->hide();
514 if ( mChainFilters && mFeature.
isValid() )
516 for (
int i = 0; i < mFilterFields.size(); i++ )
518 QVariant v = mFeature.
attribute( mFilterFields[i] );
519 QString
f = v.isNull() ? nullValue.toString() : v.toString();
520 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
525 connect( mComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::comboReferenceChanged );
526 updateAttributeEditorFrame( mFeature );
527 QApplication::restoreOverrideCursor();
531 void QgsRelationReferenceWidget::highlightActionTriggered( QAction *action )
533 if ( action == mHighlightFeatureAction )
537 else if ( action == mScaleHighlightFeatureAction )
541 else if ( action == mPanHighlightFeatureAction )
556 attributeDialog.exec();
579 if ( canvasExtent ==
Scale )
592 else if ( canvasExtent ==
Pan )
602 mHighlight =
new QgsHighlight( mCanvas, f, mReferencedLayer );
610 color.setAlpha( alpha );
616 QTimer *timer =
new QTimer(
this );
617 timer->setSingleShot(
true );
618 connect( timer, &QTimer::timeout,
this, &QgsRelationReferenceWidget::deleteHighlight );
619 timer->start( 3000 );
622 void QgsRelationReferenceWidget::deleteHighlight()
629 mHighlight =
nullptr;
634 if ( !mAllowMapIdentification || !mReferencedLayer )
643 mMapTool->
setLayer( mReferencedLayer );
646 mWindowWidget = window();
648 mCanvas->window()->raise();
649 mCanvas->activateWindow();
657 QString title = tr(
"Relation %1 for %2." ).arg( mRelationName, mReferencingLayer->
name() );
658 QString msg = tr(
"Identify a feature of %1 to be associated. Press <ESC> to cancel." ).arg( mReferencedLayer->
name() );
660 mMessageBar->
pushItem( mMessageBarItem );
664 void QgsRelationReferenceWidget::comboReferenceChanged(
int index )
668 highlightFeature( mFeature );
669 updateAttributeEditorFrame( mFeature );
673 void QgsRelationReferenceWidget::updateAttributeEditorFrame(
const QgsFeature &feature )
675 mOpenFormButton->setEnabled( feature.
isValid() );
677 if ( mAttributeEditorFrame && mReferencedAttributeForm )
679 mReferencedAttributeForm->
setFeature( feature );
685 return mAllowAddFeatures;
691 updateAddEntryButton();
694 void QgsRelationReferenceWidget::featureIdentified(
const QgsFeature &feature )
696 if ( mReadOnlySelector )
700 context.setFeature( feature );
701 QString title = expr.evaluate( &context ).toString();
702 if ( expr.hasEvalError() )
704 title = feature.
attribute( mReferencedFieldIdx ).toString();
706 mLineEdit->setText( title );
707 mForeignKey = feature.
attribute( mReferencedFieldIdx );
716 mRemoveFKButton->setEnabled( mIsEditable );
717 highlightFeature( feature );
718 updateAttributeEditorFrame( feature );
724 void QgsRelationReferenceWidget::unsetMapTool()
727 if ( mCanvas && mMapTool )
734 void QgsRelationReferenceWidget::mapToolDeactivated()
738 mWindowWidget->raise();
739 mWindowWidget->activateWindow();
742 if ( mMessageBar && mMessageBarItem )
744 mMessageBar->
popWidget( mMessageBarItem );
746 mMessageBarItem =
nullptr;
749 void QgsRelationReferenceWidget::filterChanged()
753 QMap<QString, QString> filters;
756 QComboBox *scb = qobject_cast<QComboBox *>( sender() );
762 QString filterExpression;
766 disableChainedComboBoxes( scb );
769 Q_FOREACH ( QComboBox *cb, mFilterComboBoxes )
771 if ( cb->currentIndex() != 0 )
773 const QString fieldName = cb->property(
"Field" ).toString();
775 if ( cb->currentText() == nullValue.toString() )
777 filters[fieldName] = QStringLiteral(
"\"%1\" IS NULL" ).arg( fieldName );
781 filters[fieldName] = QgsExpression::createFieldEqualityExpression( fieldName, cb->currentText() );
789 QComboBox *ccb =
nullptr;
790 Q_FOREACH ( QComboBox *cb, mFilterComboBoxes )
800 if ( ccb->currentIndex() != 0 )
802 const QString fieldName = cb->property(
"Field" ).toString();
804 cb->blockSignals(
true );
806 cb->addItem( cb->property(
"FieldAlias" ).toString() );
811 Q_FOREACH (
const QString &txt, mFilterCache[ccb->property(
"Field" ).toString()][ccb->currentText()] )
813 QMap<QString, QString> filtersAttrs = filters;
814 filtersAttrs[fieldName] = QgsExpression::createFieldEqualityExpression( fieldName, txt );
815 QString expression = filtersAttrs.values().join( QStringLiteral(
" AND " ) );
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 );
846 filterExpression = filters.values().join( QStringLiteral(
" AND " ) );
850 void QgsRelationReferenceWidget::addEntry()
856 if ( mComboBox->itemText( mComboBox->currentIndex() ) != mComboBox->currentText() )
860 if ( fieldIdx != -1 )
862 attributes.insert( fieldIdx, mComboBox->currentText() );
869 mAddEntryButton->setEnabled(
false );
873 void QgsRelationReferenceWidget::updateAddEntryButton()
875 mAddEntryButton->setVisible( mAllowAddFeatures );
876 mAddEntryButton->setEnabled( mReferencedLayer && mReferencedLayer->
isEditable() );
879 void QgsRelationReferenceWidget::disableChainedComboBoxes(
const QComboBox *scb )
881 QComboBox *ccb =
nullptr;
882 Q_FOREACH ( QComboBox *cb, mFilterComboBoxes )
894 cb->setCurrentIndex( 0 );
895 if ( ccb->currentIndex() == 0 )
897 cb->setEnabled(
false );
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
int lookupField(const QString &fieldName) const
Look up field's index from the field name.
When showing a single feature (e.g. district information when looking at the form of a house) ...
bool isValid() const
Returns the validity of this feature.
void setIdentifierValue(const QVariant &identifierValue)
The identifier value of the currently selected feature.
const QgsVectorLayerTools * vectorLayerTools() const
Wrapper for iterator of features from vector data provider or vector layer.
bool contains(const QgsRectangle &rect) const
Return true when rectangle contains other rectangle.
This offers a combobox with autocompleter that allows selecting features from a layer.
A rectangle specified with double values.
QgsFeatureRequest currentFeatureRequest() const
Shorthand for getting a feature request to query the currently selected feature.
This class is a composition of two QSettings instances:
A form was opened as a new dialog.
A groupbox that collapses/expands when toggled and can save its collapsed and checked states...
QSet< QgsFeatureId > QgsFeatureIds
This class contains context information for attribute editor widgets.
A class to represent a 2D point.
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
QgsVectorLayer referencingLayer
void setFillColor(const QColor &fillColor)
Fill color for the highlight.
A bar for displaying non-blocking messages to the user.
static const QColor DEFAULT_HIGHLIGHT_COLOR
Default highlight color.
void refresh()
Repaints the canvas map.
A geometry is the spatial representation of a feature.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const override
Calculates a list of unique values contained within an attribute in the layer.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
void setSourceLayer(QgsVectorLayer *sourceLayer)
The layer from which features should be listed.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isEditable() const override
Returns true if the provider is in editing mode.
int count() const
Return number of items.
Map canvas is a class for displaying all GIS data types on a canvas.
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
Get the feature id of the feature in this row.
void setBuffer(double buffer)
Set line / stroke buffer in millimeters.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Set feature ID that should be fetched.
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=nullptr)
make out a widget containing a message to be displayed on the bar
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QgsFields fields() const override
Returns the list of fields of this layer.
bool popWidget(QgsMessageBarItem *item)
Remove the passed widget from the bar (if previously added), then display the next one in the stack i...
void setAllowNull(bool allowNull)
Determines if a NULL value should be available in the list.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QMap< int, QVariant > QgsAttributeMap
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
void editingStopped()
Is emitted, when edited changes successfully have been written to the data provider.
A class for highlight features on the map.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
QgsVectorLayer referencedLayer
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
void editingStarted()
Is emitted, when editing on this layer has started.
QString displayExpression
void zoomByFactor(double scaleFactor, const QgsPointXY *center=nullptr)
Zoom with the factor supplied.
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
void combineExtentWith(const QgsRectangle &rect)
Expand the rectangle so that covers both the original rectangle and the given rectangle.
void setFilterExpression(const QString &filterExpression)
An additional expression to further restrict the available features.
void setValid(bool validity)
Sets the validity of the feature.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
void pushItem(QgsMessageBarItem *item)
Display a message item on the bar after hiding the currently visible one and putting it in a stack...
QgsPointXY asPoint() const
Returns contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
void setColor(const QColor &color)
Set line/stroke to color, polygon fill to color with alpha = 63.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer's CRS to output CRS
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
void setExtent(const QgsRectangle &r, bool magnified=false)
Set the extent of the map canvas.
static const double DEFAULT_HIGHLIGHT_MIN_WIDTH_MM
Default highlight line/stroke minimum width in mm.
QList< int > QgsAttributeList
bool nextFeature(QgsFeature &f)
static const double DEFAULT_HIGHLIGHT_BUFFER_MM
Default highlight buffer in mm.
void setDisplayExpression(const QString &displayExpression)
The display expression will be used to display features as well as the the value to match the typed t...
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else...
void setIdentifierField(const QString &identifierField)
Field name that will be used to uniquely identify the current feature.
A form was embedded as a widget on another form.
void setMinWidth(double width)
Set minimum line / stroke width in millimeters.