QGIS API Documentation  3.27.0-Master (0e23467727)
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  c->setCompletionMode( QCompleter::UnfilteredPopupCompletion );
32  setCompleter( c );
33  connect( this, &QgsFieldValuesLineEdit::textEdited, this, &QgsFieldValuesLineEdit::requestCompleterUpdate );
34  mShowPopupTimer.setSingleShot( true );
35  mShowPopupTimer.setInterval( 100 );
36  connect( &mShowPopupTimer, &QTimer::timeout, this, &QgsFieldValuesLineEdit::triggerCompleterUpdate );
37 }
38 
40 {
41  if ( mGatherer )
42  {
43  mGatherer->stop();
44  mGatherer->wait(); // mGatherer is deleted when wait completes
45  }
46 }
47 
49 {
50  if ( mLayer == layer )
51  return;
52 
53  mLayer = layer;
54  emit layerChanged( layer );
55 }
56 
58 {
59  if ( mAttributeIndex == index )
60  return;
61 
62  mAttributeIndex = index;
63  emit attributeIndexChanged( index );
64 }
65 
66 void QgsFieldValuesLineEdit::requestCompleterUpdate()
67 {
68  mUpdateRequested = true;
69  mShowPopupTimer.start();
70 }
71 
72 void QgsFieldValuesLineEdit::triggerCompleterUpdate()
73 {
74  mShowPopupTimer.stop();
75  const QString currentText = text();
76 
77  if ( currentText.isEmpty() )
78  {
79  if ( mGatherer )
80  mGatherer->stop();
81  return;
82  }
83 
84  updateCompletionList( currentText );
85 }
86 
87 void QgsFieldValuesLineEdit::updateCompletionList( const QString &text )
88 {
89  if ( text.isEmpty() )
90  {
91  if ( mGatherer )
92  mGatherer->stop();
93  return;
94  }
95 
96  mUpdateRequested = true;
97  if ( mGatherer )
98  {
99  mRequestedCompletionText = text;
100  mGatherer->stop();
101  return;
102  }
103 
104  mGatherer = new QgsFieldValuesLineEditValuesGatherer( mLayer, mAttributeIndex );
105  mGatherer->setSubstring( text );
106 
107  connect( mGatherer, &QgsFieldValuesLineEditValuesGatherer::collectedValues, this, &QgsFieldValuesLineEdit::updateCompleter );
108  connect( mGatherer, &QgsFieldValuesLineEditValuesGatherer::finished, this, &QgsFieldValuesLineEdit::gathererThreadFinished );
109 
110  mGatherer->start();
111 }
112 
113 void QgsFieldValuesLineEdit::gathererThreadFinished()
114 {
115  const bool wasCanceled = mGatherer->wasCanceled();
116 
117  delete mGatherer;
118  mGatherer = nullptr;
119 
120  if ( wasCanceled )
121  {
122  const QString text = mRequestedCompletionText;
123  mRequestedCompletionText.clear();
124  updateCompletionList( text );
125  return;
126  }
127 }
128 
129 void QgsFieldValuesLineEdit::updateCompleter( const QStringList &values )
130 {
131  mUpdateRequested = false;
132  completer()->setModel( new QStringListModel( values ) );
133  completer()->complete();
134 }
135 
136 
137 // just internal guff - definitely not for exposing to public API!
139 
140 void QgsFieldValuesLineEditValuesGatherer::run()
141 {
142  mWasCanceled = false;
143  if ( mSubstring.isEmpty() )
144  {
145  emit collectedValues( QStringList() );
146  return;
147  }
148 
149  // allow responsive cancellation
150  mFeedback = new QgsFeedback();
151  // just get 100 values... maybe less/more would be useful?
152  mValues = mLayer->uniqueStringsMatching( mAttributeIndex, mSubstring, 100, mFeedback );
153 
154  // be overly cautious - it's *possible* stop() might be called between deleting mFeedback and nulling it
155  mFeedbackMutex.lock();
156  delete mFeedback;
157  mFeedback = nullptr;
158  mFeedbackMutex.unlock();
159 
160  emit collectedValues( mValues );
161 }
162 
163 void QgsFieldValuesLineEditValuesGatherer::stop()
164 {
165  // be cautious, in case gatherer stops naturally just as we are canceling it and mFeedback gets deleted
166  mFeedbackMutex.lock();
167  if ( mFeedback )
168  mFeedback->cancel();
169  mFeedbackMutex.unlock();
170 
171  mWasCanceled = true;
172 }
173 
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
void attributeIndexChanged(int index)
Emitted when the field associated with the widget changes.
QgsFieldValuesLineEdit(QWidget *parent=nullptr)
Constructor for QgsFieldValuesLineEdit.
void setLayer(QgsVectorLayer *layer)
Sets the layer containing the field that values will be shown from.
void layerChanged(QgsVectorLayer *layer)
Emitted when the layer associated with the widget changes.
void setAttributeIndex(int index)
Sets the attribute index for the field containing values to show in the widget.
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
Represents a vector layer which manages a vector based data sets.
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