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 );
159 void QgsRelationReferenceWidget::updateIndex()
161 if ( mChainFilters && mComboBox->count() > 0 )
166 if ( ! mFilterComboBoxes.isEmpty()
167 && mFilterComboBoxes[0]->currentIndex() == 0 && mAllowNull )
171 else if ( mComboBox->count() > mComboBox->
nullIndex() )
175 else if ( mAllowNull )
184 if ( mComboBox->count() > index )
186 mComboBox->setCurrentIndex( index );
193 mAllowNull = allowNullValue;
194 mRemoveFKButton->setVisible( allowNullValue && mReadOnlySelector );
198 mInvalidLabel->hide();
200 mRelation = relation;
202 mRelationName = relation.
name();
204 mReferencedField = relation.
fieldPairs().at( 0 ).second;
210 mAttributeEditorFrame->setObjectName( QStringLiteral(
"referencing/" ) + relation.
name() );
216 mAttributeEditorFrame->setTitle( mReferencedLayer->
name() );
218 mAttributeEditorLayout->addWidget( mReferencedAttributeForm );
223 updateAddEntryButton();
227 mInvalidLabel->show();
230 if ( mShown && isVisible() )
241 mFilterContainer->setEnabled( editable );
242 mComboBox->setEnabled( editable );
243 mComboBox->setEditable(
true );
244 mMapIdentificationButton->setEnabled( editable );
245 mRemoveFKButton->setEnabled( editable );
246 mIsEditable = editable;
251 if ( !value.isValid() )
255 if ( value.isNull() )
261 if ( !mReferencedLayer )
264 if ( mReadOnlySelector )
280 mForeignKey = mFeature.
attribute( mReferencedFieldIdx );
284 context.setFeature( mFeature );
285 QString title = expr.evaluate( &context ).toString();
286 if ( expr.hasEvalError() )
288 title = mFeature.
attribute( mReferencedFieldIdx ).toString();
290 mLineEdit->setText( title );
304 const int count = std::min( mFilterComboBoxes.size(), mFilterFields.size() );
305 for (
int i = 0; i < count; i++ )
307 QVariant v = mFeature.
attribute( mFilterFields[i] );
308 QString f = v.isNull() ? nullValue.toString() : v.toString();
309 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
314 mRemoveFKButton->setEnabled( mIsEditable );
315 highlightFeature( mFeature );
316 updateAttributeEditorFrame( mFeature );
324 if ( mChainFilters && !mFilterComboBoxes.isEmpty() )
326 QComboBox *cb = mFilterComboBoxes.first();
327 cb->setCurrentIndex( 0 );
328 disableChainedComboBoxes( cb );
331 if ( mReadOnlySelector )
338 nullText = tr(
"%1 (no selection)" ).arg( nullValue );
340 mLineEdit->setText( nullText );
341 mForeignKey = QVariant( QVariant::Int );
348 mRemoveFKButton->setEnabled(
false );
350 emitForeignKeyChanged( QVariant( QVariant::Int ) );
356 if ( mReferencedLayer )
359 if ( mReadOnlySelector )
374 if ( mReadOnlySelector )
380 whileBlocking( mComboBox )->setIdentifierValue( QVariant() );
382 mRemoveFKButton->setEnabled(
false );
388 if ( mReadOnlySelector )
400 mEditorContext = context;
402 mMessageBar = messageBar;
407 mMapTool->
setButton( mMapIdentificationButton );
414 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::MinimumExpanding );
415 mTopLayout->setAlignment( Qt::AlignTop );
418 mAttributeEditorFrame->setVisible( display );
419 mEmbedForm = display;
424 mChooserContainer->setHidden( readOnly );
425 mLineEdit->setVisible( readOnly );
426 mRemoveFKButton->setVisible( mAllowNull && readOnly );
427 mReadOnlySelector = readOnly;
432 mHighlightFeatureButton->setVisible( allowMapIdentification );
433 mMapIdentificationButton->setVisible( allowMapIdentification );
444 mFilterFields = filterFields;
449 mOpenFormButton->setVisible( openFormButtonVisible );
469 if ( !mReadOnlySelector && mReferencedLayer )
471 QApplication::setOverrideCursor( Qt::WaitCursor );
473 QSet<QString> requestedAttrs;
475 if ( !mFilterFields.isEmpty() )
477 for (
const QString &fieldName : qgis::as_const( mFilterFields ) )
484 QComboBox *cb =
new QComboBox();
485 cb->setProperty(
"Field", fieldName );
487 mFilterComboBoxes << cb;
488 QVariantList uniqueValues = mReferencedLayer->
uniqueValues( idx ).toList();
491 cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->
fields().
at( idx ).
type() ) );
494 Q_FOREACH (
const QVariant &v, uniqueValues )
496 cb->addItem( v.toString(), v );
499 connect( cb,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::filterChanged );
502 requestedAttrs << fieldName;
504 mFilterLayout->addWidget( cb );
515 const int count = std::min( mFilterComboBoxes.count(), mFilterFields.count() );
516 for (
int i = 0; i < count - 1; i++ )
518 QVariant cv = ft.
attribute( mFilterFields.at( i ) );
519 QVariant nv = ft.
attribute( mFilterFields.at( i + 1 ) );
520 QString cf = cv.isNull() ? nullValue.toString() : cv.toString();
521 QString nf = nv.isNull() ? nullValue.toString() : nv.toString();
522 mFilterCache[mFilterFields[i]][cf] << nf;
526 if ( !mFilterComboBoxes.isEmpty() )
528 QComboBox *cb = mFilterComboBoxes.first();
529 cb->setCurrentIndex( 0 );
530 disableChainedComboBoxes( cb );
536 mFilterContainer->hide();
546 if ( mChainFilters && mFeature.
isValid() )
548 for (
int i = 0; i < mFilterFields.size(); i++ )
550 QVariant v = mFeature.
attribute( mFilterFields[i] );
551 QString f = v.isNull() ? nullValue.toString() : v.toString();
552 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
557 connect( mComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::comboReferenceChanged );
558 updateAttributeEditorFrame( mFeature );
559 QApplication::restoreOverrideCursor();
563 void QgsRelationReferenceWidget::highlightActionTriggered( QAction *action )
565 if ( action == mHighlightFeatureAction )
569 else if ( action == mScaleHighlightFeatureAction )
573 else if ( action == mPanHighlightFeatureAction )
588 attributeDialog.exec();
611 if ( canvasExtent ==
Scale )
624 else if ( canvasExtent ==
Pan )
634 mHighlight =
new QgsHighlight( mCanvas, f, mReferencedLayer );
642 color.setAlpha( alpha );
648 QTimer *timer =
new QTimer(
this );
649 timer->setSingleShot(
true );
650 connect( timer, &QTimer::timeout,
this, &QgsRelationReferenceWidget::deleteHighlight );
651 timer->start( 3000 );
654 void QgsRelationReferenceWidget::deleteHighlight()
661 mHighlight =
nullptr;
666 if ( !mAllowMapIdentification || !mReferencedLayer )
675 mMapTool->
setLayer( mReferencedLayer );
678 mWindowWidget = window();
680 mCanvas->window()->raise();
681 mCanvas->activateWindow();
689 QString title = tr(
"Relation %1 for %2." ).arg( mRelationName, mReferencingLayer->
name() );
690 QString msg = tr(
"Identify a feature of %1 to be associated. Press <ESC> to cancel." ).arg( mReferencedLayer->
name() );
692 mMessageBar->
pushItem( mMessageBarItem );
696 void QgsRelationReferenceWidget::comboReferenceChanged(
int index )
700 highlightFeature( mFeature );
701 updateAttributeEditorFrame( mFeature );
706 void QgsRelationReferenceWidget::updateAttributeEditorFrame(
const QgsFeature &feature )
708 mOpenFormButton->setEnabled( feature.
isValid() );
710 if ( mAttributeEditorFrame && mReferencedAttributeForm )
712 mReferencedAttributeForm->
setFeature( feature );
718 return mAllowAddFeatures;
724 updateAddEntryButton();
727 void QgsRelationReferenceWidget::featureIdentified(
const QgsFeature &feature )
729 if ( mReadOnlySelector )
733 context.setFeature( feature );
734 QString title = expr.evaluate( &context ).toString();
735 if ( expr.hasEvalError() )
737 title = feature.
attribute( mReferencedFieldIdx ).toString();
739 mLineEdit->setText( title );
740 mForeignKey = feature.
attribute( mReferencedFieldIdx );
749 mRemoveFKButton->setEnabled( mIsEditable );
750 highlightFeature( feature );
751 updateAttributeEditorFrame( feature );
757 void QgsRelationReferenceWidget::unsetMapTool()
760 if ( mCanvas && mMapTool )
767 void QgsRelationReferenceWidget::mapToolDeactivated()
771 mWindowWidget->raise();
772 mWindowWidget->activateWindow();
775 if ( mMessageBar && mMessageBarItem )
777 mMessageBar->
popWidget( mMessageBarItem );
779 mMessageBarItem =
nullptr;
782 void QgsRelationReferenceWidget::filterChanged()
786 QMap<QString, QString> filters;
789 QComboBox *scb = qobject_cast<QComboBox *>( sender() );
795 QString filterExpression;
799 disableChainedComboBoxes( scb );
802 Q_FOREACH ( QComboBox *cb, mFilterComboBoxes )
804 if ( cb->currentIndex() != 0 )
806 const QString fieldName = cb->property(
"Field" ).toString();
808 if ( cb->currentText() == nullValue.toString() )
810 filters[fieldName] = QStringLiteral(
"\"%1\" IS NULL" ).arg( fieldName );
822 QComboBox *ccb =
nullptr;
823 Q_FOREACH ( QComboBox *cb, mFilterComboBoxes )
833 if ( ccb->currentIndex() != 0 )
835 const QString fieldName = cb->property(
"Field" ).toString();
837 cb->blockSignals(
true );
839 cb->addItem( cb->property(
"FieldAlias" ).toString() );
844 Q_FOREACH (
const QString &txt, mFilterCache[ccb->property(
"Field" ).toString()][ccb->currentText()] )
846 QMap<QString, QString> filtersAttrs = filters;
848 QString expression = filtersAttrs.values().join( QStringLiteral(
" AND " ) );
856 while ( it.nextFeature( f ) )
858 if ( !featureIds.contains( f.
id() ) )
859 featureIds << f.
id();
870 cb->addItems( texts );
872 cb->setEnabled(
true );
873 cb->blockSignals(
false );
879 filterExpression = filters.values().join( QStringLiteral(
" AND " ) );
883 void QgsRelationReferenceWidget::addEntry()
889 if ( mComboBox->itemText( mComboBox->currentIndex() ) != mComboBox->currentText() )
893 if ( fieldIdx != -1 )
895 attributes.insert( fieldIdx, mComboBox->currentText() );
902 mAddEntryButton->setEnabled(
false );
906 void QgsRelationReferenceWidget::updateAddEntryButton()
908 mAddEntryButton->setVisible( mAllowAddFeatures );
909 mAddEntryButton->setEnabled( mReferencedLayer && mReferencedLayer->
isEditable() );
912 void QgsRelationReferenceWidget::disableChainedComboBoxes(
const QComboBox *scb )
914 QComboBox *ccb =
nullptr;
915 Q_FOREACH ( QComboBox *cb, mFilterComboBoxes )
927 cb->setCurrentIndex( 0 );
928 if ( ccb->currentIndex() == 0 )
930 cb->setEnabled(
false );
937 void QgsRelationReferenceWidget::emitForeignKeyChanged(
const QVariant &
foreignKey )
939 if ( foreignKey != mForeignKey || foreignKey.isNull() != mForeignKey.isNull() )
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.
Class for parsing and evaluation of expressions (formerly called "search strings").
void setIdentifierValue(const QVariant &identifierValue)
The identifier value of the currently selected feature.
const QgsVectorLayerTools * vectorLayerTools() const
Returns the associated vector layer tools.
Wrapper for iterator of features from vector data provider or vector layer.
bool contains(const QgsRectangle &rect) const
Returns 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.
int nullIndex() const
Returns the current index of the NULL value, or -1 if NULL values are not allowed.
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...
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
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
Returns 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
Gets 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)
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.
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.
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
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
Gets access to properties used for map rendering.
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...
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.
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)
Sets 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 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.
void setMinWidth(double width)
Set minimum line / stroke width in millimeters.