QGIS API Documentation  2.9.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
qgsvaluerelationwidgetwrapper.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvaluerelationwidgetwrapper.cpp
3  --------------------------------------
4  Date : 5.1.2014
5  Copyright : (C) 2014 Matthias Kuhn
6  Email : matthias dot kuhn at gmx dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include "qgsfield.h"
19 #include "qgsmaplayerregistry.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsfilterlineedit.h"
23 
24 #include <QStringListModel>
25 #include <QCompleter>
26 
29 {
30  switch ( p1.first.type() )
31  {
32  case QVariant::String:
33  return p1.first.toString() < p2.first.toString();
34  break;
35 
36  case QVariant::Double:
37  return p1.first.toDouble() < p2.first.toDouble();
38  break;
39 
40  default:
41  return p1.first.toInt() < p2.first.toInt();
42  break;
43  }
44 }
45 
48 {
49  return p1.second < p2.second;
50 }
51 
52 QgsValueRelationWidgetWrapper::QgsValueRelationWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent )
53  : QgsEditorWidgetWrapper( vl, fieldIdx, editor, parent )
54  , mComboBox( 0 )
55  , mListWidget( 0 )
56  , mLineEdit( 0 )
57  , mLayer( 0 )
58 {
59 }
60 
61 
63 {
64  QVariant v;
65 
66  if ( mComboBox )
67  {
68  int cbxIdx = mComboBox->currentIndex();
69  if ( cbxIdx > -1 )
70  {
71  v = mComboBox->itemData( mComboBox->currentIndex() );
72  }
73  }
74 
75  if ( mListWidget )
76  {
77  QStringList selection;
78  for ( int i = 0; i < mListWidget->count(); ++i )
79  {
80  QListWidgetItem* item = mListWidget->item( i );
81  if ( item->checkState() == Qt::Checked )
82  selection << item->data( Qt::UserRole ).toString();
83  }
84 
85  v = selection.join( "," ).prepend( "{" ).append( "}" );
86  }
87 
88  if ( mLineEdit )
89  {
90  Q_FOREACH ( const ValueRelationItem& i , mCache )
91  {
92  if ( i.second == mLineEdit->text() )
93  {
94  v = i.first;
95  break;
96  }
97  }
98  }
99 
100  return v;
101 }
102 
104 {
105  if ( config( "AllowMulti" ).toBool() )
106  {
107  return new QListWidget( parent );
108  }
109  else if ( config( "UseCompleter" ).toBool() )
110  {
111  return new QgsFilterLineEdit( parent );
112  }
113  {
114  return new QComboBox( parent );
115  }
116 }
117 
119 {
120  mCache = createCache( config() );
121 
122  mComboBox = qobject_cast<QComboBox*>( editor );
123  mListWidget = qobject_cast<QListWidget*>( editor );
124  mLineEdit = qobject_cast<QLineEdit*>( editor );
125 
126  if ( mComboBox )
127  {
128  if ( config( "AllowNull" ).toBool() )
129  {
130  mComboBox->addItem( tr( "(no selection)" ), QVariant( field().type() ) );
131  }
132 
133  Q_FOREACH ( const ValueRelationItem& element, mCache )
134  {
135  mComboBox->addItem( element.second, element.first );
136  }
137 
138  connect( mComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( valueChanged() ) );
139  }
140  else if ( mListWidget )
141  {
142  Q_FOREACH ( const ValueRelationItem& element, mCache )
143  {
144  QListWidgetItem *item;
145  item = new QListWidgetItem( element.second );
146  item->setData( Qt::UserRole, element.first );
147 
148  mListWidget->addItem( item );
149  }
150  connect( mListWidget, SIGNAL( itemChanged( QListWidgetItem* ) ), this, SLOT( valueChanged() ) );
151  }
152  else if ( mLineEdit )
153  {
154  QStringList values;
155  Q_FOREACH ( const ValueRelationItem& i, mCache )
156  {
157  values << i.second;
158  }
159 
160  QStringListModel* m = new QStringListModel( values, mLineEdit );
161  QCompleter* completer = new QCompleter( m, mLineEdit );
162  completer->setCaseSensitivity( Qt::CaseInsensitive );
163  mLineEdit->setCompleter( completer );
164  }
165 }
166 
167 void QgsValueRelationWidgetWrapper::setValue( const QVariant& value )
168 {
169  if ( mListWidget )
170  {
171  QStringList checkList = value.toString().remove( QChar( '{' ) ).remove( QChar( '}' ) ).split( "," );
172 
173  for ( int i = 0; i < mListWidget->count(); ++i )
174  {
175  QListWidgetItem* item = mListWidget->item( i );
176  if ( config( "OrderByValue" ).toBool() )
177  {
178  item->setCheckState( checkList.contains( item->data( Qt::UserRole ).toString() ) ? Qt::Checked : Qt::Unchecked );
179  }
180  else
181  {
182  item->setCheckState( checkList.contains( item->data( Qt::UserRole ).toString() ) ? Qt::Checked : Qt::Unchecked );
183  }
184  }
185  }
186  else if ( mComboBox )
187  {
188  mComboBox->setCurrentIndex( mComboBox->findData( value ) );
189  }
190  else if ( mLineEdit )
191  {
192  Q_FOREACH ( ValueRelationItem i, mCache )
193  {
194  if ( i.first == value )
195  {
196  mLineEdit->setText( i.second );
197  break;
198  }
199  }
200  }
201 }
202 
203 
205 {
206  ValueRelationCache cache;
207 
208  QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( config.value( "Layer" ).toString() ) );
209 
210  if ( layer )
211  {
212  int ki = layer->fieldNameIndex( config.value( "Key" ).toString() );
213  int vi = layer->fieldNameIndex( config.value( "Value" ).toString() );
214 
215  QgsExpression *e = 0;
216  if ( !config.value( "FilterExpression" ).toString().isEmpty() )
217  {
218  e = new QgsExpression( config.value( "FilterExpression" ).toString() );
219  if ( e->hasParserError() || !e->prepare( layer->pendingFields() ) )
220  ki = -1;
221  }
222 
223  if ( ki >= 0 && vi >= 0 )
224  {
225  QSet<int> attributes;
226  attributes << ki << vi;
227 
228  QgsFeatureRequest::Flags flags = QgsFeatureRequest::NoGeometry;
229 
230  bool requiresAllAttributes = false;
231  if ( e )
232  {
233  if ( e->needsGeometry() )
235 
236  Q_FOREACH ( const QString& field, e->referencedColumns() )
237  {
238  if ( field == QgsFeatureRequest::AllAttributes )
239  {
240  requiresAllAttributes = true;
241  break;
242  }
243  int idx = layer->fieldNameIndex( field );
244  if ( idx < 0 )
245  continue;
246  attributes << idx;
247  }
248  }
249 
251  if ( !requiresAllAttributes )
252  {
253  fr.setSubsetOfAttributes( attributes.toList() );
254  }
255 
256  QgsFeatureIterator fit = layer->getFeatures( fr );
257 
258  QgsFeature f;
259  while ( fit.nextFeature( f ) )
260  {
261  if ( e && !e->evaluate( &f ).toBool() )
262  continue;
263 
264  cache.append( ValueRelationItem( f.attribute( ki ), f.attribute( vi ).toString() ) );
265  }
266  }
267  delete e;
268  }
269 
270  if ( config.value( "OrderByValue" ).toBool() )
271  {
272  qSort( cache.begin(), cache.end(), orderByValueLessThan );
273  }
274  else
275  {
276  qSort( cache.begin(), cache.end(), orderByKeyLessThan );
277  }
278 
279  return cache;
280 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:86
Wrapper for iterator of features from vector data provider or vector layer.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
Definition: qgsexpression.h:93
QStringList referencedColumns() const
Get list of columns referenced by the expression.
void valueChanged()
Will call the value() method to determine the emitted value.
QVariant evaluate(const QgsFeature *f=NULL)
Evaluate the feature and return the result.
bool prepare(const QgsFields &fields)
Get the expression ready for evaluation - find out column indexes.
Manages an editor widget Widget and wrapper share the same parent.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsVectorLayer * layer()
Access the QgsVectorLayer, you are working on.
void setValue(const QVariant &value) override
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:113
QgsValueRelationWidgetWrapper(QgsVectorLayer *vl, int fieldIdx, QWidget *editor=0, QWidget *parent=0)
QgsField field()
Access the field.
QVariant value() override
Will be used to access the widget's value.
bool orderByValueLessThan(const QgsValueRelationWidgetWrapper::ValueRelationItem &p1, const QgsValueRelationWidgetWrapper::ValueRelationItem &p2)
QPair< QVariant, QString > ValueRelationItem
const QgsEditorWidgetConfig config()
Returns the whole config.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
void initWidget(QWidget *editor) override
This method should initialize the editor widget with runtime data.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
bool orderByKeyLessThan(const QgsValueRelationWidgetWrapper::ValueRelationItem &p1, const QgsValueRelationWidgetWrapper::ValueRelationItem &p2)
Lineedit with builtin clear button.
static const QString AllAttributes
static ValueRelationCache createCache(const QgsEditorWidgetConfig &config)
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:230
QWidget * createWidget(QWidget *parent) override
This method should create a new widget with the provided parent.
QMap< QString, QVariant > QgsEditorWidgetConfig
Holds a set of configuration parameters for a editor widget wrapper.
QgsMapLayer * mapLayer(QString theLayerId)
Retrieve a pointer to a loaded layer by id.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Represents a vector layer which manages a vector based data sets.
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
QgsFeatureRequest & setFlags(Flags flags)
Set flags that affect how features will be fetched.
QVector< ValueRelationItem > ValueRelationCache
#define tr(sourceText)