QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgsfieldvalueslineedit.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfieldvalueslineedit.cpp
3  -------------------------
4  Date : 20-08-2016
5  Copyright : (C) 2016 by Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
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 
16 #include "qgsfieldvalueslineedit.h"
17 #include "qgsvectorlayer.h"
18 #include "qgsfloatingwidget.h"
19 
20 #include <QCompleter>
21 #include <QStringListModel>
22 #include <QTimer>
23 #include <QHBoxLayout>
24 
26  : QgsFilterLineEdit( parent )
27 {
28  QCompleter *c = new QCompleter( this );
29  c->setCaseSensitivity( Qt::CaseInsensitive );
30  c->setFilterMode( Qt::MatchContains );
31  setCompleter( c );
32  connect( this, &QgsFieldValuesLineEdit::textEdited, this, &QgsFieldValuesLineEdit::requestCompleterUpdate );
33  mShowPopupTimer.setSingleShot( true );
34  mShowPopupTimer.setInterval( 100 );
35  connect( &mShowPopupTimer, &QTimer::timeout, this, &QgsFieldValuesLineEdit::triggerCompleterUpdate );
36 }
37 
39 {
40  if ( mGatherer )
41  {
42  mGatherer->stop();
43  mGatherer->wait(); // mGatherer is deleted when wait completes
44  }
45 }
46 
48 {
49  if ( mLayer == layer )
50  return;
51 
52  mLayer = layer;
53  emit layerChanged( layer );
54 }
55 
57 {
58  if ( mAttributeIndex == index )
59  return;
60 
61  mAttributeIndex = index;
62  emit attributeIndexChanged( index );
63 }
64 
65 void QgsFieldValuesLineEdit::requestCompleterUpdate()
66 {
67  mUpdateRequested = true;
68  mShowPopupTimer.start();
69 }
70 
71 void QgsFieldValuesLineEdit::triggerCompleterUpdate()
72 {
73  mShowPopupTimer.stop();
74  QString currentText = text();
75 
76  if ( currentText.isEmpty() )
77  {
78  if ( mGatherer )
79  mGatherer->stop();
80  return;
81  }
82 
83  updateCompletionList( currentText );
84 }
85 
86 void QgsFieldValuesLineEdit::updateCompletionList( const QString &text )
87 {
88  if ( text.isEmpty() )
89  {
90  if ( mGatherer )
91  mGatherer->stop();
92  return;
93  }
94 
95  mUpdateRequested = true;
96  if ( mGatherer )
97  {
98  mRequestedCompletionText = text;
99  mGatherer->stop();
100  return;
101  }
102 
103  mGatherer = new QgsFieldValuesLineEditValuesGatherer( mLayer, mAttributeIndex );
104  mGatherer->setSubstring( text );
105 
106  connect( mGatherer, &QgsFieldValuesLineEditValuesGatherer::collectedValues, this, &QgsFieldValuesLineEdit::updateCompleter );
107  connect( mGatherer, &QgsFieldValuesLineEditValuesGatherer::finished, this, &QgsFieldValuesLineEdit::gathererThreadFinished );
108 
109  mGatherer->start();
110 }
111 
112 void QgsFieldValuesLineEdit::gathererThreadFinished()
113 {
114  bool wasCanceled = mGatherer->wasCanceled();
115 
116  delete mGatherer;
117  mGatherer = nullptr;
118 
119  if ( wasCanceled )
120  {
121  QString text = mRequestedCompletionText;
122  mRequestedCompletionText.clear();
123  updateCompletionList( text );
124  return;
125  }
126 }
127 
128 void QgsFieldValuesLineEdit::updateCompleter( const QStringList &values )
129 {
130  mUpdateRequested = false;
131  completer()->setModel( new QStringListModel( values ) );
132  completer()->complete();
133 }
134 
135 
136 // just internal guff - definitely not for exposing to public API!
138 
139 void QgsFieldValuesLineEditValuesGatherer::run()
140 {
141  mWasCanceled = false;
142  if ( mSubstring.isEmpty() )
143  {
144  emit collectedValues( QStringList() );
145  return;
146  }
147 
148  // allow responsive cancellation
149  mFeedback = new QgsFeedback();
150  // just get 100 values... maybe less/more would be useful?
151  mValues = mLayer->uniqueStringsMatching( mAttributeIndex, mSubstring, 100, mFeedback );
152 
153  // be overly cautious - it's *possible* stop() might be called between deleting mFeedback and nulling it
154  mFeedbackMutex.lock();
155  delete mFeedback;
156  mFeedback = nullptr;
157  mFeedbackMutex.unlock();
158 
159  emit collectedValues( mValues );
160 }
161 
162 void QgsFieldValuesLineEditValuesGatherer::stop()
163 {
164  // be cautious, in case gatherer stops naturally just as we are canceling it and mFeedback gets deleted
165  mFeedbackMutex.lock();
166  if ( mFeedback )
167  mFeedback->cancel();
168  mFeedbackMutex.unlock();
169 
170  mWasCanceled = true;
171 }
172 
void setAttributeIndex(int index)
Sets the attribute index for the field containing values to show in the widget.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:44
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QgsFieldValuesLineEdit(QWidget *parent=nullptr)
Constructor for QgsFieldValuesLineEdit.
void setLayer(QgsVectorLayer *layer)
Sets the layer containing the field that values will be shown from.
QStringList uniqueStringsMatching(int index, const QString &substring, int limit=-1, QgsFeedback *feedback=nullptr) const
Returns unique string values of an attribute which contain a specified subset string.
void layerChanged(QgsVectorLayer *layer)
Emitted when the layer associated with the widget changes.
QLineEdit subclass with built in support for clearing the widget&#39;s value and handling custom null val...
void attributeIndexChanged(int index)
Emitted when the field associated with the widget changes.
Represents a vector layer which manages a vector based data sets.
QgsVectorLayer * layer() const
Returns the layer containing the field that values will be shown from.