18 #include <QPushButton> 20 #include <QHBoxLayout> 41 , mMessageBar( nullptr )
43 , mReferencedFieldIdx( -1 )
44 , mReferencingFieldIdx( -1 )
46 , mHighlight( nullptr )
48 , mMessageBarItem( nullptr )
50 , mReferencedAttributeForm( nullptr )
51 , mReferencedLayer( nullptr )
52 , mReferencingLayer( nullptr )
53 , mMasterModel( nullptr )
54 , mFilterModel( nullptr )
55 , mFeatureListModel( nullptr )
56 , mWindowWidget( nullptr )
60 , mReadOnlySelector( false )
61 , mAllowMapIdentification( false )
62 , mOrderByValue( false )
63 , mOpenFormButtonVisible( true )
64 , mChainFilters( false )
65 , mAllowAddFeatures( false )
79 mChooserContainer =
new QWidget;
80 editLayout->
addWidget( mChooserContainer );
86 mFilterContainer->
setLayout( mFilterLayout );
87 mChooserContainer->
setLayout( chooserLayout );
88 chooserLayout->
addWidget( mFilterContainer );
102 mOpenFormButton->
setText(
tr(
"Open related feature form" ) );
103 editLayout->
addWidget( mOpenFormButton );
107 mAddEntryButton->
setText(
tr(
"Add new entry" ) );
108 editLayout->
addWidget( mAddEntryButton );
112 mHighlightFeatureButton->
setPopupMode( QToolButton::MenuButtonPopup );
116 mHighlightFeatureButton->
addAction( mHighlightFeatureAction );
117 mHighlightFeatureButton->
addAction( mScaleHighlightFeatureAction );
118 mHighlightFeatureButton->
addAction( mPanHighlightFeatureAction );
120 editLayout->
addWidget( mHighlightFeatureButton );
123 mMapIdentificationButton =
new QToolButton(
this );
125 mMapIdentificationButton->
setText(
tr(
"Select on map" ) );
127 editLayout->
addWidget( mMapIdentificationButton );
132 mRemoveFKButton->
setText(
tr(
"No selection" ) );
133 editLayout->
addWidget( mRemoveFKButton );
140 mAttributeEditorLayout =
new QVBoxLayout( mAttributeEditorFrame );
141 mAttributeEditorFrame->
setLayout( mAttributeEditorLayout );
142 mAttributeEditorFrame->
setSizePolicy( mAttributeEditorFrame->
sizePolicy().horizontalPolicy(), QSizePolicy::Expanding );
143 mTopLayout->
addWidget( mAttributeEditorFrame );
146 mInvalidLabel =
new QLabel(
tr(
"The relation is not valid. Please make sure your relation definitions are ok." ) );
151 mInvalidLabel->
setFont( font );
156 mMapIdentificationButton->
hide();
157 mHighlightFeatureButton->
hide();
158 mAttributeEditorFrame->
hide();
159 mInvalidLabel->
hide();
162 connect( mOpenFormButton, SIGNAL( clicked() ),
this, SLOT(
openForm() ) );
163 connect( mHighlightFeatureButton, SIGNAL( triggered(
QAction* ) ),
this, SLOT( highlightActionTriggered(
QAction* ) ) );
166 connect( mAddEntryButton, SIGNAL( clicked(
bool ) ),
this, SLOT( addEntry() ) );
167 connect( mComboBox, SIGNAL( editTextChanged(
QString ) ),
this, SLOT( editTextUpdated(
const QString & ) ) );
180 mAllowNull = allowNullValue;
181 mRemoveFKButton->
setVisible( allowNullValue && mReadOnlySelector );
185 mInvalidLabel->
hide();
187 mRelation = relation;
189 mRelationName = relation.
name();
191 mReferencedFieldIdx = mReferencedLayer->fieldNameIndex( relation.
fieldPairs().at( 0 ).second );
192 mReferencingFieldIdx = mReferencingLayer->fieldNameIndex( relation.
fieldPairs().at( 0 ).first );
198 mAttributeEditorFrame->
setTitle( mReferencedLayer->name() );
200 mAttributeEditorLayout->
addWidget( mReferencedAttributeForm );
203 connect( mReferencedLayer, SIGNAL( editingStarted() ),
this, SLOT( updateAddEntryButton() ) );
204 connect( mReferencedLayer, SIGNAL( editingStopped() ),
this, SLOT( updateAddEntryButton() ) );
205 updateAddEntryButton();
209 mInvalidLabel->
show();
226 mMapIdentificationButton->
setEnabled( editable );
228 mIsEditable = editable;
243 if ( !mReferencedLayer )
249 attrs[ mReferencingLayer->fieldNameIndex( mRelation.
fieldPairs().at( 0 ).first )] = value;
253 mReferencedLayer->getFeatures( request ).nextFeature( mFeature );
260 mForeignKey = mFeature.
attribute( mReferencedFieldIdx );
262 if ( mReadOnlySelector )
269 context.setFeature( mFeature );
270 QString title = expr.evaluate( &context ).toString();
271 if ( expr.hasEvalError() )
281 if ( mChainFilters && mFeature.
isValid() && mFilterComboBoxes.
count() >= mFilterFields.
count() )
285 for (
int i = 0; i < mFilterFields.
size(); i++ )
289 mFilterComboBoxes.
at( i )->setCurrentIndex( mFilterComboBoxes.
at( i )->findText( f ) );
294 if ( i == -1 && mAllowNull )
305 highlightFeature( mFeature );
306 updateAttributeEditorFrame( mFeature );
315 if ( mChainFilters && !mFilterComboBoxes.
isEmpty() )
319 disableChainedComboBoxes( cb );
322 if ( mReadOnlySelector )
327 nullText =
tr(
"%1 (no selection)" ).
arg( nullValue.
toString() );
329 mLineEdit->
setText( nullText );
352 if ( mReferencedLayer )
355 if ( mReadOnlySelector )
363 mReferencedLayer->getFeatures(
QgsFeatureRequest().setFilterFid( fid ) ).nextFeature( f );
370 if ( mReadOnlySelector )
384 if ( mReadOnlySelector )
390 if ( mReferencingFieldIdx < 0 || mReferencingFieldIdx >= mReferencingLayer->fields().count() )
394 else if ( !mFeature.
isValid() )
396 return QVariant( mReferencingLayer->fields().at( mReferencingFieldIdx ).type() );
400 return mFeature.
attribute( mReferencedFieldIdx );
407 mEditorContext = context;
409 mMessageBar = messageBar;
414 mMapTool->
setButton( mMapIdentificationButton );
426 mEmbedForm = display;
431 mChooserContainer->
setHidden( readOnly );
433 mRemoveFKButton->
setVisible( mAllowNull && readOnly );
434 mReadOnlySelector = readOnly;
439 mHighlightFeatureButton->
setVisible( allowMapIdentification );
440 mMapIdentificationButton->
setVisible( allowMapIdentification );
451 mFilterFields = filterFields;
456 mOpenFormButton->
setVisible( openFormButtonVisible );
476 if ( !mReadOnlySelector && mComboBox->
count() == 0 && mReferencedLayer )
482 const int cacheSize =
QSettings().
value(
"/QgsRelationReferenceWidget/cacheSize" ).
toInt();
485 if ( !mFilterFields.
isEmpty() )
487 Q_FOREACH (
const QString& fieldName, mFilterFields )
489 QVariantList uniqueValues;
490 int idx = mReferencedLayer->fieldNameIndex( fieldName );
497 cb->
setProperty(
"FieldAlias", mReferencedLayer->attributeDisplayName( idx ) );
498 mFilterComboBoxes << cb;
499 mReferencedLayer->uniqueValues( idx, uniqueValues );
500 cb->addItem( mReferencedLayer->attributeDisplayName( idx ) );
502 cb->addItem( nullValue.
toString(),
QVariant( mReferencedLayer->fields().at( idx ).type() ) );
505 Q_FOREACH (
const QVariant& v, uniqueValues )
510 connect( cb, SIGNAL( currentIndexChanged(
int ) ),
this, SLOT( filterChanged() ) );
513 requestedAttrs << fieldName;
526 for (
int i = 0; i < mFilterComboBoxes.
count() - 1; ++i )
532 mFilterCache[mFilterFields[i]][cf] << nf;
536 if ( !mFilterComboBoxes.
isEmpty() )
540 disableChainedComboBoxes( cb );
546 mFilterContainer->
hide();
551 requestedAttrs += exp.referencedColumns().toSet();
552 requestedAttrs << mRelation.
fieldPairs().at( 0 ).second;
555 Q_FOREACH (
const QString& attr, requestedAttrs )
556 attributes << mReferencedLayer->fieldNameIndex( attr );
570 mFilterModel->
sort( mReferencedLayer->displayExpression() );
573 mComboBox->
setModel( mFeatureListModel );
577 if ( mChainFilters && mFeature.
isValid() )
579 for (
int i = 0; i < mFilterFields.
size(); i++ )
583 mFilterComboBoxes.
at( i )->setCurrentIndex( mFilterComboBoxes.
at( i )->findText( f ) );
597 connect( mComboBox, SIGNAL( currentIndexChanged(
int ) ),
this, SLOT( comboReferenceChanged(
int ) ) );
598 updateAttributeEditorFrame( mFeature );
603 void QgsRelationReferenceWidget::highlightActionTriggered(
QAction* action )
605 if ( action == mHighlightFeatureAction )
609 else if ( action == mScaleHighlightFeatureAction )
613 else if ( action == mPanHighlightFeatureAction )
628 attributeDialog.
exec();
651 if ( canvasExtent ==
Scale )
664 else if ( canvasExtent ==
Pan )
675 mHighlight =
new QgsHighlight( mCanvas, f, mReferencedLayer );
691 connect( timer, SIGNAL( timeout() ),
this, SLOT( deleteHighlight() ) );
692 timer->
start( 3000 );
695 void QgsRelationReferenceWidget::deleteHighlight()
702 mHighlight =
nullptr;
707 if ( !mAllowMapIdentification || !mReferencedLayer )
716 mMapTool->
setLayer( mReferencedLayer );
726 connect( mMapTool, SIGNAL( deactivated() ),
this, SLOT( mapToolDeactivated() ) );
730 QString title =
tr(
"Relation %1 for %2." ).
arg( mRelationName, mReferencingLayer->name() );
731 QString msg =
tr(
"Identify a feature of %1 to be associated. Press <ESC> to cancel." ).
arg( mReferencedLayer->name() );
733 mMessageBar->
pushItem( mMessageBarItem );
737 void QgsRelationReferenceWidget::comboReferenceChanged(
int index )
740 mReferencedLayer->getFeatures(
QgsFeatureRequest().setFilterFid( fid ) ).nextFeature( mFeature );
741 highlightFeature( mFeature );
742 updateAttributeEditorFrame( mFeature );
746 void QgsRelationReferenceWidget::updateAttributeEditorFrame(
const QgsFeature& feature )
750 if ( mAttributeEditorFrame && mReferencedAttributeForm )
752 mReferencedAttributeForm->
setFeature( feature );
758 return mAllowAddFeatures;
764 updateAddEntryButton();
767 void QgsRelationReferenceWidget::featureIdentified(
const QgsFeature& feature )
769 if ( mReadOnlySelector )
776 context.setFeature( feature );
777 QString title = expr.evaluate( &context ).toString();
778 if ( expr.hasEvalError() )
783 mForeignKey = feature.
attribute( mReferencedFieldIdx );
793 highlightFeature( feature );
794 updateAttributeEditorFrame( feature );
800 void QgsRelationReferenceWidget::unsetMapTool()
803 if ( mCanvas && mMapTool )
810 void QgsRelationReferenceWidget::mapToolDeactivated()
814 mWindowWidget->
raise();
818 if ( mMessageBar && mMessageBarItem )
820 mMessageBar->
popWidget( mMessageBarItem );
822 mMessageBarItem =
nullptr;
825 void QgsRelationReferenceWidget::filterChanged()
842 disableChainedComboBoxes( scb );
845 Q_FOREACH (
QComboBox *cb, mFilterComboBoxes )
853 filters[fieldName] =
QString(
"\"%1\" IS NULL" ).
arg( fieldName );
859 attrs << mReferencedLayer->fieldNameIndex( fieldName );
863 bool filtered =
false;
867 Q_FOREACH (
QComboBox* cb, mFilterComboBoxes )
897 subset << mReferencedLayer->fieldNameIndex( fieldName );
902 while ( it.nextFeature( f ) )
905 featureIds << f.
id();
926 if ( !mChainFilters || ( mChainFilters && !filtered ) )
929 filterExpression = vals.
join(
QString(
" AND " ) );
932 if ( !filterExpression.
isEmpty() )
937 while ( it.nextFeature( f ) )
939 featureIds << f.
id();
945 if ( mChainFilters && mComboBox->
count() > 0 )
951 else if ( mComboBox->
count() > 1 )
958 void QgsRelationReferenceWidget::addEntry()
966 int fieldIdx = mReferencedLayer->fieldNameIndex( mReferencedLayer->displayExpression() );
968 if ( fieldIdx != -1 )
970 attributes.insert( fieldIdx, mComboBox->
currentText() );
982 void QgsRelationReferenceWidget::updateAddEntryButton()
984 mAddEntryButton->
setVisible( mAllowAddFeatures );
985 mAddEntryButton->
setEnabled( mReferencedLayer && mReferencedLayer->isEditable() );
988 void QgsRelationReferenceWidget::disableChainedComboBoxes(
const QComboBox *scb )
991 Q_FOREACH (
QComboBox *cb, mFilterComboBoxes )
1013 void QgsRelationReferenceWidget::editTextUpdated(
const QString &text )
1015 updateAddEntryButton();
1019 if ( text.
isEmpty() && mAllowNull )
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) ...
bool isValid() const
Returns the validity of this feature.
void setRequest(const QgsFeatureRequest &request)
Set a request that will be used to fill this attribute table model.
Class for parsing and evaluation of expressions (formerly called "search strings").
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
bool isValid() const
Returns the validity of this relation.
static double DEFAULT_HIGHLIGHT_BUFFER_MM
Default highlight buffer in mm.
A rectangle specified with double values.
static const QColor DEFAULT_HIGHLIGHT_COLOR
Default highlight color.
void setContentsMargins(int left, int top, int right, int bottom)
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before using this model as source for any oth...
QgsPoint asPoint() const
Return contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
QList< T > values() const
bool setDisplayExpression(const QString &expression)
A form was opened as a new dialog.
void setText(const QString &)
A groupbox that collapses/expands when toggled and can save its collapsed and checked states...
This class contains context information for attribute editor widgets.
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
const T & at(int i) const
void scale(double scaleFactor, const QgsPoint *c=nullptr)
Scale the rectangle around its center point.
void setFillColor(const QColor &fillColor)
Set polygons fill color.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
A bar for displaying non-blocking messages to the user.
void refresh()
Repaints the canvas map.
A geometry is the spatial representation of a feature.
QString itemText(int index) const
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
QString join(const QString &separator) const
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
A model backed by a QgsVectorLayerCache which is able to provide feature/attribute information to a Q...
void setEditable(bool editable)
QString tr(const char *sourceText, const char *disambiguation, int n)
Map canvas is a class for displaying all GIS data types on a canvas.
void setCacheSubsetOfAttributes(const QgsAttributeList &attributes)
Set the subset of attributes to be cached.
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
QgsGeometry * centroid() const
Returns the center of mass of a geometry.
virtual void setFilteredFeatures(const QgsFeatureIds &ids)
Specify a list of features, which the filter will accept.
Get the feature id of the feature in this row.
void addItem(const QString &text, const QVariant &userData)
void setMapTool(QgsMapTool *mapTool)
Sets the map tool currently being used on the canvas.
void setBuffer(double buffer)
Set line / outline buffer in millimeters.
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=nullptr)
make out a widget containing a message to be displayed on the bar
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
int count(const T &value) const
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QVariant property(const char *name) const
int toInt(bool *ok) const
bool popWidget(QgsMessageBarItem *item)
Remove the passed widget from the bar (if previously added), then display the next one in the stack i...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsRectangle extent() const
Returns the current zoom exent of the map canvas.
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)...
void setOverrideCursor(const QCursor &cursor)
QgsVectorLayerCache * layerCache() const
Returns the layer cache this model uses as backend.
void restoreOverrideCursor()
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
A class to represent a point.
QVariant itemData(int index, int role) const
QgsFeatureId id() const
Get the feature ID for this feature.
QList< FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
bool blockSignals(bool block)
QgsVectorLayer * referencedLayer() const
Access the referenced (parent) layer.
This class caches features of a given QgsVectorLayer.
void setItalic(bool enable)
bool contains(const T &value) const
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 setValid(bool validity)
Sets the validity of the feature.
bool setAlignment(QWidget *w, QFlags< Qt::AlignmentFlag > alignment)
QVariant value(const QString &key, const QVariant &defaultValue) const
QgsVectorLayer * referencingLayer() const
Access the referencing (child) layer This is the layer which has the field(s) which point to another ...
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...
QgsPoint layerToMapCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from layer's CRS to output CRS
int findData(const QVariant &data, int role, QFlags< Qt::MatchFlag > flags) const
void setColor(const QColor &color)
Set line/outline to color, polygon fill to color with alpha = 63.
void setModel(QAbstractItemModel *model)
virtual void sort(int column, Qt::SortOrder order=Qt::AscendingOrder) override
Sort by the given column using the given order.
void setTitle(const QString &title)
QgsRectangle boundingBox() const
Returns the bounding box of this feature.
void setCurrentIndex(int index)
void setExtent(const QgsRectangle &r, bool magnified=false)
Set the extent of the map canvas.
void setText(const QString &text)
double toDouble(bool *ok) const
bool setProperty(const char *name, const QVariant &value)
void addItems(const QStringList &texts)
void setInjectNull(bool injectNull)
If true is specified, a NULL value will be injected.
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &featureRequest=QgsFeatureRequest())
Query this VectorLayerCache for features.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
static double DEFAULT_HIGHLIGHT_MIN_WIDTH_MM
Default highlight line/outline minimum width in mm.
void setWordWrap(bool on)
void setSpacing(int spacing)
QString name() const
Returns a human readable name for this relation.
void zoomByFactor(double scaleFactor, const QgsPoint *center=nullptr)
Zoom with the factor supplied.
A form was embedded as a widget on another form.
void addLayout(QLayout *layout, int stretch)
void setMinWidth(double width)
Set minimum line / outline width in millimeters.
void setSingleShot(bool singleShot)