Skip to content

Commit ca5c7e2

Browse files
committed
[FEATURE] Form based select/filter in attribute table
This commit adds a new mode to the attribute table dialog for searching and filtering features. When activated (using a button on the toolbar or by pressng CTRL+F), the dialog will switch to form view and all widgets are replaced with their search widget wrapper variant. Alongside each widget is a tool button with options for controlling the search/filter behaviour for that field, eg "equal to", "not equal to", "is null", "greater than", etc.., with the options presented matching themselves to the corresponding field and widget type. New buttons appear at the bottom of the form for either selecting matching features (with options for add to selection/remove from selection/select within current selection) or filtering features in the table (with options for adding features to a current filter or further restricting a current filter). Sponsored by SIGE
1 parent 3fcd1fd commit ca5c7e2

34 files changed

+2312
-53
lines changed

python/gui/attributetable/qgsdualview.sip

+19
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,12 @@ class QgsDualView : QStackedWidget
183183
*/
184184
void setMultiEditEnabled( bool enabled );
185185

186+
/** Toggles whether search mode should be enabled in the form.
187+
* @param enabled set to true to switch on search mode
188+
* @note added in QGIS 2.16
189+
*/
190+
void toggleSearchMode( bool enabled );
191+
186192
signals:
187193
/**
188194
* Is emitted, whenever the display expression is successfully changed
@@ -194,6 +200,19 @@ class QgsDualView : QStackedWidget
194200
* Is emitted, whenever the filter changes
195201
*/
196202
void filterChanged();
203+
204+
/** Is emitted when a filter expression is set using the view.
205+
* @param expression filter expression
206+
* @param type filter type
207+
* @note added in QGIS 2.16
208+
*/
209+
void filterExpressionSet( const QString& expression, QgsAttributeForm::FilterType type );
210+
211+
/** Emitted when the form changes mode.
212+
* @param mode new mode
213+
*/
214+
void formModeChanged( QgsAttributeForm::Mode mode );
215+
197216
};
198217

199218
class QgsAttributeTableAction : QAction

python/gui/editorwidgets/core/qgssearchwidgetwrapper.sip

+131
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,50 @@
1+
%MappedType QList<QgsSearchWidgetWrapper::FilterFlag>
2+
{
3+
%TypeHeaderCode
4+
#include <QList>
5+
%End
6+
7+
%ConvertFromTypeCode
8+
// Create the list.
9+
PyObject *l;
10+
11+
if ((l = PyList_New(sipCpp->size())) == NULL)
12+
return NULL;
13+
14+
// Set the list elements.
15+
QList<QgsSearchWidgetWrapper::FilterFlag>::iterator it = sipCpp->begin();
16+
for (int i = 0; it != sipCpp->end(); ++it, ++i)
17+
{
18+
PyObject *tobj;
19+
20+
if ((tobj = sipConvertFromEnum(*it, sipType_QgsSearchWidgetWrapper_FilterFlag)) == NULL)
21+
{
22+
Py_DECREF(l);
23+
return NULL;
24+
}
25+
PyList_SET_ITEM(l, i, tobj);
26+
}
27+
28+
return l;
29+
%End
30+
31+
%ConvertToTypeCode
32+
// Check the type if that is all that is required.
33+
if (sipIsErr == NULL)
34+
return PyList_Check(sipPy);
35+
36+
QList<QgsSearchWidgetWrapper::FilterFlag> *qlist = new QList<QgsSearchWidgetWrapper::FilterFlag>;
37+
38+
for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i)
39+
{
40+
*qlist << (QgsSearchWidgetWrapper::FilterFlag)SIPLong_AsLong(PyList_GET_ITEM(sipPy, i));
41+
}
42+
43+
*sipCppPtr = qlist;
44+
return sipGetState(sipTransferObj);
45+
%End
46+
};
47+
148
/**
249
* Manages an editor widget
350
* Widget and wrapper share the same parent
@@ -14,6 +61,43 @@ class QgsSearchWidgetWrapper : QgsWidgetWrapper
1461
#include <qgssearchwidgetwrapper.h>
1562
%End
1663
public:
64+
65+
//! Flags which indicate what types of filtering and searching is possible using the widget
66+
//! @note added in QGIS 2.16
67+
enum FilterFlag
68+
{
69+
EqualTo, /*!< Supports equal to */
70+
NotEqualTo, /*!< Supports not equal to */
71+
GreaterThan, /*!< Supports greater than */
72+
LessThan, /*!< Supports less than */
73+
GreaterThanOrEqualTo, /*!< Supports >= */
74+
LessThanOrEqualTo, /*!< Supports <= */
75+
Between, /*!< Supports searches between two values */
76+
CaseInsensitive, /*!< Supports case insensitive searching */
77+
Contains, /*!< Supports value "contains" searching */
78+
DoesNotContain, /*!< Supports value does not contain searching */
79+
IsNull, /*!< Supports searching for null values */
80+
};
81+
typedef QFlags<QgsSearchWidgetWrapper::FilterFlag> FilterFlags;
82+
83+
/** Returns a list of exclusive filter flags, which cannot be combined with other flags (eg EqualTo/NotEqualTo)
84+
* @note added in QGIS 2.16
85+
* @see nonExclusiveFilterFlags()
86+
*/
87+
static QList< QgsSearchWidgetWrapper::FilterFlag > exclusiveFilterFlags();
88+
89+
/** Returns a list of non-exclusive filter flags, which can be combined with other flags (eg CaseInsensitive)
90+
* @note added in QGIS 2.16
91+
* @see exclusiveFilterFlags()
92+
*/
93+
static QList< QgsSearchWidgetWrapper::FilterFlag > nonExclusiveFilterFlags();
94+
95+
/** Returns a translated string representing a filter flag.
96+
* @param flag flag to convert to string
97+
* @note added in QGIS 2.16
98+
*/
99+
static QString toString( FilterFlag flag );
100+
17101
/**
18102
* Create a new widget wrapper
19103
*
@@ -23,6 +107,18 @@ class QgsSearchWidgetWrapper : QgsWidgetWrapper
23107
*/
24108
explicit QgsSearchWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* parent /TransferThis/ = nullptr );
25109

110+
/** Returns filter flags supported by the search widget.
111+
* @note added in QGIS 2.16
112+
* @see defaultFlags()
113+
*/
114+
virtual FilterFlags supportedFlags() const;
115+
116+
/** Returns the filter flags which should be set by default for the search widget.
117+
* @note added in QGIS 2.16
118+
* @see supportedFlags()
119+
*/
120+
virtual FilterFlags defaultFlags() const;
121+
26122
/**
27123
* Will be used to access the widget's value. Read the value from the widget and
28124
* return it properly formatted to be saved in the attribute.
@@ -39,6 +135,28 @@ class QgsSearchWidgetWrapper : QgsWidgetWrapper
39135
*/
40136
virtual bool applyDirectly() = 0;
41137

138+
/** Creates a filter expression based on the current state of the search widget
139+
* and the specified filter flags.
140+
* @param flags filter flags
141+
* @returns filter expression
142+
* @note added in QGIS 2.16
143+
*/
144+
// TODO QGIS 3.0 - make pure virtual
145+
virtual QString createExpression( FilterFlags flags ) const;
146+
147+
public slots:
148+
149+
/** Clears the widget's current value and resets it back to the default state
150+
* @note added in QGIS 2.16
151+
*/
152+
virtual void clearWidget();
153+
154+
/** Toggles whether the search widget is enabled or disabled.
155+
* @param enabled set to true to enable widget
156+
* @note added in QGIS 2.16
157+
*/
158+
virtual void setEnabled( bool enabled );
159+
42160
signals:
43161

44162
/**
@@ -47,6 +165,17 @@ class QgsSearchWidgetWrapper : QgsWidgetWrapper
47165
*/
48166
void expressionChanged( const QString& exp );
49167

168+
/** Emitted when a user changes the value of the search widget.
169+
* @note added in QGIS 2.16
170+
*/
171+
void valueChanged();
172+
173+
/** Emitted when a user changes the value of the search widget back
174+
* to an empty, default state.
175+
* @note added in QGIS 2.16
176+
*/
177+
void valueCleared();
178+
50179
protected slots:
51180

52181
virtual void setExpression( QString value ) = 0;
@@ -57,3 +186,5 @@ class QgsSearchWidgetWrapper : QgsWidgetWrapper
57186
void clearExpression();
58187

59188
};
189+
190+
QFlags<QgsSearchWidgetWrapper::FilterFlag> operator|(QgsSearchWidgetWrapper::FilterFlag f1, QFlags<QgsSearchWidgetWrapper::FilterFlag> f2);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Wraps a search widget. Default form is just a QgsLineFilterEdit
3+
*/
4+
class QgsDefaultSearchWidgetWrapper : QgsSearchWidgetWrapper
5+
{
6+
%TypeHeaderCode
7+
#include <qgsdefaultsearchwidgetwrapper.h>
8+
%End
9+
public:
10+
11+
explicit QgsDefaultSearchWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* parent /TransferThis/ = nullptr );
12+
13+
// QgsSearchWidgetWrapper interface
14+
public:
15+
QString expression();
16+
bool applyDirectly();
17+
FilterFlags supportedFlags() const;
18+
FilterFlags defaultFlags() const;
19+
virtual QString createExpression( FilterFlags flags ) const;
20+
21+
public slots:
22+
23+
virtual void clearWidget();
24+
virtual void setEnabled( bool enabled );
25+
26+
protected slots:
27+
void setExpression( QString exp );
28+
29+
protected:
30+
QWidget* createWidget( QWidget* parent );
31+
void initWidget( QWidget* editor );
32+
bool valid() const;
33+
34+
/** Returns a pointer to the line edit part of the widget.
35+
* @note this method is in place for unit testing only, and is not considered
36+
* stable API
37+
*/
38+
QgsFilterLineEdit* lineEdit();
39+
40+
/** Returns a pointer to the case sensitivity check box in the widget.
41+
* @note this method is in place for unit testing only, and is not considered
42+
* stable API
43+
*/
44+
QCheckBox* caseSensitiveCheckBox();
45+
46+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/** \ingroup gui
2+
* \class QgsSearchWidgetToolButton
3+
* A tool button widget which is displayed next to search widgets in forms, and
4+
* allows for controlling how the widget behaves and how the filtering/searching
5+
* operates.
6+
* \note Added in version 2.16
7+
*/
8+
class QgsSearchWidgetToolButton : QToolButton
9+
{
10+
%TypeHeaderCode
11+
#include <qgssearchwidgettoolbutton.h>
12+
%End
13+
public:
14+
15+
/** Constructor for QgsSearchWidgetToolButton.
16+
* @param parent parent object
17+
*/
18+
explicit QgsSearchWidgetToolButton( QWidget *parent /TransferThis/ = nullptr );
19+
20+
/** Sets the search widget wrapper associated with this button.
21+
* Calling this will automatically set the available flags to match those
22+
* supported by the wrapper and reset the active flags to match the wrapper's
23+
* default flags.
24+
* @param wrapper search wrapper. Ownership is not transferred.
25+
*/
26+
void setSearchWidgetWrapper( QgsSearchWidgetWrapper* wrapper );
27+
28+
/** Sets the available filter flags to show in the widget. Any active flags
29+
* (see activeFlags()) which are not present in the new available filter
30+
* flags will be cleared;
31+
* @param flags available flags to show in widget
32+
* @see availableFlags()
33+
* @see setActiveFlags()
34+
*/
35+
void setAvailableFlags( QgsSearchWidgetWrapper::FilterFlags flags );
36+
37+
/** Returns the available filter flags shown in the widget.
38+
* @see setAvailableFlags()
39+
* @see activeFlags()
40+
*/
41+
QgsSearchWidgetWrapper::FilterFlags availableFlags() const;
42+
43+
/** Sets the current active filter flags for the widget. Any flags
44+
* which are not present in the available filter flags (see availableFlags())
45+
* will not be set.
46+
* @param flags active flags to show in widget
47+
* @see toggleFlag()
48+
* @see activeFlags()
49+
* @see setAvailableFlags()
50+
*/
51+
void setActiveFlags( QgsSearchWidgetWrapper::FilterFlags flags );
52+
53+
/** Toggles an individual active filter flag for the widget. Any flags
54+
* which are not present in the available filter flags (see availableFlags())
55+
* will be ignore. Other flags may be cleared if they conflict with the newly
56+
* toggled flag.
57+
* @param flag flag to toggle
58+
* @see setActiveFlags()
59+
* @see activeFlags()
60+
*/
61+
void toggleFlag( QgsSearchWidgetWrapper::FilterFlag flag );
62+
63+
/** Returns the active filter flags shown in the widget.
64+
* @see setActiveFlags()
65+
* @see toggleFlag()
66+
* @see availableFlags()
67+
*/
68+
QgsSearchWidgetWrapper::FilterFlags activeFlags() const;
69+
70+
/** Returns true if the widget is set to be included in the search.
71+
* @see setInactive()
72+
* @see setActive()
73+
*/
74+
bool isActive() const;
75+
76+
public slots:
77+
78+
/** Sets the search widget as inactive, ie do not search the corresponding field.
79+
* @see isActive()
80+
* @see setActive()
81+
*/
82+
void setInactive();
83+
84+
/** Sets the search widget as active by selecting the first available search type.
85+
* @see isActive()
86+
* @see setInactive()
87+
*/
88+
void setActive();
89+
90+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Wraps a value map search widget. This widget will offer a combobox with values from another layer
3+
* referenced by a foreign key (a constraint may be set but is not required on data level).
4+
* It will be used as a search widget and produces expression to look for in the layer.
5+
*/
6+
7+
class QgsValueMapSearchWidgetWrapper : QgsSearchWidgetWrapper
8+
{
9+
%TypeHeaderCode
10+
#include <qgsvaluemapsearchwidgetwrapper.h>
11+
%End
12+
public:
13+
14+
explicit QgsValueMapSearchWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* parent /TransferThis/ = nullptr );
15+
bool applyDirectly();
16+
QString expression();
17+
bool valid() const;
18+
FilterFlags supportedFlags() const;
19+
FilterFlags defaultFlags() const;
20+
virtual QString createExpression( FilterFlags flags ) const;
21+
22+
public slots:
23+
24+
virtual void clearWidget();
25+
virtual void setEnabled( bool enabled );
26+
27+
protected:
28+
QWidget* createWidget( QWidget* parent );
29+
void initWidget( QWidget* editor );
30+
31+
protected slots:
32+
void setExpression( QString exp );
33+
34+
};

0 commit comments

Comments
 (0)