18 #include <QPushButton> 20 #include <QHBoxLayout> 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 );
158 void QgsRelationReferenceWidget::updateIndex()
160 if ( mChainFilters && mComboBox->count() > 0 )
165 if ( ! mFilterComboBoxes.isEmpty()
166 && mFilterComboBoxes[0]->currentIndex() == 0 && mAllowNull )
170 else if ( mComboBox->count() > mComboBox->
nullIndex() )
174 else if ( mAllowNull )
183 if ( mComboBox->count() > index )
185 mComboBox->setCurrentIndex( index );
192 mAllowNull = allowNullValue;
193 mRemoveFKButton->setVisible( allowNullValue && mReadOnlySelector );
197 mInvalidLabel->hide();
199 mRelation = relation;
201 mRelationName = relation.
name();
203 mReferencedField = relation.
fieldPairs().at( 0 ).second;
208 mAttributeEditorFrame->setObjectName( QStringLiteral(
"referencing/" ) + relation.
name() );
214 mAttributeEditorFrame->setTitle( mReferencedLayer->
name() );
216 mAttributeEditorLayout->addWidget( mReferencedAttributeForm );
221 updateAddEntryButton();
225 mInvalidLabel->show();
228 if ( mShown && isVisible() )
239 mFilterContainer->setEnabled( editable );
240 mComboBox->setEnabled( editable );
241 mComboBox->setEditable(
true );
242 mMapIdentificationButton->setEnabled( editable );
243 mRemoveFKButton->setEnabled( editable );
244 mIsEditable = editable;
249 if ( !value.isValid() )
253 if ( value.isNull() )
259 if ( !mReferencedLayer )
262 if ( mReadOnlySelector )
278 mForeignKey = mFeature.
attribute( mReferencedFieldIdx );
282 context.setFeature( mFeature );
283 QString title = expr.evaluate( &context ).toString();
284 if ( expr.hasEvalError() )
286 title = mFeature.
attribute( mReferencedFieldIdx ).toString();
288 mLineEdit->setText( title );
302 const int count = std::min( mFilterComboBoxes.size(), mFilterFields.size() );
303 for (
int i = 0; i < count; i++ )
305 QVariant v = mFeature.
attribute( mFilterFields[i] );
306 QString f = v.isNull() ? nullValue.toString() : v.toString();
307 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
312 mRemoveFKButton->setEnabled( mIsEditable );
313 highlightFeature( mFeature );
314 updateAttributeEditorFrame( mFeature );
322 if ( mChainFilters && !mFilterComboBoxes.isEmpty() )
324 QComboBox *cb = mFilterComboBoxes.first();
325 cb->setCurrentIndex( 0 );
326 disableChainedComboBoxes( cb );
329 if ( mReadOnlySelector )
336 nullText = tr(
"%1 (no selection)" ).arg( nullValue );
338 mLineEdit->setText( nullText );
339 mForeignKey = QVariant( QVariant::Int );
346 mRemoveFKButton->setEnabled(
false );
348 emitForeignKeyChanged( QVariant( QVariant::Int ) );
354 if ( mReferencedLayer )
357 if ( mReadOnlySelector )
372 if ( mReadOnlySelector )
378 whileBlocking( mComboBox )->setIdentifierValue( QVariant() );
380 mRemoveFKButton->setEnabled(
false );
386 if ( mReadOnlySelector )
398 mEditorContext = context;
400 mMessageBar = messageBar;
404 mMapTool->
setButton( mMapIdentificationButton );
411 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::MinimumExpanding );
412 mTopLayout->setAlignment( Qt::AlignTop );
415 mAttributeEditorFrame->setVisible( display );
416 mEmbedForm = display;
421 mChooserContainer->setHidden( readOnly );
422 mLineEdit->setVisible( readOnly );
423 mRemoveFKButton->setVisible( mAllowNull && readOnly );
424 mReadOnlySelector = readOnly;
429 mHighlightFeatureButton->setVisible( allowMapIdentification );
430 mMapIdentificationButton->setVisible( allowMapIdentification );
441 mFilterFields = filterFields;
446 mOpenFormButton->setVisible( openFormButtonVisible );
466 if ( !mReadOnlySelector && mReferencedLayer )
468 QApplication::setOverrideCursor( Qt::WaitCursor );
470 QSet<QString> requestedAttrs;
472 if ( !mFilterFields.isEmpty() )
474 for (
const QString &fieldName : qgis::as_const( mFilterFields ) )
481 QComboBox *cb =
new QComboBox();
482 cb->setProperty(
"Field", fieldName );
484 mFilterComboBoxes << cb;
485 QVariantList uniqueValues = mReferencedLayer->
uniqueValues( idx ).toList();
488 cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->
fields().
at( idx ).
type() ) );
491 Q_FOREACH (
const QVariant &v, uniqueValues )
493 cb->addItem( v.toString(), v );
496 connect( cb,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::filterChanged );
499 requestedAttrs << fieldName;
501 mFilterLayout->addWidget( cb );
512 const int count = std::min( mFilterComboBoxes.count(), mFilterFields.count() );
513 for (
int i = 0; i < count - 1; i++ )
515 QVariant cv = ft.
attribute( mFilterFields.at( i ) );
516 QVariant nv = ft.
attribute( mFilterFields.at( i + 1 ) );
517 QString cf = cv.isNull() ? nullValue.toString() : cv.toString();
518 QString nf = nv.isNull() ? nullValue.toString() : nv.toString();
519 mFilterCache[mFilterFields[i]][cf] << nf;
523 if ( !mFilterComboBoxes.isEmpty() )
525 QComboBox *cb = mFilterComboBoxes.first();
526 cb->setCurrentIndex( 0 );
527 disableChainedComboBoxes( cb );
533 mFilterContainer->hide();
543 if ( mChainFilters && mFeature.
isValid() )
545 for (
int i = 0; i < mFilterFields.size(); i++ )
547 QVariant v = mFeature.
attribute( mFilterFields[i] );
548 QString f = v.isNull() ? nullValue.toString() : v.toString();
549 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
554 connect( mComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::comboReferenceChanged );
556 emit mComboBox->currentIndexChanged( mComboBox->currentIndex() );
558 QApplication::restoreOverrideCursor();
564 void QgsRelationReferenceWidget::highlightActionTriggered( QAction *action )
566 if ( action == mHighlightFeatureAction )
570 else if ( action == mScaleHighlightFeatureAction )
574 else if ( action == mPanHighlightFeatureAction )
589 attributeDialog.exec();
612 if ( canvasExtent ==
Scale )
625 else if ( canvasExtent ==
Pan )
635 mHighlight =
new QgsHighlight( mCanvas, f, mReferencedLayer );
643 color.setAlpha( alpha );
649 QTimer *timer =
new QTimer(
this );
650 timer->setSingleShot(
true );
651 connect( timer, &QTimer::timeout,
this, &QgsRelationReferenceWidget::deleteHighlight );
652 timer->start( 3000 );
655 void QgsRelationReferenceWidget::deleteHighlight()
662 mHighlight =
nullptr;
667 if ( !mAllowMapIdentification || !mReferencedLayer )
676 mMapTool->
setLayer( mReferencedLayer );
679 mWindowWidget = window();
681 mCanvas->window()->raise();
682 mCanvas->activateWindow();
690 QString title = tr(
"Relation %1 for %2." ).arg( mRelationName, mReferencingLayer->
name() );
691 QString msg = tr(
"Identify a feature of %1 to be associated. Press <ESC> to cancel." ).arg( mReferencedLayer->
name() );
693 mMessageBar->
pushItem( mMessageBarItem );
697 void QgsRelationReferenceWidget::comboReferenceChanged(
int index )
701 highlightFeature( mFeature );
702 updateAttributeEditorFrame( mFeature );
707 void QgsRelationReferenceWidget::updateAttributeEditorFrame(
const QgsFeature &feature )
709 mOpenFormButton->setEnabled( feature.
isValid() );
711 if ( mAttributeEditorFrame && mReferencedAttributeForm )
713 mReferencedAttributeForm->
setFeature( feature );
719 return mAllowAddFeatures;
725 updateAddEntryButton();
728 void QgsRelationReferenceWidget::featureIdentified(
const QgsFeature &feature )
730 if ( mReadOnlySelector )
734 context.setFeature( feature );
735 QString title = expr.evaluate( &context ).toString();
736 if ( expr.hasEvalError() )
738 title = feature.
attribute( mReferencedFieldIdx ).toString();
740 mLineEdit->setText( title );
741 mForeignKey = feature.
attribute( mReferencedFieldIdx );
746 mComboBox->setCurrentIndex( mComboBox->findData( feature.
attribute( mReferencedFieldIdx ), QgsFeatureFilterModel::Role::IdentifierValueRole ) );
750 mRemoveFKButton->setEnabled( mIsEditable );
751 highlightFeature( feature );
752 updateAttributeEditorFrame( feature );
758 void QgsRelationReferenceWidget::unsetMapTool()
761 if ( mCanvas && mMapTool )
768 void QgsRelationReferenceWidget::mapToolDeactivated()
772 mWindowWidget->raise();
773 mWindowWidget->activateWindow();
776 if ( mMessageBar && mMessageBarItem )
778 mMessageBar->
popWidget( mMessageBarItem );
780 mMessageBarItem =
nullptr;
783 void QgsRelationReferenceWidget::filterChanged()
787 QMap<QString, QString> filters;
790 QComboBox *scb = qobject_cast<QComboBox *>( sender() );
796 QString filterExpression;
800 disableChainedComboBoxes( scb );
803 Q_FOREACH ( QComboBox *cb, mFilterComboBoxes )
805 if ( cb->currentIndex() != 0 )
807 const QString fieldName = cb->property(
"Field" ).toString();
809 if ( cb->currentText() == nullValue.toString() )
811 filters[fieldName] = QStringLiteral(
"\"%1\" IS NULL" ).arg( fieldName );
823 QComboBox *ccb =
nullptr;
824 Q_FOREACH ( QComboBox *cb, mFilterComboBoxes )
834 if ( ccb->currentIndex() != 0 )
836 const QString fieldName = cb->property(
"Field" ).toString();
838 cb->blockSignals(
true );
840 cb->addItem( cb->property(
"FieldAlias" ).toString() );
845 Q_FOREACH (
const QString &txt, mFilterCache[ccb->property(
"Field" ).toString()][ccb->currentText()] )
847 QMap<QString, QString> filtersAttrs = filters;
849 QString expression = filtersAttrs.values().join( QStringLiteral(
" AND " ) );
857 while ( it.nextFeature( f ) )
859 if ( !featureIds.contains( f.
id() ) )
860 featureIds << f.
id();
871 cb->addItems( texts );
873 cb->setEnabled(
true );
874 cb->blockSignals(
false );
880 filterExpression = filters.values().join( QStringLiteral(
" AND " ) );
884 void QgsRelationReferenceWidget::addEntry()
890 if ( mComboBox->itemText( mComboBox->currentIndex() ) != mComboBox->currentText() )
894 if ( fieldIdx != -1 )
896 attributes.insert( fieldIdx, mComboBox->currentText() );
903 mAddEntryButton->setEnabled(
false );
907 void QgsRelationReferenceWidget::updateAddEntryButton()
909 mAddEntryButton->setVisible( mAllowAddFeatures );
910 mAddEntryButton->setEnabled( mReferencedLayer && mReferencedLayer->
isEditable() );
913 void QgsRelationReferenceWidget::disableChainedComboBoxes(
const QComboBox *scb )
915 QComboBox *ccb =
nullptr;
916 Q_FOREACH ( QComboBox *cb, mFilterComboBoxes )
928 cb->setCurrentIndex( 0 );
929 if ( ccb->currentIndex() == 0 )
931 cb->setEnabled(
false );
938 void QgsRelationReferenceWidget::emitForeignKeyChanged(
const QVariant &
foreignKey )
940 if ( foreignKey != mForeignKey || foreignKey.isNull() != mForeignKey.isNull() )
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
When showing a single feature (e.g. district information when looking at the form of a house) ...
Class for parsing and evaluation of expressions (formerly called "search strings").
void setIdentifierValue(const QVariant &identifierValue)
The identifier value of the currently selected feature.
Wrapper for iterator of features from vector data provider or vector layer.
This offers a combobox with autocompleter that allows selecting features from a layer.
A rectangle specified with double values.
QSet< QgsFeatureId > QgsFeatureIds
bool isValid() const
Returns the validity of this feature.
This class is a composition of two QSettings instances:
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer's CRS to output CRS
A form was opened as a new dialog.
A groupbox that collapses/expands when toggled and can save its collapsed and checked states...
This class contains context information for attribute editor widgets.
int nullIndex() const
Returns the current index of the NULL value, or -1 if NULL values are not allowed.
A class to represent a 2D point.
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
QgsVectorLayer referencingLayer
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
void setFillColor(const QColor &fillColor)
Fill color for the highlight.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
A bar for displaying non-blocking messages to the user.
static const QColor DEFAULT_HIGHLIGHT_COLOR
Default highlight color.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
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.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
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.
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
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.
void setBuffer(double buffer)
Set line / stroke buffer in millimeters.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets 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.
QgsFields fields() const FINAL
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...
QgsFeatureRequest currentFeatureRequest() const
Shorthand for getting a feature request to query the currently selected feature.
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
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.
int count() const
Returns number of items.
QgsVectorLayer referencedLayer
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
void editingStarted()
Is emitted, when editing on this layer has started.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
QString displayExpression
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else...
void zoomByFactor(double scaleFactor, const QgsPointXY *center=nullptr)
Zoom with the factor supplied.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it 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...
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
void setColor(const QColor &color)
Set line/stroke to color, polygon fill to color with alpha = 63.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
const QgsVectorLayerTools * vectorLayerTools() const
Returns the associated vector layer tools.
void setExtent(const QgsRectangle &r, bool magnified=false)
Sets the extent of the map canvas.
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
static const double DEFAULT_HIGHLIGHT_MIN_WIDTH_MM
Default highlight line/stroke minimum width in mm.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
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 value to match the typed text ...
void modelUpdated()
The underlying model has been updated.
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.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
void setMinWidth(double width)
Set minimum line / stroke width in millimeters.