18 #include <QPushButton> 20 #include <QHBoxLayout> 49 mTopLayout =
new QVBoxLayout(
this );
50 mTopLayout->setContentsMargins( 0, 0, 0, 0 );
52 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::Fixed );
54 setLayout( mTopLayout );
56 QHBoxLayout *editLayout =
new QHBoxLayout();
57 editLayout->setContentsMargins( 0, 0, 0, 0 );
58 editLayout->setSpacing( 2 );
61 mChooserContainer =
new QWidget;
62 editLayout->addWidget( mChooserContainer );
63 QHBoxLayout *chooserLayout =
new QHBoxLayout;
64 chooserLayout->setContentsMargins( 0, 0, 0, 0 );
65 mFilterLayout =
new QHBoxLayout;
66 mFilterLayout->setContentsMargins( 0, 0, 0, 0 );
67 mFilterContainer =
new QWidget;
68 mFilterContainer->setLayout( mFilterLayout );
69 mChooserContainer->setLayout( chooserLayout );
70 chooserLayout->addWidget( mFilterContainer );
73 mChooserContainer->layout()->addWidget( mComboBox );
76 mLineEdit =
new QLineEdit();
77 mLineEdit->setReadOnly(
true );
78 editLayout->addWidget( mLineEdit );
81 mOpenFormButton =
new QToolButton();
83 mOpenFormButton->setText( tr(
"Open Related Feature Form" ) );
84 editLayout->addWidget( mOpenFormButton );
86 mAddEntryButton =
new QToolButton();
88 mAddEntryButton->setText( tr(
"Add New Entry" ) );
89 editLayout->addWidget( mAddEntryButton );
92 mHighlightFeatureButton =
new QToolButton(
this );
93 mHighlightFeatureButton->setPopupMode( QToolButton::MenuButtonPopup );
94 mHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionHighlightFeature.svg" ) ), tr(
"Highlight feature" ),
this );
95 mScaleHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionScaleHighlightFeature.svg" ) ), tr(
"Scale and highlight feature" ),
this );
96 mPanHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionPanHighlightFeature.svg" ) ), tr(
"Pan and highlight feature" ),
this );
97 mHighlightFeatureButton->addAction( mHighlightFeatureAction );
98 mHighlightFeatureButton->addAction( mScaleHighlightFeatureAction );
99 mHighlightFeatureButton->addAction( mPanHighlightFeatureAction );
100 mHighlightFeatureButton->setDefaultAction( mHighlightFeatureAction );
101 editLayout->addWidget( mHighlightFeatureButton );
104 mMapIdentificationButton =
new QToolButton(
this );
106 mMapIdentificationButton->setText( tr(
"Select on Map" ) );
107 mMapIdentificationButton->setCheckable(
true );
108 editLayout->addWidget( mMapIdentificationButton );
111 mRemoveFKButton =
new QToolButton(
this );
113 mRemoveFKButton->setText( tr(
"No Selection" ) );
114 editLayout->addWidget( mRemoveFKButton );
117 mTopLayout->addLayout( editLayout );
121 mAttributeEditorLayout =
new QVBoxLayout( mAttributeEditorFrame );
122 mAttributeEditorFrame->setLayout( mAttributeEditorLayout );
123 mAttributeEditorFrame->setSizePolicy( mAttributeEditorFrame->sizePolicy().horizontalPolicy(), QSizePolicy::Expanding );
124 mTopLayout->addWidget( mAttributeEditorFrame );
127 mInvalidLabel =
new QLabel( tr(
"The relation is not valid. Please make sure your relation definitions are OK." ) );
128 mInvalidLabel->setWordWrap(
true );
129 QFont font = mInvalidLabel->font();
130 font.setItalic(
true );
131 mInvalidLabel->setStyleSheet( QStringLiteral(
"QLabel { color: red; } " ) );
132 mInvalidLabel->setFont( font );
133 mTopLayout->addWidget( mInvalidLabel );
137 mMapIdentificationButton->hide();
138 mHighlightFeatureButton->hide();
139 mAttributeEditorFrame->hide();
140 mInvalidLabel->hide();
144 connect( mHighlightFeatureButton, &QToolButton::triggered,
this, &QgsRelationReferenceWidget::highlightActionTriggered );
147 connect( mAddEntryButton, &QAbstractButton::clicked,
this, &QgsRelationReferenceWidget::addEntry );
148 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;
406 mMapTool->
setButton( mMapIdentificationButton );
413 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::MinimumExpanding );
414 mTopLayout->setAlignment( Qt::AlignTop );
417 mAttributeEditorFrame->setVisible( display );
418 mEmbedForm = display;
423 mChooserContainer->setHidden( readOnly );
424 mLineEdit->setVisible( readOnly );
425 mRemoveFKButton->setVisible( mAllowNull && readOnly );
426 mReadOnlySelector = readOnly;
431 mHighlightFeatureButton->setVisible( allowMapIdentification );
432 mMapIdentificationButton->setVisible( allowMapIdentification );
443 mFilterFields = filterFields;
448 mOpenFormButton->setVisible( openFormButtonVisible );
468 if ( !mReadOnlySelector && mReferencedLayer )
470 QApplication::setOverrideCursor( Qt::WaitCursor );
472 QSet<QString> requestedAttrs;
474 if ( !mFilterFields.isEmpty() )
476 for (
const QString &fieldName : qgis::as_const( mFilterFields ) )
483 QComboBox *cb =
new QComboBox();
484 cb->setProperty(
"Field", fieldName );
486 mFilterComboBoxes << cb;
487 QVariantList uniqueValues = mReferencedLayer->
uniqueValues( idx ).toList();
490 cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->
fields().
at( idx ).
type() ) );
493 Q_FOREACH (
const QVariant &v, uniqueValues )
495 cb->addItem( v.toString(), v );
498 connect( cb,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::filterChanged );
501 requestedAttrs << fieldName;
503 mFilterLayout->addWidget( cb );
514 const int count = std::min( mFilterComboBoxes.count(), mFilterFields.count() );
515 for (
int i = 0; i < count - 1; i++ )
517 QVariant cv = ft.
attribute( mFilterFields.at( i ) );
518 QVariant nv = ft.
attribute( mFilterFields.at( i + 1 ) );
519 QString cf = cv.isNull() ? nullValue.toString() : cv.toString();
520 QString nf = nv.isNull() ? nullValue.toString() : nv.toString();
521 mFilterCache[mFilterFields[i]][cf] << nf;
525 if ( !mFilterComboBoxes.isEmpty() )
527 QComboBox *cb = mFilterComboBoxes.first();
528 cb->setCurrentIndex( 0 );
529 disableChainedComboBoxes( cb );
535 mFilterContainer->hide();
545 if ( mChainFilters && mFeature.
isValid() )
547 for (
int i = 0; i < mFilterFields.size(); i++ )
549 QVariant v = mFeature.
attribute( mFilterFields[i] );
550 QString f = v.isNull() ? nullValue.toString() : v.toString();
551 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
556 connect( mComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::comboReferenceChanged );
558 emit mComboBox->currentIndexChanged( mComboBox->currentIndex() );
560 QApplication::restoreOverrideCursor();
566 void QgsRelationReferenceWidget::highlightActionTriggered( QAction *action )
568 if ( action == mHighlightFeatureAction )
572 else if ( action == mScaleHighlightFeatureAction )
576 else if ( action == mPanHighlightFeatureAction )
591 attributeDialog.exec();
614 if ( canvasExtent ==
Scale )
627 else if ( canvasExtent ==
Pan )
637 mHighlight =
new QgsHighlight( mCanvas, f, mReferencedLayer );
645 color.setAlpha( alpha );
651 QTimer *timer =
new QTimer(
this );
652 timer->setSingleShot(
true );
653 connect( timer, &QTimer::timeout,
this, &QgsRelationReferenceWidget::deleteHighlight );
654 timer->start( 3000 );
657 void QgsRelationReferenceWidget::deleteHighlight()
664 mHighlight =
nullptr;
669 if ( !mAllowMapIdentification || !mReferencedLayer )
678 mMapTool->
setLayer( mReferencedLayer );
681 mWindowWidget = window();
683 mCanvas->window()->raise();
684 mCanvas->activateWindow();
692 QString title = tr(
"Relation %1 for %2." ).arg( mRelationName, mReferencingLayer->
name() );
693 QString msg = tr(
"Identify a feature of %1 to be associated. Press <ESC> to cancel." ).arg( mReferencedLayer->
name() );
695 mMessageBar->
pushItem( mMessageBarItem );
699 void QgsRelationReferenceWidget::comboReferenceChanged(
int index )
703 highlightFeature( mFeature );
704 updateAttributeEditorFrame( mFeature );
709 void QgsRelationReferenceWidget::updateAttributeEditorFrame(
const QgsFeature &feature )
711 mOpenFormButton->setEnabled( feature.
isValid() );
713 if ( mAttributeEditorFrame && mReferencedAttributeForm )
715 mReferencedAttributeForm->
setFeature( feature );
721 return mAllowAddFeatures;
727 updateAddEntryButton();
730 void QgsRelationReferenceWidget::featureIdentified(
const QgsFeature &feature )
732 if ( mReadOnlySelector )
736 context.setFeature( feature );
737 QString title = expr.evaluate( &context ).toString();
738 if ( expr.hasEvalError() )
740 title = feature.
attribute( mReferencedFieldIdx ).toString();
742 mLineEdit->setText( title );
743 mForeignKey = feature.
attribute( mReferencedFieldIdx );
752 mRemoveFKButton->setEnabled( mIsEditable );
753 highlightFeature( feature );
754 updateAttributeEditorFrame( feature );
760 void QgsRelationReferenceWidget::unsetMapTool()
763 if ( mCanvas && mMapTool )
770 void QgsRelationReferenceWidget::mapToolDeactivated()
774 mWindowWidget->raise();
775 mWindowWidget->activateWindow();
778 if ( mMessageBar && mMessageBarItem )
780 mMessageBar->
popWidget( mMessageBarItem );
782 mMessageBarItem =
nullptr;
785 void QgsRelationReferenceWidget::filterChanged()
789 QMap<QString, QString> filters;
792 QComboBox *scb = qobject_cast<QComboBox *>( sender() );
798 QString filterExpression;
802 disableChainedComboBoxes( scb );
805 Q_FOREACH ( QComboBox *cb, mFilterComboBoxes )
807 if ( cb->currentIndex() != 0 )
809 const QString fieldName = cb->property(
"Field" ).toString();
811 if ( cb->currentText() == nullValue.toString() )
813 filters[fieldName] = QStringLiteral(
"\"%1\" IS NULL" ).arg( fieldName );
825 QComboBox *ccb =
nullptr;
826 Q_FOREACH ( QComboBox *cb, mFilterComboBoxes )
836 if ( ccb->currentIndex() != 0 )
838 const QString fieldName = cb->property(
"Field" ).toString();
840 cb->blockSignals(
true );
842 cb->addItem( cb->property(
"FieldAlias" ).toString() );
847 Q_FOREACH (
const QString &txt, mFilterCache[ccb->property(
"Field" ).toString()][ccb->currentText()] )
849 QMap<QString, QString> filtersAttrs = filters;
851 QString expression = filtersAttrs.values().join( QStringLiteral(
" AND " ) );
859 while ( it.nextFeature( f ) )
861 if ( !featureIds.contains( f.
id() ) )
862 featureIds << f.
id();
873 cb->addItems( texts );
875 cb->setEnabled(
true );
876 cb->blockSignals(
false );
882 filterExpression = filters.values().join( QStringLiteral(
" AND " ) );
886 void QgsRelationReferenceWidget::addEntry()
892 if ( mComboBox->itemText( mComboBox->currentIndex() ) != mComboBox->currentText() )
896 if ( fieldIdx != -1 )
898 attributes.insert( fieldIdx, mComboBox->currentText() );
905 mAddEntryButton->setEnabled(
false );
909 void QgsRelationReferenceWidget::updateAddEntryButton()
911 mAddEntryButton->setVisible( mAllowAddFeatures );
912 mAddEntryButton->setEnabled( mReferencedLayer && mReferencedLayer->
isEditable() );
915 void QgsRelationReferenceWidget::disableChainedComboBoxes(
const QComboBox *scb )
917 QComboBox *ccb =
nullptr;
918 Q_FOREACH ( QComboBox *cb, mFilterComboBoxes )
930 cb->setCurrentIndex( 0 );
931 if ( ccb->currentIndex() == 0 )
933 cb->setEnabled(
false );
940 void QgsRelationReferenceWidget::emitForeignKeyChanged(
const QVariant &
foreignKey )
942 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
Looks 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.
QSet< QgsFeatureId > QgsFeatureIds
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.
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.
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.
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.
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.
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 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...
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.
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...
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 the contents of the geometry as a 2-dimensional point.
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.
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.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request.
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 ...
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.