mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
[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
This commit is contained in:
parent
3fcd1fd6a9
commit
ca5c7e2cd4
@ -183,6 +183,12 @@ class QgsDualView : QStackedWidget
|
||||
*/
|
||||
void setMultiEditEnabled( bool enabled );
|
||||
|
||||
/** Toggles whether search mode should be enabled in the form.
|
||||
* @param enabled set to true to switch on search mode
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void toggleSearchMode( bool enabled );
|
||||
|
||||
signals:
|
||||
/**
|
||||
* Is emitted, whenever the display expression is successfully changed
|
||||
@ -194,6 +200,19 @@ class QgsDualView : QStackedWidget
|
||||
* Is emitted, whenever the filter changes
|
||||
*/
|
||||
void filterChanged();
|
||||
|
||||
/** Is emitted when a filter expression is set using the view.
|
||||
* @param expression filter expression
|
||||
* @param type filter type
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void filterExpressionSet( const QString& expression, QgsAttributeForm::FilterType type );
|
||||
|
||||
/** Emitted when the form changes mode.
|
||||
* @param mode new mode
|
||||
*/
|
||||
void formModeChanged( QgsAttributeForm::Mode mode );
|
||||
|
||||
};
|
||||
|
||||
class QgsAttributeTableAction : QAction
|
||||
|
@ -1,3 +1,50 @@
|
||||
%MappedType QList<QgsSearchWidgetWrapper::FilterFlag>
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <QList>
|
||||
%End
|
||||
|
||||
%ConvertFromTypeCode
|
||||
// Create the list.
|
||||
PyObject *l;
|
||||
|
||||
if ((l = PyList_New(sipCpp->size())) == NULL)
|
||||
return NULL;
|
||||
|
||||
// Set the list elements.
|
||||
QList<QgsSearchWidgetWrapper::FilterFlag>::iterator it = sipCpp->begin();
|
||||
for (int i = 0; it != sipCpp->end(); ++it, ++i)
|
||||
{
|
||||
PyObject *tobj;
|
||||
|
||||
if ((tobj = sipConvertFromEnum(*it, sipType_QgsSearchWidgetWrapper_FilterFlag)) == NULL)
|
||||
{
|
||||
Py_DECREF(l);
|
||||
return NULL;
|
||||
}
|
||||
PyList_SET_ITEM(l, i, tobj);
|
||||
}
|
||||
|
||||
return l;
|
||||
%End
|
||||
|
||||
%ConvertToTypeCode
|
||||
// Check the type if that is all that is required.
|
||||
if (sipIsErr == NULL)
|
||||
return PyList_Check(sipPy);
|
||||
|
||||
QList<QgsSearchWidgetWrapper::FilterFlag> *qlist = new QList<QgsSearchWidgetWrapper::FilterFlag>;
|
||||
|
||||
for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i)
|
||||
{
|
||||
*qlist << (QgsSearchWidgetWrapper::FilterFlag)SIPLong_AsLong(PyList_GET_ITEM(sipPy, i));
|
||||
}
|
||||
|
||||
*sipCppPtr = qlist;
|
||||
return sipGetState(sipTransferObj);
|
||||
%End
|
||||
};
|
||||
|
||||
/**
|
||||
* Manages an editor widget
|
||||
* Widget and wrapper share the same parent
|
||||
@ -14,6 +61,43 @@ class QgsSearchWidgetWrapper : QgsWidgetWrapper
|
||||
#include <qgssearchwidgetwrapper.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
//! Flags which indicate what types of filtering and searching is possible using the widget
|
||||
//! @note added in QGIS 2.16
|
||||
enum FilterFlag
|
||||
{
|
||||
EqualTo, /*!< Supports equal to */
|
||||
NotEqualTo, /*!< Supports not equal to */
|
||||
GreaterThan, /*!< Supports greater than */
|
||||
LessThan, /*!< Supports less than */
|
||||
GreaterThanOrEqualTo, /*!< Supports >= */
|
||||
LessThanOrEqualTo, /*!< Supports <= */
|
||||
Between, /*!< Supports searches between two values */
|
||||
CaseInsensitive, /*!< Supports case insensitive searching */
|
||||
Contains, /*!< Supports value "contains" searching */
|
||||
DoesNotContain, /*!< Supports value does not contain searching */
|
||||
IsNull, /*!< Supports searching for null values */
|
||||
};
|
||||
typedef QFlags<QgsSearchWidgetWrapper::FilterFlag> FilterFlags;
|
||||
|
||||
/** Returns a list of exclusive filter flags, which cannot be combined with other flags (eg EqualTo/NotEqualTo)
|
||||
* @note added in QGIS 2.16
|
||||
* @see nonExclusiveFilterFlags()
|
||||
*/
|
||||
static QList< QgsSearchWidgetWrapper::FilterFlag > exclusiveFilterFlags();
|
||||
|
||||
/** Returns a list of non-exclusive filter flags, which can be combined with other flags (eg CaseInsensitive)
|
||||
* @note added in QGIS 2.16
|
||||
* @see exclusiveFilterFlags()
|
||||
*/
|
||||
static QList< QgsSearchWidgetWrapper::FilterFlag > nonExclusiveFilterFlags();
|
||||
|
||||
/** Returns a translated string representing a filter flag.
|
||||
* @param flag flag to convert to string
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
static QString toString( FilterFlag flag );
|
||||
|
||||
/**
|
||||
* Create a new widget wrapper
|
||||
*
|
||||
@ -23,6 +107,18 @@ class QgsSearchWidgetWrapper : QgsWidgetWrapper
|
||||
*/
|
||||
explicit QgsSearchWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* parent /TransferThis/ = nullptr );
|
||||
|
||||
/** Returns filter flags supported by the search widget.
|
||||
* @note added in QGIS 2.16
|
||||
* @see defaultFlags()
|
||||
*/
|
||||
virtual FilterFlags supportedFlags() const;
|
||||
|
||||
/** Returns the filter flags which should be set by default for the search widget.
|
||||
* @note added in QGIS 2.16
|
||||
* @see supportedFlags()
|
||||
*/
|
||||
virtual FilterFlags defaultFlags() const;
|
||||
|
||||
/**
|
||||
* Will be used to access the widget's value. Read the value from the widget and
|
||||
* return it properly formatted to be saved in the attribute.
|
||||
@ -39,6 +135,28 @@ class QgsSearchWidgetWrapper : QgsWidgetWrapper
|
||||
*/
|
||||
virtual bool applyDirectly() = 0;
|
||||
|
||||
/** Creates a filter expression based on the current state of the search widget
|
||||
* and the specified filter flags.
|
||||
* @param flags filter flags
|
||||
* @returns filter expression
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
// TODO QGIS 3.0 - make pure virtual
|
||||
virtual QString createExpression( FilterFlags flags ) const;
|
||||
|
||||
public slots:
|
||||
|
||||
/** Clears the widget's current value and resets it back to the default state
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
virtual void clearWidget();
|
||||
|
||||
/** Toggles whether the search widget is enabled or disabled.
|
||||
* @param enabled set to true to enable widget
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
virtual void setEnabled( bool enabled );
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
@ -47,6 +165,17 @@ class QgsSearchWidgetWrapper : QgsWidgetWrapper
|
||||
*/
|
||||
void expressionChanged( const QString& exp );
|
||||
|
||||
/** Emitted when a user changes the value of the search widget.
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void valueChanged();
|
||||
|
||||
/** Emitted when a user changes the value of the search widget back
|
||||
* to an empty, default state.
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void valueCleared();
|
||||
|
||||
protected slots:
|
||||
|
||||
virtual void setExpression( QString value ) = 0;
|
||||
@ -57,3 +186,5 @@ class QgsSearchWidgetWrapper : QgsWidgetWrapper
|
||||
void clearExpression();
|
||||
|
||||
};
|
||||
|
||||
QFlags<QgsSearchWidgetWrapper::FilterFlag> operator|(QgsSearchWidgetWrapper::FilterFlag f1, QFlags<QgsSearchWidgetWrapper::FilterFlag> f2);
|
||||
|
46
python/gui/editorwidgets/qgsdefaultsearchwidgetwrapper.sip
Normal file
46
python/gui/editorwidgets/qgsdefaultsearchwidgetwrapper.sip
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Wraps a search widget. Default form is just a QgsLineFilterEdit
|
||||
*/
|
||||
class QgsDefaultSearchWidgetWrapper : QgsSearchWidgetWrapper
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgsdefaultsearchwidgetwrapper.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
explicit QgsDefaultSearchWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* parent /TransferThis/ = nullptr );
|
||||
|
||||
// QgsSearchWidgetWrapper interface
|
||||
public:
|
||||
QString expression();
|
||||
bool applyDirectly();
|
||||
FilterFlags supportedFlags() const;
|
||||
FilterFlags defaultFlags() const;
|
||||
virtual QString createExpression( FilterFlags flags ) const;
|
||||
|
||||
public slots:
|
||||
|
||||
virtual void clearWidget();
|
||||
virtual void setEnabled( bool enabled );
|
||||
|
||||
protected slots:
|
||||
void setExpression( QString exp );
|
||||
|
||||
protected:
|
||||
QWidget* createWidget( QWidget* parent );
|
||||
void initWidget( QWidget* editor );
|
||||
bool valid() const;
|
||||
|
||||
/** Returns a pointer to the line edit part of the widget.
|
||||
* @note this method is in place for unit testing only, and is not considered
|
||||
* stable API
|
||||
*/
|
||||
QgsFilterLineEdit* lineEdit();
|
||||
|
||||
/** Returns a pointer to the case sensitivity check box in the widget.
|
||||
* @note this method is in place for unit testing only, and is not considered
|
||||
* stable API
|
||||
*/
|
||||
QCheckBox* caseSensitiveCheckBox();
|
||||
|
||||
};
|
90
python/gui/editorwidgets/qgssearchwidgettoolbutton.sip
Normal file
90
python/gui/editorwidgets/qgssearchwidgettoolbutton.sip
Normal file
@ -0,0 +1,90 @@
|
||||
/** \ingroup gui
|
||||
* \class QgsSearchWidgetToolButton
|
||||
* A tool button widget which is displayed next to search widgets in forms, and
|
||||
* allows for controlling how the widget behaves and how the filtering/searching
|
||||
* operates.
|
||||
* \note Added in version 2.16
|
||||
*/
|
||||
class QgsSearchWidgetToolButton : QToolButton
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgssearchwidgettoolbutton.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
/** Constructor for QgsSearchWidgetToolButton.
|
||||
* @param parent parent object
|
||||
*/
|
||||
explicit QgsSearchWidgetToolButton( QWidget *parent /TransferThis/ = nullptr );
|
||||
|
||||
/** Sets the search widget wrapper associated with this button.
|
||||
* Calling this will automatically set the available flags to match those
|
||||
* supported by the wrapper and reset the active flags to match the wrapper's
|
||||
* default flags.
|
||||
* @param wrapper search wrapper. Ownership is not transferred.
|
||||
*/
|
||||
void setSearchWidgetWrapper( QgsSearchWidgetWrapper* wrapper );
|
||||
|
||||
/** Sets the available filter flags to show in the widget. Any active flags
|
||||
* (see activeFlags()) which are not present in the new available filter
|
||||
* flags will be cleared;
|
||||
* @param flags available flags to show in widget
|
||||
* @see availableFlags()
|
||||
* @see setActiveFlags()
|
||||
*/
|
||||
void setAvailableFlags( QgsSearchWidgetWrapper::FilterFlags flags );
|
||||
|
||||
/** Returns the available filter flags shown in the widget.
|
||||
* @see setAvailableFlags()
|
||||
* @see activeFlags()
|
||||
*/
|
||||
QgsSearchWidgetWrapper::FilterFlags availableFlags() const;
|
||||
|
||||
/** Sets the current active filter flags for the widget. Any flags
|
||||
* which are not present in the available filter flags (see availableFlags())
|
||||
* will not be set.
|
||||
* @param flags active flags to show in widget
|
||||
* @see toggleFlag()
|
||||
* @see activeFlags()
|
||||
* @see setAvailableFlags()
|
||||
*/
|
||||
void setActiveFlags( QgsSearchWidgetWrapper::FilterFlags flags );
|
||||
|
||||
/** Toggles an individual active filter flag for the widget. Any flags
|
||||
* which are not present in the available filter flags (see availableFlags())
|
||||
* will be ignore. Other flags may be cleared if they conflict with the newly
|
||||
* toggled flag.
|
||||
* @param flag flag to toggle
|
||||
* @see setActiveFlags()
|
||||
* @see activeFlags()
|
||||
*/
|
||||
void toggleFlag( QgsSearchWidgetWrapper::FilterFlag flag );
|
||||
|
||||
/** Returns the active filter flags shown in the widget.
|
||||
* @see setActiveFlags()
|
||||
* @see toggleFlag()
|
||||
* @see availableFlags()
|
||||
*/
|
||||
QgsSearchWidgetWrapper::FilterFlags activeFlags() const;
|
||||
|
||||
/** Returns true if the widget is set to be included in the search.
|
||||
* @see setInactive()
|
||||
* @see setActive()
|
||||
*/
|
||||
bool isActive() const;
|
||||
|
||||
public slots:
|
||||
|
||||
/** Sets the search widget as inactive, ie do not search the corresponding field.
|
||||
* @see isActive()
|
||||
* @see setActive()
|
||||
*/
|
||||
void setInactive();
|
||||
|
||||
/** Sets the search widget as active by selecting the first available search type.
|
||||
* @see isActive()
|
||||
* @see setInactive()
|
||||
*/
|
||||
void setActive();
|
||||
|
||||
};
|
34
python/gui/editorwidgets/qgsvaluemapsearchwidgetwrapper.sip
Normal file
34
python/gui/editorwidgets/qgsvaluemapsearchwidgetwrapper.sip
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Wraps a value map search widget. This widget will offer a combobox with values from another layer
|
||||
* referenced by a foreign key (a constraint may be set but is not required on data level).
|
||||
* It will be used as a search widget and produces expression to look for in the layer.
|
||||
*/
|
||||
|
||||
class QgsValueMapSearchWidgetWrapper : QgsSearchWidgetWrapper
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgsvaluemapsearchwidgetwrapper.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
explicit QgsValueMapSearchWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* parent /TransferThis/ = nullptr );
|
||||
bool applyDirectly();
|
||||
QString expression();
|
||||
bool valid() const;
|
||||
FilterFlags supportedFlags() const;
|
||||
FilterFlags defaultFlags() const;
|
||||
virtual QString createExpression( FilterFlags flags ) const;
|
||||
|
||||
public slots:
|
||||
|
||||
virtual void clearWidget();
|
||||
virtual void setEnabled( bool enabled );
|
||||
|
||||
protected:
|
||||
QWidget* createWidget( QWidget* parent );
|
||||
void initWidget( QWidget* editor );
|
||||
|
||||
protected slots:
|
||||
void setExpression( QString exp );
|
||||
|
||||
};
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Wraps a value relation search widget. This widget will offer a combobox with values from another layer
|
||||
* referenced by a foreign key (a constraint may be set but is not required on data level).
|
||||
* It will be used as a search widget and produces expression to look for in the layer.
|
||||
*/
|
||||
|
||||
class QgsValueRelationSearchWidgetWrapper : QgsSearchWidgetWrapper
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgsvaluerelationsearchwidgetwrapper.h>
|
||||
%End
|
||||
public:
|
||||
explicit QgsValueRelationSearchWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* parent /TransferThis/ = nullptr );
|
||||
bool applyDirectly();
|
||||
QString expression();
|
||||
bool valid() const;
|
||||
QVariant value() const;
|
||||
FilterFlags supportedFlags() const;
|
||||
FilterFlags defaultFlags() const;
|
||||
virtual QString createExpression( FilterFlags flags ) const;
|
||||
|
||||
public slots:
|
||||
|
||||
virtual void clearWidget();
|
||||
virtual void setEnabled( bool enabled );
|
||||
|
||||
protected:
|
||||
QWidget* createWidget( QWidget* parent );
|
||||
void initWidget( QWidget* editor );
|
||||
|
||||
public slots:
|
||||
|
||||
//! Called when current value of search widget changes
|
||||
void onValueChanged();
|
||||
|
||||
protected slots:
|
||||
void setExpression( QString exp );
|
||||
|
||||
};
|
@ -253,11 +253,15 @@
|
||||
%Include editorwidgets/core/qgssearchwidgetwrapper.sip
|
||||
%Include editorwidgets/core/qgswidgetwrapper.sip
|
||||
%Include editorwidgets/qgsdatetimeedit.sip
|
||||
%Include editorwidgets/qgsdefaultsearchwidgetwrapper.sip
|
||||
%Include editorwidgets/qgsdoublespinbox.sip
|
||||
%Include editorwidgets/qgsmultiedittoolbutton.sip
|
||||
%Include editorwidgets/qgsrelationreferencewidget.sip
|
||||
%Include editorwidgets/qgsrelationreferencewidgetwrapper.sip
|
||||
%Include editorwidgets/qgsrelationwidgetwrapper.sip
|
||||
%Include editorwidgets/qgssearchwidgettoolbutton.sip
|
||||
%Include editorwidgets/qgsspinbox.sip
|
||||
%Include editorwidgets/qgsvaluemapsearchwidgetwrapper.sip
|
||||
%Include editorwidgets/qgsvaluerelationsearchwidgetwrapper.sip
|
||||
|
||||
%Include layertree/qgslayertreeview.sip
|
||||
|
@ -25,8 +25,18 @@ class QgsAttributeForm : QWidget
|
||||
enum Mode
|
||||
{
|
||||
SingleEditMode, /*!< Single edit mode, for editing a single feature */
|
||||
AddFeatureMode, /*!< Add feature mode, for setting attributes for a new feature. In this mode the dialog will be editable even with an invalid feature and
|
||||
will add a new feature when the form is accepted. */
|
||||
MultiEditMode, /*!< Multi edit mode, for editing fields of multiple features at once */
|
||||
// TODO: SearchMode, /*!< Form values are used for searching/filtering the layer */
|
||||
SearchMode, /*!< Form values are used for searching/filtering the layer */
|
||||
};
|
||||
|
||||
//! Filter types
|
||||
enum FilterType
|
||||
{
|
||||
ReplaceFilter, /*!< Filter should replace any existing filter */
|
||||
FilterAnd, /*!< Filter should be combined using "AND" */
|
||||
FilterOr, /*!< Filter should be combined using "OR" */
|
||||
};
|
||||
|
||||
explicit QgsAttributeForm( QgsVectorLayer* vl, const QgsFeature& feature = QgsFeature(), const QgsAttributeEditorContext& context = QgsAttributeEditorContext(), QWidget *parent /TransferThis/ = 0 );
|
||||
@ -139,6 +149,18 @@ class QgsAttributeForm : QWidget
|
||||
*/
|
||||
void featureSaved( const QgsFeature& feature );
|
||||
|
||||
/** Is emitted when a filter expression is set using the form.
|
||||
* @param expression filter expression
|
||||
* @param type filter type
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void filterExpressionSet( const QString& expression, QgsAttributeForm::FilterType type );
|
||||
|
||||
/** Emitted when the form changes mode.
|
||||
* @param mode new mode
|
||||
*/
|
||||
void modeChanged( QgsAttributeForm::Mode mode );
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* Call this to change the content of a given attribute. Will update the editor(s) related to this field.
|
||||
@ -181,6 +203,11 @@ class QgsAttributeForm : QWidget
|
||||
*/
|
||||
void resetValues();
|
||||
|
||||
/** Resets the search/filter form values.
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void resetSearch();
|
||||
|
||||
/**
|
||||
* reload current feature
|
||||
*/
|
||||
|
@ -19,17 +19,31 @@ class QgsAttributeFormEditorWidget : QWidget
|
||||
{
|
||||
DefaultMode, /*!< Default mode, only the editor widget is shown */
|
||||
MultiEditMode, /*!< Multi edit mode, both the editor widget and a QgsMultiEditToolButton is shown */
|
||||
// TODO: SearchMode, /*!< Layer search/filter mode */
|
||||
SearchMode, /*!< Layer search/filter mode */
|
||||
};
|
||||
|
||||
/** Constructor for QgsAttributeFormEditorWidget.
|
||||
* @param editorWidget associated editor widget wrapper
|
||||
* @param editorWidget associated editor widget wrapper (for default/edit modes)
|
||||
* @param form parent attribute form
|
||||
*/
|
||||
explicit QgsAttributeFormEditorWidget( QgsEditorWidgetWrapper* editorWidget, QgsAttributeForm* form /TransferThis/ );
|
||||
|
||||
explicit QgsAttributeFormEditorWidget( QgsEditorWidgetWrapper* editorWidget,
|
||||
QgsAttributeForm* form /TransferThis/ );
|
||||
~QgsAttributeFormEditorWidget();
|
||||
|
||||
/** Sets the search widget wrapper for the widget used when the form is in
|
||||
* search mode.
|
||||
* @param wrapper search widget wrapper.
|
||||
* @note the search widget wrapper should be created using searchWidgetFrame()
|
||||
* as its parent
|
||||
*/
|
||||
void setSearchWidgetWrapper( QgsSearchWidgetWrapper* wrapper );
|
||||
|
||||
/** Returns the widget which should be used as a parent during construction
|
||||
* of the search widget wrapper.
|
||||
* @see setSearchWidgetWrapper()
|
||||
*/
|
||||
QWidget* searchWidgetFrame();
|
||||
|
||||
/** Sets the current mode for the widget. The widget will adapt its state and visible widgets to
|
||||
* reflect the updated mode. Eg, showing multi edit tool buttons if the mode is set to MultiEditMode.
|
||||
* @param mode widget mode
|
||||
@ -57,6 +71,12 @@ class QgsAttributeFormEditorWidget : QWidget
|
||||
*/
|
||||
QVariant currentValue() const;
|
||||
|
||||
/** Creates an expression matching the current search filter value and
|
||||
* search properties represented in the widget.
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
QString currentFilterExpression() const;
|
||||
|
||||
public slots:
|
||||
|
||||
/** Sets whether the widget should be displayed in a "mixed values" mode.
|
||||
@ -68,11 +88,22 @@ class QgsAttributeFormEditorWidget : QWidget
|
||||
*/
|
||||
void changesCommitted();
|
||||
|
||||
/** Resets the search/filter value of the widget.
|
||||
*/
|
||||
void resetSearch();
|
||||
|
||||
signals:
|
||||
|
||||
//! Emitted when the widget's value changes
|
||||
//! @param value new widget value
|
||||
void valueChanged( const QVariant& value );
|
||||
|
||||
protected:
|
||||
|
||||
/** Returns a pointer to the search widget tool button in the widget.
|
||||
* @note this method is in place for unit testing only, and is not considered
|
||||
* stable API
|
||||
*/
|
||||
QgsSearchWidgetToolButton* searchWidgetToolButton();
|
||||
|
||||
};
|
||||
|
@ -166,6 +166,8 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid
|
||||
|
||||
// connect table info to window
|
||||
connect( mMainView, SIGNAL( filterChanged() ), this, SLOT( updateTitle() ) );
|
||||
connect( mMainView, SIGNAL( filterExpressionSet( QString, QgsAttributeForm::FilterType ) ), this, SLOT( setFilterExpression( QString, QgsAttributeForm::FilterType ) ) );
|
||||
connect( mMainView, SIGNAL( formModeChanged( QgsAttributeForm::Mode ) ), this, SLOT( viewModeChanged( QgsAttributeForm::Mode ) ) );
|
||||
|
||||
// info from table to application
|
||||
connect( this, SIGNAL( saveEdits( QgsMapLayer * ) ), QgisApp::instance(), SLOT( saveEdits( QgsMapLayer * ) ) );
|
||||
@ -265,6 +267,7 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid
|
||||
mMainViewButtonGroup->button( initialView )->setChecked( true );
|
||||
|
||||
connect( mToggleMultiEditButton, SIGNAL( toggled( bool ) ), mMainView, SLOT( setMultiEditEnabled( bool ) ) );
|
||||
connect( mSearchFormButton, SIGNAL( toggled( bool ) ), mMainView, SLOT( toggleSearchMode( bool ) ) );
|
||||
updateMultiEditButtonState();
|
||||
|
||||
editingToggled();
|
||||
@ -379,6 +382,12 @@ void QgsAttributeTableDialog::updateFieldFromExpressionSelected()
|
||||
runFieldCalculation( mLayer, mFieldCombo->currentText(), mUpdateExpressionText->asExpression(), filteredIds );
|
||||
}
|
||||
|
||||
void QgsAttributeTableDialog::viewModeChanged( QgsAttributeForm::Mode mode )
|
||||
{
|
||||
if ( mode != QgsAttributeForm::SearchMode )
|
||||
mSearchFormButton->setChecked( false );
|
||||
}
|
||||
|
||||
void QgsAttributeTableDialog::runFieldCalculation( QgsVectorLayer* layer, const QString& fieldName, const QString& expression, const QgsFeatureIds& filteredIds )
|
||||
{
|
||||
QApplication::setOverrideCursor( Qt::WaitCursor );
|
||||
@ -524,6 +533,7 @@ void QgsAttributeTableDialog::filterShowAll()
|
||||
mFilterButton->setDefaultAction( mActionShowAllFilter );
|
||||
mFilterButton->setPopupMode( QToolButton::InstantPopup );
|
||||
mFilterQuery->setVisible( false );
|
||||
mFilterQuery->setText( QString() );
|
||||
if ( mCurrentSearchWidgetWrapper )
|
||||
{
|
||||
mCurrentSearchWidgetWrapper->widget()->setVisible( false );
|
||||
@ -717,6 +727,9 @@ void QgsAttributeTableDialog::on_mMainView_currentChanged( int viewMode )
|
||||
mMainViewButtonGroup->button( viewMode )->click();
|
||||
updateMultiEditButtonState();
|
||||
|
||||
if ( viewMode == 0 )
|
||||
mSearchFormButton->setChecked( false );
|
||||
|
||||
QSettings s;
|
||||
s.setValue( "/qgis/attributeTableLastView", static_cast< int >( viewMode ) );
|
||||
}
|
||||
@ -739,6 +752,10 @@ void QgsAttributeTableDialog::editingToggled()
|
||||
mSaveEditsButton->setEnabled( mLayer->isEditable() );
|
||||
mReloadButton->setEnabled( ! mLayer->isEditable() );
|
||||
updateMultiEditButtonState();
|
||||
if ( mLayer->isEditable() )
|
||||
{
|
||||
mSearchFormButton->setChecked( false );
|
||||
}
|
||||
mToggleEditingButton->blockSignals( false );
|
||||
|
||||
bool canChangeAttributes = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
|
||||
@ -866,11 +883,39 @@ void QgsAttributeTableDialog::openConditionalStyles()
|
||||
mMainView->openConditionalStyles();
|
||||
}
|
||||
|
||||
void QgsAttributeTableDialog::setFilterExpression( const QString& filterString )
|
||||
void QgsAttributeTableDialog::setFilterExpression( const QString& filterString, QgsAttributeForm::FilterType type )
|
||||
{
|
||||
QString filter;
|
||||
if ( !mFilterQuery->text().isEmpty() && !filterString.isEmpty() )
|
||||
{
|
||||
switch ( type )
|
||||
{
|
||||
case QgsAttributeForm::ReplaceFilter:
|
||||
filter = filterString;
|
||||
break;
|
||||
|
||||
case QgsAttributeForm::FilterAnd:
|
||||
filter = QString( "(%1) AND (%2)" ).arg( mFilterQuery->text(), filterString );
|
||||
break;
|
||||
|
||||
case QgsAttributeForm::FilterOr:
|
||||
filter = QString( "(%1) OR (%2)" ).arg( mFilterQuery->text(), filterString );
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( !filterString.isEmpty() )
|
||||
{
|
||||
filter = filterString;
|
||||
}
|
||||
else
|
||||
{
|
||||
filterShowAll();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !mCurrentSearchWidgetWrapper || !mCurrentSearchWidgetWrapper->applyDirectly() )
|
||||
{
|
||||
mFilterQuery->setText( filterString );
|
||||
mFilterQuery->setText( filter );
|
||||
mFilterButton->setDefaultAction( mActionAdvancedFilter );
|
||||
mFilterButton->setPopupMode( QToolButton::MenuButtonPopup );
|
||||
mFilterQuery->setVisible( true );
|
||||
@ -890,7 +935,7 @@ void QgsAttributeTableDialog::setFilterExpression( const QString& filterString )
|
||||
myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
|
||||
|
||||
// parse search string and build parsed tree
|
||||
QgsExpression filterExpression( filterString );
|
||||
QgsExpression filterExpression( filter );
|
||||
if ( filterExpression.hasParserError() )
|
||||
{
|
||||
QgisApp::instance()->messageBar()->pushMessage( tr( "Parsing error" ), filterExpression.parserErrorString(), QgsMessageBar::WARNING, QgisApp::instance()->messageTimeout() );
|
||||
|
@ -56,18 +56,18 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
|
||||
QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::Window );
|
||||
~QgsAttributeTableDialog();
|
||||
|
||||
/**
|
||||
* Sets the filter expression to filter visible features
|
||||
* @param filterString filter query string. QgsExpression compatible.
|
||||
*/
|
||||
void setFilterExpression( const QString& filterString );
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* Toggles editing mode
|
||||
*/
|
||||
void editingToggled();
|
||||
|
||||
/**
|
||||
* Sets the filter expression to filter visible features
|
||||
* @param filterString filter query string. QgsExpression compatible.
|
||||
*/
|
||||
void setFilterExpression( const QString& filterString, QgsAttributeForm::FilterType type = QgsAttributeForm::ReplaceFilter );
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* Copies selected rows to the clipboard
|
||||
@ -212,6 +212,7 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
|
||||
void runFieldCalculation( QgsVectorLayer* layer, const QString& fieldName, const QString& expression, const QgsFeatureIds& filteredIds = QgsFeatureIds() );
|
||||
void updateFieldFromExpression();
|
||||
void updateFieldFromExpressionSelected();
|
||||
void viewModeChanged( QgsAttributeForm::Mode mode );
|
||||
|
||||
private:
|
||||
QMenu* mMenuActions;
|
||||
|
@ -123,6 +123,7 @@ SET(QGIS_GUI_SRCS
|
||||
editorwidgets/qgsrangeconfigdlg.cpp
|
||||
editorwidgets/qgsrangewidgetwrapper.cpp
|
||||
editorwidgets/qgsrangewidgetfactory.cpp
|
||||
editorwidgets/qgssearchwidgettoolbutton.cpp
|
||||
editorwidgets/qgsspinbox.cpp
|
||||
editorwidgets/qgsrelationwidgetwrapper.cpp
|
||||
editorwidgets/qgsrelationreferenceconfigdlg.cpp
|
||||
@ -545,6 +546,7 @@ SET(QGIS_GUI_MOC_HDRS
|
||||
editorwidgets/qgsrelationreferencewidget.h
|
||||
editorwidgets/qgsrelationreferencewidgetwrapper.h
|
||||
editorwidgets/qgsrelationwidgetwrapper.h
|
||||
editorwidgets/qgssearchwidgettoolbutton.h
|
||||
editorwidgets/qgsspinbox.h
|
||||
editorwidgets/qgstexteditconfigdlg.h
|
||||
editorwidgets/qgstexteditwrapper.h
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsactionmanager.h"
|
||||
#include "qgsattributeform.h"
|
||||
#include "qgsattributetablemodel.h"
|
||||
#include "qgsdualview.h"
|
||||
#include "qgsexpressionbuilderdialog.h"
|
||||
@ -84,8 +83,9 @@ void QgsDualView::init( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas, const Qg
|
||||
mAttributeForm->hideButtonBox();
|
||||
|
||||
connect( mAttributeForm, SIGNAL( attributeChanged( QString, QVariant ) ), this, SLOT( featureFormAttributeChanged() ) );
|
||||
connect( mAttributeForm, SIGNAL( modeChanged( QgsAttributeForm::Mode ) ), this, SIGNAL( formModeChanged( QgsAttributeForm::Mode ) ) );
|
||||
connect( mMasterModel, SIGNAL( modelChanged() ), mAttributeForm, SLOT( refreshFeature() ) );
|
||||
|
||||
connect( mAttributeForm, SIGNAL( filterExpressionSet( QString, QgsAttributeForm::FilterType ) ), this, SIGNAL( filterExpressionSet( QString, QgsAttributeForm::FilterType ) ) );
|
||||
if ( mFeatureListPreviewButton->defaultAction() )
|
||||
mFeatureList->setDisplayExpression( mDisplayExpression );
|
||||
else
|
||||
@ -308,6 +308,19 @@ void QgsDualView::setMultiEditEnabled( bool enabled )
|
||||
mAttributeForm->setMode( enabled ? QgsAttributeForm::MultiEditMode : QgsAttributeForm::SingleEditMode );
|
||||
}
|
||||
|
||||
void QgsDualView::toggleSearchMode( bool enabled )
|
||||
{
|
||||
if ( enabled )
|
||||
{
|
||||
setView( AttributeEditor );
|
||||
mAttributeForm->setMode( QgsAttributeForm::SearchMode );
|
||||
}
|
||||
else
|
||||
{
|
||||
mAttributeForm->setMode( QgsAttributeForm::SingleEditMode );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsDualView::previewExpressionBuilder()
|
||||
{
|
||||
// Show expression builder
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "qgsattributetablefiltermodel.h"
|
||||
#include "qgscachedfeatureiterator.h"
|
||||
#include "qgsdistancearea.h"
|
||||
#include "qgsattributeform.h"
|
||||
|
||||
class QgsAttributeForm;
|
||||
class QgsFeatureRequest;
|
||||
@ -219,6 +220,12 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
|
||||
*/
|
||||
void setMultiEditEnabled( bool enabled );
|
||||
|
||||
/** Toggles whether search mode should be enabled in the form.
|
||||
* @param enabled set to true to switch on search mode
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void toggleSearchMode( bool enabled );
|
||||
|
||||
signals:
|
||||
/**
|
||||
* Is emitted, whenever the display expression is successfully changed
|
||||
@ -231,6 +238,18 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
|
||||
*/
|
||||
void filterChanged();
|
||||
|
||||
/** Is emitted when a filter expression is set using the view.
|
||||
* @param expression filter expression
|
||||
* @param type filter type
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void filterExpressionSet( const QString& expression, QgsAttributeForm::FilterType type );
|
||||
|
||||
/** Emitted when the form changes mode.
|
||||
* @param mode new mode
|
||||
*/
|
||||
void formModeChanged( QgsAttributeForm::Mode mode );
|
||||
|
||||
private slots:
|
||||
|
||||
void on_mFeatureList_aboutToChangeEditSelection( bool& ok );
|
||||
|
@ -20,6 +20,57 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
QList<QgsSearchWidgetWrapper::FilterFlag> QgsSearchWidgetWrapper::exclusiveFilterFlags()
|
||||
{
|
||||
return QList<FilterFlag>()
|
||||
<< EqualTo
|
||||
<< NotEqualTo
|
||||
<< GreaterThan
|
||||
<< LessThan
|
||||
<< GreaterThanOrEqualTo
|
||||
<< LessThanOrEqualTo
|
||||
<< Between
|
||||
<< Contains
|
||||
<< DoesNotContain
|
||||
<< IsNull;
|
||||
}
|
||||
|
||||
QList<QgsSearchWidgetWrapper::FilterFlag> QgsSearchWidgetWrapper::nonExclusiveFilterFlags()
|
||||
{
|
||||
return QList<FilterFlag>()
|
||||
<< CaseInsensitive;
|
||||
}
|
||||
|
||||
QString QgsSearchWidgetWrapper::toString( QgsSearchWidgetWrapper::FilterFlag flag )
|
||||
{
|
||||
switch ( flag )
|
||||
{
|
||||
case EqualTo:
|
||||
return QObject::tr( "Equal to (=)" );
|
||||
case NotEqualTo:
|
||||
return QObject::tr( "Not equal to" );
|
||||
case GreaterThan:
|
||||
return QObject::tr( "Greater than (>)" );
|
||||
case LessThan:
|
||||
return QObject::tr( "Less than (<)" );
|
||||
case GreaterThanOrEqualTo:
|
||||
return QObject::tr( "Greater than or equal to (>=)" );
|
||||
case LessThanOrEqualTo:
|
||||
return QObject::tr( "Less than or equal to (<=)" );
|
||||
case Between:
|
||||
return QObject::tr( "Between (inclusive)" );
|
||||
case CaseInsensitive:
|
||||
return QObject::tr( "Case insensitive" );
|
||||
case Contains:
|
||||
return QObject::tr( "Contains" );
|
||||
case DoesNotContain:
|
||||
return QObject::tr( "Does not contain" );
|
||||
case IsNull:
|
||||
return QObject::tr( "Is missing (null)" );
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QgsSearchWidgetWrapper::QgsSearchWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* parent )
|
||||
: QgsWidgetWrapper( vl, nullptr, parent )
|
||||
, mExpression( QString() )
|
||||
@ -27,6 +78,16 @@ QgsSearchWidgetWrapper::QgsSearchWidgetWrapper( QgsVectorLayer* vl, int fieldIdx
|
||||
{
|
||||
}
|
||||
|
||||
QgsSearchWidgetWrapper::FilterFlags QgsSearchWidgetWrapper::supportedFlags() const
|
||||
{
|
||||
return EqualTo;
|
||||
}
|
||||
|
||||
QgsSearchWidgetWrapper::FilterFlags QgsSearchWidgetWrapper::defaultFlags() const
|
||||
{
|
||||
return FilterFlags();
|
||||
}
|
||||
|
||||
|
||||
void QgsSearchWidgetWrapper::setFeature( const QgsFeature& feature )
|
||||
{
|
||||
|
@ -41,6 +41,43 @@ class GUI_EXPORT QgsSearchWidgetWrapper : public QgsWidgetWrapper
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
//! Flags which indicate what types of filtering and searching is possible using the widget
|
||||
//! @note added in QGIS 2.16
|
||||
enum FilterFlag
|
||||
{
|
||||
EqualTo = 1 << 1, /*!< Supports equal to */
|
||||
NotEqualTo = 1 << 2, /*!< Supports not equal to */
|
||||
GreaterThan = 1 << 3, /*!< Supports greater than */
|
||||
LessThan = 1 << 4, /*!< Supports less than */
|
||||
GreaterThanOrEqualTo = 1 << 5, /*!< Supports >= */
|
||||
LessThanOrEqualTo = 1 << 6, /*!< Supports <= */
|
||||
Between = 1 << 7, /*!< Supports searches between two values */
|
||||
CaseInsensitive = 1 << 8, /*!< Supports case insensitive searching */
|
||||
Contains = 1 << 9, /*!< Supports value "contains" searching */
|
||||
DoesNotContain = 1 << 10, /*!< Supports value does not contain searching */
|
||||
IsNull = 1 << 11, /*!< Supports searching for null values */
|
||||
};
|
||||
Q_DECLARE_FLAGS( FilterFlags, FilterFlag )
|
||||
|
||||
/** Returns a list of exclusive filter flags, which cannot be combined with other flags (eg EqualTo/NotEqualTo)
|
||||
* @note added in QGIS 2.16
|
||||
* @see nonExclusiveFilterFlags()
|
||||
*/
|
||||
static QList< FilterFlag > exclusiveFilterFlags();
|
||||
|
||||
/** Returns a list of non-exclusive filter flags, which can be combined with other flags (eg CaseInsensitive)
|
||||
* @note added in QGIS 2.16
|
||||
* @see exclusiveFilterFlags()
|
||||
*/
|
||||
static QList< FilterFlag > nonExclusiveFilterFlags();
|
||||
|
||||
/** Returns a translated string representing a filter flag.
|
||||
* @param flag flag to convert to string
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
static QString toString( FilterFlag flag );
|
||||
|
||||
/**
|
||||
* Create a new widget wrapper
|
||||
*
|
||||
@ -50,6 +87,18 @@ class GUI_EXPORT QgsSearchWidgetWrapper : public QgsWidgetWrapper
|
||||
*/
|
||||
explicit QgsSearchWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* parent = nullptr );
|
||||
|
||||
/** Returns filter flags supported by the search widget.
|
||||
* @note added in QGIS 2.16
|
||||
* @see defaultFlags()
|
||||
*/
|
||||
virtual FilterFlags supportedFlags() const;
|
||||
|
||||
/** Returns the filter flags which should be set by default for the search widget.
|
||||
* @note added in QGIS 2.16
|
||||
* @see supportedFlags()
|
||||
*/
|
||||
virtual FilterFlags defaultFlags() const;
|
||||
|
||||
/**
|
||||
* Will be used to access the widget's value. Read the value from the widget and
|
||||
* return it properly formatted to be saved in the attribute.
|
||||
@ -60,12 +109,34 @@ class GUI_EXPORT QgsSearchWidgetWrapper : public QgsWidgetWrapper
|
||||
* @return The current value the widget represents
|
||||
*/
|
||||
virtual QString expression() = 0;
|
||||
|
||||
/**
|
||||
* If this is true, then this search widget should take effect directly
|
||||
* when its expression changes
|
||||
*/
|
||||
virtual bool applyDirectly() = 0;
|
||||
|
||||
/** Creates a filter expression based on the current state of the search widget
|
||||
* and the specified filter flags.
|
||||
* @param flags filter flags
|
||||
* @returns filter expression
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
// TODO QGIS 3.0 - make pure virtual
|
||||
virtual QString createExpression( FilterFlags flags ) const { Q_UNUSED( flags ); return "TRUE"; }
|
||||
|
||||
public slots:
|
||||
|
||||
/** Clears the widget's current value and resets it back to the default state
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
virtual void clearWidget() {}
|
||||
|
||||
/** Toggles whether the search widget is enabled or disabled.
|
||||
* @param enabled set to true to enable widget
|
||||
*/
|
||||
virtual void setEnabled( bool enabled ) override { Q_UNUSED( enabled ); }
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
@ -74,6 +145,17 @@ class GUI_EXPORT QgsSearchWidgetWrapper : public QgsWidgetWrapper
|
||||
*/
|
||||
void expressionChanged( const QString& exp );
|
||||
|
||||
/** Emitted when a user changes the value of the search widget.
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void valueChanged();
|
||||
|
||||
/** Emitted when a user changes the value of the search widget back
|
||||
* to an empty, default state.
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void valueCleared();
|
||||
|
||||
protected slots:
|
||||
|
||||
virtual void setExpression( QString value ) = 0;
|
||||
@ -90,4 +172,6 @@ class GUI_EXPORT QgsSearchWidgetWrapper : public QgsWidgetWrapper
|
||||
// We'll use this class inside a QVariant in the widgets properties
|
||||
Q_DECLARE_METATYPE( QgsSearchWidgetWrapper* )
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsSearchWidgetWrapper::FilterFlags )
|
||||
|
||||
#endif // QGSSEARCHWIDGETWRAPPER_H
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
#include "qgsfield.h"
|
||||
#include "qgsfieldvalidator.h"
|
||||
|
||||
#include "qgsexpression.h"
|
||||
#include <QSettings>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
@ -30,7 +30,6 @@ QgsDefaultSearchWidgetWrapper::QgsDefaultSearchWidgetWrapper( QgsVectorLayer* vl
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QString QgsDefaultSearchWidgetWrapper::expression()
|
||||
{
|
||||
return mExpression;
|
||||
@ -86,17 +85,178 @@ bool QgsDefaultSearchWidgetWrapper::applyDirectly()
|
||||
return false;
|
||||
}
|
||||
|
||||
QgsSearchWidgetWrapper::FilterFlags QgsDefaultSearchWidgetWrapper::supportedFlags() const
|
||||
{
|
||||
FilterFlags flags = EqualTo | NotEqualTo | IsNull;
|
||||
|
||||
QVariant::Type fldType = layer()->fields().at( mFieldIdx ).type();
|
||||
switch ( fldType )
|
||||
{
|
||||
case QVariant::Int:
|
||||
case QVariant::UInt:
|
||||
case QVariant::Double:
|
||||
case QVariant::LongLong:
|
||||
case QVariant::ULongLong:
|
||||
//numeric
|
||||
flags |= GreaterThan | LessThan | GreaterThanOrEqualTo | LessThanOrEqualTo;
|
||||
break;
|
||||
|
||||
case QVariant::Date:
|
||||
case QVariant::DateTime:
|
||||
case QVariant::Time:
|
||||
flags |= GreaterThan | LessThan | GreaterThanOrEqualTo | LessThanOrEqualTo | Between;
|
||||
break;
|
||||
|
||||
case QVariant::String:
|
||||
flags |= Contains | DoesNotContain;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
QgsSearchWidgetWrapper::FilterFlags QgsDefaultSearchWidgetWrapper::defaultFlags() const
|
||||
{
|
||||
QVariant::Type fldType = layer()->fields().at( mFieldIdx ).type();
|
||||
switch ( fldType )
|
||||
{
|
||||
case QVariant::Int:
|
||||
case QVariant::UInt:
|
||||
case QVariant::Double:
|
||||
case QVariant::LongLong:
|
||||
case QVariant::ULongLong:
|
||||
//numeric
|
||||
return EqualTo;
|
||||
|
||||
case QVariant::Date:
|
||||
case QVariant::DateTime:
|
||||
case QVariant::Time:
|
||||
return EqualTo;
|
||||
|
||||
case QVariant::String:
|
||||
return Contains;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return EqualTo;
|
||||
}
|
||||
|
||||
QString QgsDefaultSearchWidgetWrapper::createExpression( QgsSearchWidgetWrapper::FilterFlags flags ) const
|
||||
{
|
||||
//clear any unsupported flags
|
||||
flags &= supportedFlags();
|
||||
|
||||
QVariant::Type fldType = layer()->fields().at( mFieldIdx ).type();
|
||||
QString fieldName = QgsExpression::quotedColumnRef( layer()->fields().at( mFieldIdx ).name() );
|
||||
|
||||
if ( flags & IsNull )
|
||||
return fieldName + " IS NULL";
|
||||
|
||||
switch ( fldType )
|
||||
{
|
||||
case QVariant::Int:
|
||||
case QVariant::UInt:
|
||||
case QVariant::Double:
|
||||
case QVariant::LongLong:
|
||||
case QVariant::ULongLong:
|
||||
{
|
||||
if ( flags & EqualTo )
|
||||
return fieldName + '=' + mLineEdit->text();
|
||||
else if ( flags & NotEqualTo )
|
||||
return fieldName + "<>" + mLineEdit->text();
|
||||
else if ( flags & GreaterThan )
|
||||
return fieldName + '>' + mLineEdit->text();
|
||||
else if ( flags & LessThan )
|
||||
return fieldName + '<' + mLineEdit->text();
|
||||
else if ( flags & GreaterThanOrEqualTo )
|
||||
return fieldName + ">=" + mLineEdit->text();
|
||||
else if ( flags & LessThanOrEqualTo )
|
||||
return fieldName + "<=" + mLineEdit->text();
|
||||
break;
|
||||
}
|
||||
|
||||
case QVariant::Date:
|
||||
case QVariant::DateTime:
|
||||
case QVariant::Time:
|
||||
{
|
||||
if ( flags & EqualTo )
|
||||
return fieldName + "='" + mLineEdit->text() + '\'';
|
||||
else if ( flags & NotEqualTo )
|
||||
return fieldName + "<>'" + mLineEdit->text() + '\'';
|
||||
else if ( flags & GreaterThan )
|
||||
return fieldName + ">'" + mLineEdit->text() + '\'';
|
||||
else if ( flags & LessThan )
|
||||
return fieldName + "<'" + mLineEdit->text() + '\'';
|
||||
else if ( flags & GreaterThanOrEqualTo )
|
||||
return fieldName + ">='" + mLineEdit->text() + '\'';
|
||||
else if ( flags & LessThanOrEqualTo )
|
||||
return fieldName + "<='" + mLineEdit->text() + '\'';
|
||||
break;
|
||||
}
|
||||
|
||||
case QVariant::String:
|
||||
{
|
||||
// case insensitive!
|
||||
if ( flags & EqualTo || flags & NotEqualTo )
|
||||
{
|
||||
if ( mCheckbox->isChecked() )
|
||||
return fieldName + ( flags & EqualTo ? "=" : "<>" )
|
||||
+ QgsExpression::quotedString( mLineEdit->text() );
|
||||
else
|
||||
return QString( "lower(%1)" ).arg( fieldName )
|
||||
+ ( flags & EqualTo ? "=" : "<>" ) +
|
||||
QString( "lower(%1)" ).arg( QgsExpression::quotedString( mLineEdit->text() ) );
|
||||
}
|
||||
else if ( flags & Contains || flags & DoesNotContain )
|
||||
{
|
||||
QString exp = fieldName + ( mCheckbox->isChecked() ? " LIKE " : " ILIKE " );
|
||||
QString value = QgsExpression::quotedString( mLineEdit->text() );
|
||||
value.chop( 1 );
|
||||
value = value.remove( 0, 1 );
|
||||
exp += "'%" + value + "%'";
|
||||
if ( flags & DoesNotContain )
|
||||
exp.prepend( "NOT (" ).append( ")" );
|
||||
return exp;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void QgsDefaultSearchWidgetWrapper::clearWidget()
|
||||
{
|
||||
mLineEdit->setText( QString() );
|
||||
}
|
||||
|
||||
void QgsDefaultSearchWidgetWrapper::setEnabled( bool enabled )
|
||||
{
|
||||
mLineEdit->setEnabled( enabled );
|
||||
mCheckbox->setEnabled( enabled );
|
||||
}
|
||||
|
||||
void QgsDefaultSearchWidgetWrapper::initWidget( QWidget* widget )
|
||||
{
|
||||
mContainer = widget;
|
||||
mContainer->setLayout( new QHBoxLayout() );
|
||||
mContainer->layout()->setMargin( 0 );
|
||||
mContainer->layout()->setContentsMargins( 0, 0, 0, 0 );
|
||||
mLineEdit = new QgsFilterLineEdit();
|
||||
mCheckbox = new QCheckBox( "Case sensitive" );
|
||||
mContainer->layout()->addWidget( mLineEdit );
|
||||
mContainer->layout()->addWidget( mCheckbox );
|
||||
connect( mLineEdit, SIGNAL( textChanged( QString ) ), this, SLOT( setExpression( QString ) ) );
|
||||
connect( mLineEdit, SIGNAL( textChanged( QString ) ), this, SLOT( textChanged( QString ) ) );
|
||||
connect( mLineEdit, SIGNAL( returnPressed() ), this, SLOT( filterChanged() ) );
|
||||
connect( mCheckbox, SIGNAL( stateChanged( int ) ), this, SLOT( setCaseString( int ) ) );
|
||||
connect( mLineEdit, SIGNAL( textEdited( QString ) ), this, SIGNAL( valueChanged() ) );
|
||||
mCheckbox->setChecked( Qt::Unchecked );
|
||||
mCaseString = "ILIKE";
|
||||
}
|
||||
@ -106,7 +266,25 @@ bool QgsDefaultSearchWidgetWrapper::valid() const
|
||||
return true;
|
||||
}
|
||||
|
||||
QgsFilterLineEdit* QgsDefaultSearchWidgetWrapper::lineEdit()
|
||||
{
|
||||
return mLineEdit;
|
||||
}
|
||||
|
||||
QCheckBox* QgsDefaultSearchWidgetWrapper::caseSensitiveCheckBox()
|
||||
{
|
||||
return mCheckbox;
|
||||
}
|
||||
|
||||
void QgsDefaultSearchWidgetWrapper::filterChanged()
|
||||
{
|
||||
emit expressionChanged( mExpression );
|
||||
}
|
||||
|
||||
void QgsDefaultSearchWidgetWrapper::textChanged( const QString& text )
|
||||
{
|
||||
if ( text.isEmpty() )
|
||||
emit valueCleared();
|
||||
|
||||
setExpression( text );
|
||||
}
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
/**
|
||||
* Wraps a search widget. Default form is just a QgsLineFilterEdit
|
||||
* \note not available in Python bindings
|
||||
*/
|
||||
|
||||
class GUI_EXPORT QgsDefaultSearchWidgetWrapper : public QgsSearchWidgetWrapper
|
||||
@ -36,6 +35,15 @@ class GUI_EXPORT QgsDefaultSearchWidgetWrapper : public QgsSearchWidgetWrapper
|
||||
public:
|
||||
QString expression() override;
|
||||
bool applyDirectly() override;
|
||||
FilterFlags supportedFlags() const override;
|
||||
FilterFlags defaultFlags() const override;
|
||||
virtual QString createExpression( FilterFlags flags ) const override;
|
||||
|
||||
public slots:
|
||||
|
||||
virtual void clearWidget() override;
|
||||
|
||||
virtual void setEnabled( bool enabled ) override;
|
||||
|
||||
protected slots:
|
||||
void setExpression( QString exp ) override;
|
||||
@ -43,12 +51,25 @@ class GUI_EXPORT QgsDefaultSearchWidgetWrapper : public QgsSearchWidgetWrapper
|
||||
private slots:
|
||||
void setCaseString( int caseSensitiveCheckState );
|
||||
void filterChanged();
|
||||
void textChanged( const QString& text );
|
||||
|
||||
protected:
|
||||
QWidget* createWidget( QWidget* parent ) override;
|
||||
void initWidget( QWidget* editor ) override;
|
||||
bool valid() const override;
|
||||
|
||||
/** Returns a pointer to the line edit part of the widget.
|
||||
* @note this method is in place for unit testing only, and is not considered
|
||||
* stable API
|
||||
*/
|
||||
QgsFilterLineEdit* lineEdit();
|
||||
|
||||
/** Returns a pointer to the case sensitivity check box in the widget.
|
||||
* @note this method is in place for unit testing only, and is not considered
|
||||
* stable API
|
||||
*/
|
||||
QCheckBox* caseSensitiveCheckBox();
|
||||
|
||||
private:
|
||||
QgsFilterLineEdit* mLineEdit;
|
||||
QCheckBox* mCheckbox;
|
||||
|
265
src/gui/editorwidgets/qgssearchwidgettoolbutton.cpp
Normal file
265
src/gui/editorwidgets/qgssearchwidgettoolbutton.cpp
Normal file
@ -0,0 +1,265 @@
|
||||
/***************************************************************************
|
||||
qgssearchwidgettoolbutton.cpp
|
||||
-----------------------------
|
||||
Date : May 2016
|
||||
Copyright : (C) 2016 Nyall Dawson
|
||||
Email : nyall dot dawson at gmail.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgssearchwidgettoolbutton.h"
|
||||
#include "qgsapplication.h"
|
||||
#include <QMenu>
|
||||
|
||||
QgsSearchWidgetToolButton::QgsSearchWidgetToolButton( QWidget* parent )
|
||||
: QToolButton( parent )
|
||||
, mAvailableFilterFlags( QgsSearchWidgetWrapper::EqualTo | QgsSearchWidgetWrapper::NotEqualTo | QgsSearchWidgetWrapper::CaseInsensitive )
|
||||
, mFilterFlags( QgsSearchWidgetWrapper::EqualTo )
|
||||
, mSearchWrapper( nullptr )
|
||||
, mMenu( nullptr )
|
||||
{
|
||||
setFocusPolicy( Qt::StrongFocus );
|
||||
setPopupMode( QToolButton::InstantPopup );
|
||||
|
||||
mMenu = new QMenu( this );
|
||||
connect( mMenu, SIGNAL( aboutToShow() ), this, SLOT( aboutToShowMenu() ) );
|
||||
setMenu( mMenu );
|
||||
|
||||
// sets initial appearance
|
||||
updateState();
|
||||
}
|
||||
|
||||
void QgsSearchWidgetToolButton::setSearchWidgetWrapper( QgsSearchWidgetWrapper* wrapper )
|
||||
{
|
||||
mSearchWrapper = wrapper;
|
||||
setAvailableFlags( mSearchWrapper->supportedFlags() );
|
||||
setActiveFlags( QgsSearchWidgetWrapper::FilterFlags() );
|
||||
connect( mSearchWrapper, SIGNAL( valueChanged() ), this, SLOT( searchWidgetValueChanged() ) );
|
||||
connect( mSearchWrapper, SIGNAL( valueCleared() ), this, SLOT( setInactive() ) );
|
||||
}
|
||||
|
||||
void QgsSearchWidgetToolButton::setAvailableFlags( QgsSearchWidgetWrapper::FilterFlags flags )
|
||||
{
|
||||
mFilterFlags &= flags;
|
||||
mAvailableFilterFlags = flags;
|
||||
updateState();
|
||||
}
|
||||
|
||||
void QgsSearchWidgetToolButton::setActiveFlags( QgsSearchWidgetWrapper::FilterFlags flags )
|
||||
{
|
||||
// sanitize list
|
||||
QgsSearchWidgetWrapper::FilterFlags newFlags;
|
||||
|
||||
// only accept a single exclusive flag
|
||||
Q_FOREACH ( QgsSearchWidgetWrapper::FilterFlag flag, QgsSearchWidgetWrapper::exclusiveFilterFlags() )
|
||||
{
|
||||
if ( !( mAvailableFilterFlags & flag ) )
|
||||
{
|
||||
//unsupported
|
||||
continue;
|
||||
}
|
||||
if ( flags & flag )
|
||||
{
|
||||
newFlags |= flag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Q_FOREACH ( QgsSearchWidgetWrapper::FilterFlag flag, QgsSearchWidgetWrapper::nonExclusiveFilterFlags() )
|
||||
{
|
||||
if ( !( mAvailableFilterFlags & flag ) )
|
||||
{
|
||||
//unsupported
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( flags & flag )
|
||||
newFlags |= flag;
|
||||
}
|
||||
|
||||
mFilterFlags = newFlags;
|
||||
|
||||
updateState();
|
||||
}
|
||||
|
||||
void QgsSearchWidgetToolButton::toggleFlag( QgsSearchWidgetWrapper::FilterFlag flag )
|
||||
{
|
||||
if ( !( flag & mAvailableFilterFlags ) )
|
||||
return;
|
||||
|
||||
if ( QgsSearchWidgetWrapper::nonExclusiveFilterFlags().contains( flag ) )
|
||||
{
|
||||
if ( flag & mFilterFlags )
|
||||
mFilterFlags &= ~flag;
|
||||
else
|
||||
mFilterFlags |= flag;
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear other exclusive flags
|
||||
Q_FOREACH ( QgsSearchWidgetWrapper::FilterFlag exclusiveFlag, QgsSearchWidgetWrapper::exclusiveFilterFlags() )
|
||||
{
|
||||
mFilterFlags &= ~exclusiveFlag;
|
||||
}
|
||||
// and set new exclusive flag
|
||||
mFilterFlags |= flag;
|
||||
}
|
||||
|
||||
updateState();
|
||||
}
|
||||
|
||||
bool QgsSearchWidgetToolButton::isActive() const
|
||||
{
|
||||
Q_FOREACH ( QgsSearchWidgetWrapper::FilterFlag flag, QgsSearchWidgetWrapper::exclusiveFilterFlags() )
|
||||
{
|
||||
if ( mFilterFlags & flag )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void QgsSearchWidgetToolButton::aboutToShowMenu()
|
||||
{
|
||||
mMenu->clear();
|
||||
bool fieldActive = false;
|
||||
Q_FOREACH ( QgsSearchWidgetWrapper::FilterFlag flag, QgsSearchWidgetWrapper::exclusiveFilterFlags() )
|
||||
{
|
||||
if ( !( mAvailableFilterFlags & flag ) )
|
||||
{
|
||||
//unsupported
|
||||
continue;
|
||||
}
|
||||
|
||||
QAction* action = mMenu->addAction( QgsSearchWidgetWrapper::toString( flag ) );
|
||||
connect( action, SIGNAL( triggered( bool ) ), this, SLOT( actionSelected() ) );
|
||||
action->setData( flag );
|
||||
action->setCheckable( true );
|
||||
if ( mFilterFlags & flag )
|
||||
{
|
||||
fieldActive = true;
|
||||
action->setChecked( true );
|
||||
}
|
||||
}
|
||||
|
||||
QAction* clearAction = mMenu->addAction( tr( "Exclude field" ) );
|
||||
connect( clearAction, SIGNAL( triggered( bool ) ), this, SLOT( setInactive() ) );
|
||||
clearAction->setCheckable( true );
|
||||
clearAction->setChecked( !fieldActive );
|
||||
if ( mMenu->actions().count() > 0 )
|
||||
{
|
||||
mMenu->insertAction( mMenu->actions().at( 0 ), clearAction );
|
||||
mMenu->insertSeparator( mMenu->actions().at( 1 ) );
|
||||
}
|
||||
else
|
||||
mMenu->addAction( clearAction );
|
||||
|
||||
mMenu->addSeparator();
|
||||
|
||||
Q_FOREACH ( QgsSearchWidgetWrapper::FilterFlag flag, QgsSearchWidgetWrapper::nonExclusiveFilterFlags() )
|
||||
{
|
||||
if ( !( mAvailableFilterFlags & flag ) )
|
||||
{
|
||||
//unsupported
|
||||
continue;
|
||||
}
|
||||
|
||||
QAction* action = mMenu->addAction( QgsSearchWidgetWrapper::toString( flag ) );
|
||||
connect( action, SIGNAL( triggered( bool ) ), this, SLOT( actionSelected() ) );
|
||||
action->setData( flag );
|
||||
action->setCheckable( true );
|
||||
if ( mFilterFlags & flag )
|
||||
action->setChecked( true );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsSearchWidgetToolButton::actionSelected()
|
||||
{
|
||||
QgsSearchWidgetWrapper::FilterFlag flag = static_cast< QgsSearchWidgetWrapper::FilterFlag >( qobject_cast< QAction* >( sender() )->data().toInt() );
|
||||
toggleFlag( flag );
|
||||
}
|
||||
|
||||
void QgsSearchWidgetToolButton::searchWidgetValueChanged()
|
||||
{
|
||||
setActive();
|
||||
}
|
||||
|
||||
void QgsSearchWidgetToolButton::setInactive()
|
||||
{
|
||||
if ( !isActive() )
|
||||
return;
|
||||
|
||||
if ( mSearchWrapper )
|
||||
mSearchWrapper->clearWidget();
|
||||
|
||||
QgsSearchWidgetWrapper::FilterFlags newFlags;
|
||||
Q_FOREACH ( QgsSearchWidgetWrapper::FilterFlag flag, QgsSearchWidgetWrapper::nonExclusiveFilterFlags() )
|
||||
{
|
||||
if ( !( mAvailableFilterFlags & flag ) || !( mFilterFlags & flag ) )
|
||||
continue;
|
||||
newFlags |= flag;
|
||||
}
|
||||
mFilterFlags = newFlags;
|
||||
updateState();
|
||||
}
|
||||
|
||||
void QgsSearchWidgetToolButton::setActive()
|
||||
{
|
||||
if ( isActive() )
|
||||
return;
|
||||
|
||||
Q_FOREACH ( QgsSearchWidgetWrapper::FilterFlag flag, QgsSearchWidgetWrapper::exclusiveFilterFlags() )
|
||||
{
|
||||
if ( mSearchWrapper && mSearchWrapper->defaultFlags() & flag )
|
||||
{
|
||||
toggleFlag( flag );
|
||||
return;
|
||||
}
|
||||
else if ( !mSearchWrapper && mAvailableFilterFlags & flag )
|
||||
{
|
||||
toggleFlag( flag );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsSearchWidgetToolButton::updateState()
|
||||
{
|
||||
if ( mSearchWrapper )
|
||||
mSearchWrapper->setEnabled( !( mFilterFlags & QgsSearchWidgetWrapper::IsNull ) );
|
||||
|
||||
bool active = false;
|
||||
QStringList toolTips;
|
||||
Q_FOREACH ( QgsSearchWidgetWrapper::FilterFlag flag, QgsSearchWidgetWrapper::exclusiveFilterFlags() )
|
||||
{
|
||||
if ( mFilterFlags & flag )
|
||||
{
|
||||
toolTips << QgsSearchWidgetWrapper::toString( flag );
|
||||
active = true;
|
||||
}
|
||||
}
|
||||
Q_FOREACH ( QgsSearchWidgetWrapper::FilterFlag flag, QgsSearchWidgetWrapper::nonExclusiveFilterFlags() )
|
||||
{
|
||||
if ( mFilterFlags & flag )
|
||||
{
|
||||
toolTips << QgsSearchWidgetWrapper::toString( flag ).toLower();
|
||||
}
|
||||
}
|
||||
|
||||
if ( active )
|
||||
{
|
||||
QString text = toolTips.join( ", " );
|
||||
setText( text );
|
||||
setToolTip( text );
|
||||
}
|
||||
else
|
||||
{
|
||||
setText( tr( "Exclude field" ) );
|
||||
setToolTip( QString() );
|
||||
}
|
||||
}
|
129
src/gui/editorwidgets/qgssearchwidgettoolbutton.h
Normal file
129
src/gui/editorwidgets/qgssearchwidgettoolbutton.h
Normal file
@ -0,0 +1,129 @@
|
||||
/***************************************************************************
|
||||
qgssearchwidgettoolbutton.h
|
||||
---------------------------
|
||||
Date : May 2016
|
||||
Copyright : (C) 2016 Nyall Dawson
|
||||
Email : nyall dot dawson at gmail.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSSEARCHWIDGETTOOLBUTTON_H
|
||||
#define QGSSEARCHWIDGETTOOLBUTTON_H
|
||||
|
||||
#include "editorwidgets/core/qgssearchwidgetwrapper.h"
|
||||
#include <QToolButton>
|
||||
|
||||
/** \ingroup gui
|
||||
* \class QgsSearchWidgetToolButton
|
||||
* A tool button widget which is displayed next to search widgets in forms, and
|
||||
* allows for controlling how the widget behaves and how the filtering/searching
|
||||
* operates.
|
||||
* \note Added in version 2.16
|
||||
*/
|
||||
class GUI_EXPORT QgsSearchWidgetToolButton : public QToolButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/** Constructor for QgsSearchWidgetToolButton.
|
||||
* @param parent parent object
|
||||
*/
|
||||
explicit QgsSearchWidgetToolButton( QWidget *parent = nullptr );
|
||||
|
||||
/** Sets the search widget wrapper associated with this button.
|
||||
* Calling this will automatically set the available flags to match those
|
||||
* supported by the wrapper and reset the active flags to match the wrapper's
|
||||
* default flags.
|
||||
* @param wrapper search wrapper. Ownership is not transferred.
|
||||
*/
|
||||
void setSearchWidgetWrapper( QgsSearchWidgetWrapper* wrapper );
|
||||
|
||||
/** Sets the available filter flags to show in the widget. Any active flags
|
||||
* (see activeFlags()) which are not present in the new available filter
|
||||
* flags will be cleared;
|
||||
* @param flags available flags to show in widget
|
||||
* @see availableFlags()
|
||||
* @see setActiveFlags()
|
||||
*/
|
||||
void setAvailableFlags( QgsSearchWidgetWrapper::FilterFlags flags );
|
||||
|
||||
/** Returns the available filter flags shown in the widget.
|
||||
* @see setAvailableFlags()
|
||||
* @see activeFlags()
|
||||
*/
|
||||
QgsSearchWidgetWrapper::FilterFlags availableFlags() const { return mAvailableFilterFlags; }
|
||||
|
||||
/** Sets the current active filter flags for the widget. Any flags
|
||||
* which are not present in the available filter flags (see availableFlags())
|
||||
* will not be set.
|
||||
* @param flags active flags to show in widget
|
||||
* @see toggleFlag()
|
||||
* @see activeFlags()
|
||||
* @see setAvailableFlags()
|
||||
*/
|
||||
void setActiveFlags( QgsSearchWidgetWrapper::FilterFlags flags );
|
||||
|
||||
/** Toggles an individual active filter flag for the widget. Any flags
|
||||
* which are not present in the available filter flags (see availableFlags())
|
||||
* will be ignore. Other flags may be cleared if they conflict with the newly
|
||||
* toggled flag.
|
||||
* @param flag flag to toggle
|
||||
* @see setActiveFlags()
|
||||
* @see activeFlags()
|
||||
*/
|
||||
void toggleFlag( QgsSearchWidgetWrapper::FilterFlag flag );
|
||||
|
||||
/** Returns the active filter flags shown in the widget.
|
||||
* @see setActiveFlags()
|
||||
* @see toggleFlag()
|
||||
* @see availableFlags()
|
||||
*/
|
||||
QgsSearchWidgetWrapper::FilterFlags activeFlags() const { return mFilterFlags; }
|
||||
|
||||
/** Returns true if the widget is set to be included in the search.
|
||||
* @see setInactive()
|
||||
* @see setActive()
|
||||
*/
|
||||
bool isActive() const;
|
||||
|
||||
public slots:
|
||||
|
||||
/** Sets the search widget as inactive, ie do not search the corresponding field.
|
||||
* @see isActive()
|
||||
* @see setActive()
|
||||
*/
|
||||
void setInactive();
|
||||
|
||||
/** Sets the search widget as active by selecting the first available search type.
|
||||
* @see isActive()
|
||||
* @see setInactive()
|
||||
*/
|
||||
void setActive();
|
||||
|
||||
private slots:
|
||||
|
||||
void aboutToShowMenu();
|
||||
|
||||
void actionSelected();
|
||||
|
||||
void searchWidgetValueChanged();
|
||||
|
||||
private:
|
||||
|
||||
QgsSearchWidgetWrapper::FilterFlags mAvailableFilterFlags;
|
||||
QgsSearchWidgetWrapper::FilterFlags mFilterFlags;
|
||||
QgsSearchWidgetWrapper* mSearchWrapper;
|
||||
QMenu* mMenu;
|
||||
|
||||
void updateState();
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSSEARCHWIDGETTOOLBUTTON_H
|
@ -40,10 +40,12 @@ void QgsValueMapSearchWidgetWrapper::comboBoxIndexChanged( int idx )
|
||||
if ( idx == 0 )
|
||||
{
|
||||
clearExpression();
|
||||
emit valueCleared();
|
||||
}
|
||||
else
|
||||
{
|
||||
setExpression( mComboBox->itemData( idx ).toString() );
|
||||
emit valueChanged();
|
||||
}
|
||||
emit expressionChanged( mExpression );
|
||||
}
|
||||
@ -64,6 +66,71 @@ bool QgsValueMapSearchWidgetWrapper::valid() const
|
||||
return true;
|
||||
}
|
||||
|
||||
QgsSearchWidgetWrapper::FilterFlags QgsValueMapSearchWidgetWrapper::supportedFlags() const
|
||||
{
|
||||
return EqualTo | NotEqualTo | IsNull;
|
||||
}
|
||||
|
||||
QgsSearchWidgetWrapper::FilterFlags QgsValueMapSearchWidgetWrapper::defaultFlags() const
|
||||
{
|
||||
return EqualTo;
|
||||
}
|
||||
|
||||
QString QgsValueMapSearchWidgetWrapper::createExpression( QgsSearchWidgetWrapper::FilterFlags flags ) const
|
||||
{
|
||||
//if unselect value, always pass
|
||||
if ( mComboBox->currentIndex() == 0 )
|
||||
return QString();
|
||||
|
||||
//clear any unsupported flags
|
||||
flags &= supportedFlags();
|
||||
|
||||
QVariant::Type fldType = layer()->fields().at( mFieldIdx ).type();
|
||||
QString fieldName = QgsExpression::quotedColumnRef( layer()->fields().at( mFieldIdx ).name() );
|
||||
|
||||
if ( flags & IsNull )
|
||||
return fieldName + " IS NULL";
|
||||
|
||||
QString currentKey = mComboBox->itemData( mComboBox->currentIndex() ).toString();
|
||||
|
||||
switch ( fldType )
|
||||
{
|
||||
case QVariant::Int:
|
||||
case QVariant::UInt:
|
||||
case QVariant::Double:
|
||||
case QVariant::LongLong:
|
||||
case QVariant::ULongLong:
|
||||
{
|
||||
if ( flags & EqualTo )
|
||||
return fieldName + '=' + currentKey;
|
||||
else if ( flags & NotEqualTo )
|
||||
return fieldName + "<>" + currentKey;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if ( flags & EqualTo )
|
||||
return fieldName + "='" + currentKey + '\'';
|
||||
else if ( flags & NotEqualTo )
|
||||
return fieldName + "<>'" + currentKey + '\'';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void QgsValueMapSearchWidgetWrapper::clearWidget()
|
||||
{
|
||||
mComboBox->setCurrentIndex( 0 );
|
||||
}
|
||||
|
||||
void QgsValueMapSearchWidgetWrapper::setEnabled( bool enabled )
|
||||
{
|
||||
mComboBox->setEnabled( enabled );
|
||||
}
|
||||
|
||||
void QgsValueMapSearchWidgetWrapper::initWidget( QWidget* editor )
|
||||
{
|
||||
mComboBox = qobject_cast<QComboBox*>( editor );
|
||||
|
@ -23,7 +23,6 @@
|
||||
* Wraps a value map search widget. This widget will offer a combobox with values from another layer
|
||||
* referenced by a foreign key (a constraint may be set but is not required on data level).
|
||||
* It will be used as a search widget and produces expression to look for in the layer.
|
||||
* \note not available in Python bindings
|
||||
*/
|
||||
|
||||
class GUI_EXPORT QgsValueMapSearchWidgetWrapper : public QgsSearchWidgetWrapper
|
||||
@ -34,6 +33,14 @@ class GUI_EXPORT QgsValueMapSearchWidgetWrapper : public QgsSearchWidgetWrapper
|
||||
bool applyDirectly() override;
|
||||
QString expression() override;
|
||||
bool valid() const override;
|
||||
FilterFlags supportedFlags() const override;
|
||||
FilterFlags defaultFlags() const override;
|
||||
virtual QString createExpression( FilterFlags flags ) const override;
|
||||
|
||||
public slots:
|
||||
|
||||
virtual void clearWidget() override;
|
||||
virtual void setEnabled( bool enabled ) override;
|
||||
|
||||
protected:
|
||||
QWidget* createWidget( QWidget* parent ) override;
|
||||
|
@ -89,22 +89,107 @@ QVariant QgsValueRelationSearchWidgetWrapper::value() const
|
||||
return v;
|
||||
}
|
||||
|
||||
QgsSearchWidgetWrapper::FilterFlags QgsValueRelationSearchWidgetWrapper::supportedFlags() const
|
||||
{
|
||||
return EqualTo | NotEqualTo | IsNull;
|
||||
}
|
||||
|
||||
QgsSearchWidgetWrapper::FilterFlags QgsValueRelationSearchWidgetWrapper::defaultFlags() const
|
||||
{
|
||||
return EqualTo;
|
||||
}
|
||||
|
||||
QString QgsValueRelationSearchWidgetWrapper::createExpression( QgsSearchWidgetWrapper::FilterFlags flags ) const
|
||||
{
|
||||
QString fieldName = QgsExpression::quotedColumnRef( layer()->fields().at( mFieldIdx ).name() );
|
||||
|
||||
//clear any unsupported flags
|
||||
flags &= supportedFlags();
|
||||
if ( flags & IsNull )
|
||||
return fieldName + " IS NULL";
|
||||
|
||||
QVariant v = value();
|
||||
if ( !v.isValid() )
|
||||
return QString();
|
||||
|
||||
switch ( v.type() )
|
||||
{
|
||||
case QVariant::Int:
|
||||
case QVariant::UInt:
|
||||
case QVariant::Double:
|
||||
case QVariant::LongLong:
|
||||
case QVariant::ULongLong:
|
||||
{
|
||||
if ( flags & EqualTo )
|
||||
return fieldName + '=' + v.toString();
|
||||
else if ( flags & NotEqualTo )
|
||||
return fieldName + "<>" + v.toString();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if ( flags & EqualTo )
|
||||
return fieldName + "='" + v.toString() + '\'';
|
||||
else if ( flags & NotEqualTo )
|
||||
return fieldName + "<>'" + v.toString() + '\'';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void QgsValueRelationSearchWidgetWrapper::clearWidget()
|
||||
{
|
||||
if ( mComboBox )
|
||||
{
|
||||
mComboBox->setCurrentIndex( 0 );
|
||||
}
|
||||
if ( mListWidget )
|
||||
{
|
||||
mListWidget->clearSelection();
|
||||
}
|
||||
if ( mLineEdit )
|
||||
{
|
||||
mLineEdit->setText( QString() );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsValueRelationSearchWidgetWrapper::setEnabled( bool enabled )
|
||||
{
|
||||
if ( mComboBox )
|
||||
{
|
||||
mComboBox->setEnabled( enabled );
|
||||
}
|
||||
if ( mListWidget )
|
||||
{
|
||||
mListWidget->setEnabled( enabled );
|
||||
}
|
||||
if ( mLineEdit )
|
||||
{
|
||||
mLineEdit->setEnabled( enabled );
|
||||
}
|
||||
}
|
||||
|
||||
bool QgsValueRelationSearchWidgetWrapper::valid() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsValueRelationSearchWidgetWrapper::valueChanged()
|
||||
void QgsValueRelationSearchWidgetWrapper::onValueChanged()
|
||||
{
|
||||
QVariant vl = value();
|
||||
if ( !vl.isValid() )
|
||||
{
|
||||
clearExpression();
|
||||
emit valueCleared();
|
||||
}
|
||||
else
|
||||
{
|
||||
QSettings settings;
|
||||
setExpression( vl.isNull() ? settings.value( "qgis/nullValue", "NULL" ).toString() : vl.toString() );
|
||||
emit valueChanged();
|
||||
}
|
||||
emit expressionChanged( mExpression );
|
||||
}
|
||||
@ -167,7 +252,7 @@ void QgsValueRelationSearchWidgetWrapper::initWidget( QWidget* editor )
|
||||
mComboBox->addItem( element.second, element.first );
|
||||
}
|
||||
|
||||
connect( mComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( valueChanged() ) );
|
||||
connect( mComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( onValueChanged() ) );
|
||||
}
|
||||
else if ( mListWidget )
|
||||
{
|
||||
@ -179,7 +264,7 @@ void QgsValueRelationSearchWidgetWrapper::initWidget( QWidget* editor )
|
||||
|
||||
mListWidget->addItem( item );
|
||||
}
|
||||
connect( mListWidget, SIGNAL( itemChanged( QListWidgetItem* ) ), this, SLOT( valueChanged() ) );
|
||||
connect( mListWidget, SIGNAL( itemChanged( QListWidgetItem* ) ), this, SLOT( onValueChanged() ) );
|
||||
}
|
||||
else if ( mLineEdit )
|
||||
{
|
||||
@ -193,7 +278,7 @@ void QgsValueRelationSearchWidgetWrapper::initWidget( QWidget* editor )
|
||||
QCompleter* completer = new QCompleter( m, mLineEdit );
|
||||
completer->setCaseSensitivity( Qt::CaseInsensitive );
|
||||
mLineEdit->setCompleter( completer );
|
||||
connect( mLineEdit, SIGNAL( textChanged( QListWidgetItem* ) ), this, SLOT( valueChanged() ) );
|
||||
connect( mLineEdit, SIGNAL( textChanged( QString ) ), this, SLOT( onValueChanged() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,6 @@ class QgsValueRelationWidgetFactory;
|
||||
* Wraps a value relation search widget. This widget will offer a combobox with values from another layer
|
||||
* referenced by a foreign key (a constraint may be set but is not required on data level).
|
||||
* It will be used as a search widget and produces expression to look for in the layer.
|
||||
* \note not available in Python bindings
|
||||
*/
|
||||
|
||||
class GUI_EXPORT QgsValueRelationSearchWidgetWrapper : public QgsSearchWidgetWrapper
|
||||
@ -46,13 +45,23 @@ class GUI_EXPORT QgsValueRelationSearchWidgetWrapper : public QgsSearchWidgetWra
|
||||
QString expression() override;
|
||||
bool valid() const override;
|
||||
QVariant value() const;
|
||||
FilterFlags supportedFlags() const override;
|
||||
FilterFlags defaultFlags() const override;
|
||||
virtual QString createExpression( FilterFlags flags ) const override;
|
||||
|
||||
public slots:
|
||||
|
||||
virtual void clearWidget() override;
|
||||
virtual void setEnabled( bool enabled ) override;
|
||||
|
||||
protected:
|
||||
QWidget* createWidget( QWidget* parent ) override;
|
||||
void initWidget( QWidget* editor ) override;
|
||||
|
||||
public slots:
|
||||
void valueChanged();
|
||||
|
||||
//! Called when current value of search widget changes
|
||||
void onValueChanged();
|
||||
|
||||
protected slots:
|
||||
void setExpression( QString exp ) override;
|
||||
|
@ -35,6 +35,7 @@ class QgsVectorLayer;
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
// TODO QGIS 3.0 - remove
|
||||
class GUI_EXPORT QgsAttributeEditor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -42,6 +42,8 @@
|
||||
#include <QUiLoader>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
#include <QToolButton>
|
||||
#include <QMenu>
|
||||
|
||||
int QgsAttributeForm::sFormCounter = 0;
|
||||
|
||||
@ -53,6 +55,7 @@ QgsAttributeForm::QgsAttributeForm( QgsVectorLayer* vl, const QgsFeature &featur
|
||||
, mMultiEditMessageBarItem( nullptr )
|
||||
, mContext( context )
|
||||
, mButtonBox( nullptr )
|
||||
, mSearchButtonBox( nullptr )
|
||||
, mFormNr( sFormCounter++ )
|
||||
, mIsSaving( false )
|
||||
, mPreventFeatureRefresh( false )
|
||||
@ -166,11 +169,9 @@ void QgsAttributeForm::setMode( QgsAttributeForm::Mode mode )
|
||||
w->setMode( QgsAttributeFormEditorWidget::MultiEditMode );
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case QgsAttributeForm::SearchMode:
|
||||
w->setMode( QgsAttributeFormEditorWidget::SearchMode );
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,18 +179,26 @@ void QgsAttributeForm::setMode( QgsAttributeForm::Mode mode )
|
||||
{
|
||||
case QgsAttributeForm::SingleEditMode:
|
||||
setFeature( mFeature );
|
||||
mSearchButtonBox->setVisible( false );
|
||||
break;
|
||||
|
||||
case QgsAttributeForm::AddFeatureMode:
|
||||
synchronizeEnabledState();
|
||||
mSearchButtonBox->setVisible( false );
|
||||
break;
|
||||
|
||||
case QgsAttributeForm::MultiEditMode:
|
||||
resetMultiEdit( false );
|
||||
synchronizeEnabledState();
|
||||
mSearchButtonBox->setVisible( false );
|
||||
break;
|
||||
|
||||
case QgsAttributeForm::SearchMode:
|
||||
mSearchButtonBox->setVisible( true );
|
||||
break;
|
||||
}
|
||||
|
||||
emit modeChanged( mMode );
|
||||
}
|
||||
|
||||
void QgsAttributeForm::setIsAddDialog( bool isAddDialog )
|
||||
@ -230,6 +239,7 @@ void QgsAttributeForm::setFeature( const QgsFeature& feature )
|
||||
break;
|
||||
}
|
||||
case MultiEditMode:
|
||||
case SearchMode:
|
||||
{
|
||||
//ignore setFeature
|
||||
break;
|
||||
@ -368,6 +378,84 @@ void QgsAttributeForm::multiEditMessageClicked( const QString& link )
|
||||
resetMultiEdit( link == "#apply" );
|
||||
}
|
||||
|
||||
void QgsAttributeForm::filterTriggered()
|
||||
{
|
||||
QString filter = createFilterExpression();
|
||||
emit filterExpressionSet( filter, ReplaceFilter );
|
||||
setMode( SingleEditMode );
|
||||
}
|
||||
|
||||
void QgsAttributeForm::filterAndTriggered()
|
||||
{
|
||||
QString filter = createFilterExpression();
|
||||
if ( filter.isEmpty() )
|
||||
return;
|
||||
|
||||
setMode( SingleEditMode );
|
||||
emit filterExpressionSet( filter, FilterAnd );
|
||||
}
|
||||
|
||||
void QgsAttributeForm::filterOrTriggered()
|
||||
{
|
||||
QString filter = createFilterExpression();
|
||||
if ( filter.isEmpty() )
|
||||
return;
|
||||
|
||||
setMode( SingleEditMode );
|
||||
emit filterExpressionSet( filter, FilterOr );
|
||||
}
|
||||
|
||||
void QgsAttributeForm::pushSelectedFeaturesMessage()
|
||||
{
|
||||
int count = mLayer->selectedFeatureCount();
|
||||
if ( count > 0 )
|
||||
{
|
||||
mMessageBar->pushMessage( QString(),
|
||||
tr( "%1 matching %2 selected" ).arg( count )
|
||||
.arg( count == 1 ? tr( "feature" ) : tr( "features" ) ),
|
||||
QgsMessageBar::INFO,
|
||||
messageTimeout() );
|
||||
}
|
||||
else
|
||||
{
|
||||
mMessageBar->pushMessage( QString(),
|
||||
tr( "No matching features found" ),
|
||||
QgsMessageBar::WARNING,
|
||||
messageTimeout() );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAttributeForm::runSearchSelect( QgsVectorLayer::SelectBehaviour behaviour )
|
||||
{
|
||||
QString filter = createFilterExpression();
|
||||
if ( filter.isEmpty() )
|
||||
return;
|
||||
|
||||
mLayer->selectByExpression( filter, behaviour );
|
||||
pushSelectedFeaturesMessage();
|
||||
setMode( SingleEditMode );
|
||||
}
|
||||
|
||||
void QgsAttributeForm::searchSetSelection()
|
||||
{
|
||||
runSearchSelect( QgsVectorLayer::SetSelection );
|
||||
}
|
||||
|
||||
void QgsAttributeForm::searchAddToSelection()
|
||||
{
|
||||
runSearchSelect( QgsVectorLayer::AddToSelection );
|
||||
}
|
||||
|
||||
void QgsAttributeForm::searchRemoveFromSelection()
|
||||
{
|
||||
runSearchSelect( QgsVectorLayer::RemoveFromSelection );
|
||||
}
|
||||
|
||||
void QgsAttributeForm::searchIntersectSelection()
|
||||
{
|
||||
runSearchSelect( QgsVectorLayer::IntersectSelection );
|
||||
}
|
||||
|
||||
bool QgsAttributeForm::saveMultiEdits()
|
||||
{
|
||||
//find changed attributes
|
||||
@ -458,6 +546,7 @@ bool QgsAttributeForm::save()
|
||||
{
|
||||
case SingleEditMode:
|
||||
case AddFeatureMode:
|
||||
case SearchMode:
|
||||
success = saveEdits();
|
||||
break;
|
||||
|
||||
@ -480,6 +569,14 @@ void QgsAttributeForm::resetValues()
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAttributeForm::resetSearch()
|
||||
{
|
||||
Q_FOREACH ( QgsAttributeFormEditorWidget* w, findChildren< QgsAttributeFormEditorWidget* >() )
|
||||
{
|
||||
w->resetSearch();
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAttributeForm::clearMultiEditMessages()
|
||||
{
|
||||
if ( mMultiEditUnsavedMessageBarItem )
|
||||
@ -496,6 +593,23 @@ void QgsAttributeForm::clearMultiEditMessages()
|
||||
}
|
||||
}
|
||||
|
||||
QString QgsAttributeForm::createFilterExpression() const
|
||||
{
|
||||
QStringList filters;
|
||||
Q_FOREACH ( QgsAttributeFormEditorWidget* w, findChildren< QgsAttributeFormEditorWidget* >() )
|
||||
{
|
||||
QString filter = w->currentFilterExpression();
|
||||
if ( !filter.isEmpty() )
|
||||
filters << filter;
|
||||
}
|
||||
|
||||
if ( filters.isEmpty() )
|
||||
return QString();
|
||||
|
||||
QString filter = filters.join( ") AND (" ).prepend( '(' ).append( ')' );
|
||||
return filter;
|
||||
}
|
||||
|
||||
void QgsAttributeForm::onAttributeChanged( const QVariant& value )
|
||||
{
|
||||
QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( sender() );
|
||||
@ -532,6 +646,9 @@ void QgsAttributeForm::onAttributeChanged( const QVariant& value )
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SearchMode:
|
||||
//nothing to do
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -651,6 +768,12 @@ void QgsAttributeForm::init()
|
||||
mButtonBox = nullptr;
|
||||
}
|
||||
|
||||
if ( mSearchButtonBox )
|
||||
{
|
||||
delete mSearchButtonBox;
|
||||
mSearchButtonBox = nullptr;
|
||||
}
|
||||
|
||||
qDeleteAll( mWidgets );
|
||||
mWidgets.clear();
|
||||
|
||||
@ -660,17 +783,24 @@ void QgsAttributeForm::init()
|
||||
}
|
||||
delete layout();
|
||||
|
||||
QVBoxLayout* vl = new QVBoxLayout();
|
||||
vl->setMargin( 0 );
|
||||
vl->setContentsMargins( 0, 0, 0, 0 );
|
||||
mMessageBar = new QgsMessageBar( this );
|
||||
mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
|
||||
vl->addWidget( mMessageBar );
|
||||
setLayout( vl );
|
||||
|
||||
// Get a layout
|
||||
QGridLayout* layout = new QGridLayout();
|
||||
setLayout( layout );
|
||||
QWidget* container = new QWidget();
|
||||
container->setLayout( layout );
|
||||
vl->addWidget( container );
|
||||
|
||||
mFormEditorWidgets.clear();
|
||||
|
||||
// a bar to warn the user with non-blocking messages
|
||||
setContentsMargins( 0, 0, 0, 0 );
|
||||
mMessageBar = new QgsMessageBar( this );
|
||||
mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
|
||||
layout->addWidget( mMessageBar, 0, 0, 1, 1 );
|
||||
|
||||
// Try to load Ui-File for layout
|
||||
if ( mLayer->editFormConfig()->layout() == QgsEditFormConfig::UiFileLayout && !mLayer->editFormConfig()->uiForm().isEmpty() )
|
||||
@ -770,8 +900,12 @@ void QgsAttributeForm::init()
|
||||
QWidget* w = nullptr;
|
||||
if ( eww )
|
||||
{
|
||||
w = new QgsAttributeFormEditorWidget( eww, this );
|
||||
mFormEditorWidgets.insert( idx, static_cast< QgsAttributeFormEditorWidget* >( w ) );
|
||||
QgsAttributeFormEditorWidget* formWidget = new QgsAttributeFormEditorWidget( eww, this );
|
||||
w = formWidget;
|
||||
mFormEditorWidgets.insert( idx, formWidget );
|
||||
QgsSearchWidgetWrapper* sww = QgsEditorWidgetRegistry::instance()->createSearchWidget( widgetType, mLayer, idx, widgetConfig,
|
||||
formWidget->searchWidgetFrame(), mContext );
|
||||
formWidget->setSearchWidgetWrapper( sww );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -806,7 +940,9 @@ void QgsAttributeForm::init()
|
||||
if ( QgsProject::instance()->relationManager()->referencedRelations( mLayer ).isEmpty() )
|
||||
{
|
||||
QSpacerItem *spacerItem = new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
|
||||
gridLayout->addItem( spacerItem, row++, 0 );
|
||||
gridLayout->addItem( spacerItem, row, 0 );
|
||||
gridLayout->setRowStretch( row, 1 );
|
||||
row++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -816,9 +952,62 @@ void QgsAttributeForm::init()
|
||||
mButtonBox->setObjectName( "buttonBox" );
|
||||
layout->addWidget( mButtonBox );
|
||||
}
|
||||
|
||||
mButtonBox->setVisible( buttonBoxVisible );
|
||||
|
||||
if ( !mSearchButtonBox )
|
||||
{
|
||||
mSearchButtonBox = new QWidget();
|
||||
QHBoxLayout* boxLayout = new QHBoxLayout();
|
||||
boxLayout->setMargin( 0 );
|
||||
boxLayout->setContentsMargins( 0, 0, 0, 0 );
|
||||
mSearchButtonBox->setLayout( boxLayout );
|
||||
mSearchButtonBox->setObjectName( "searchButtonBox" );
|
||||
|
||||
QPushButton* clearButton = new QPushButton( tr( "Reset form" ), mSearchButtonBox );
|
||||
connect( clearButton, SIGNAL( clicked( bool ) ), this, SLOT( resetSearch() ) );
|
||||
boxLayout->addWidget( clearButton );
|
||||
boxLayout->addStretch( 1 );
|
||||
|
||||
QToolButton* selectButton = new QToolButton();
|
||||
selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
|
||||
selectButton->setText( tr( "Select features" ) );
|
||||
selectButton->setPopupMode( QToolButton::MenuButtonPopup );
|
||||
connect( selectButton, SIGNAL( clicked( bool ) ), this, SLOT( searchSetSelection() ) );
|
||||
QMenu* selectMenu = new QMenu( selectButton );
|
||||
QAction* selectAction = new QAction( tr( "Select features" ), selectMenu );
|
||||
connect( selectAction, SIGNAL( triggered( bool ) ), this, SLOT( searchSetSelection() ) );
|
||||
selectMenu->addAction( selectAction );
|
||||
QAction* addSelectAction = new QAction( tr( "Add to current selection" ), selectMenu );
|
||||
connect( addSelectAction, SIGNAL( triggered( bool ) ), this, SLOT( searchAddToSelection() ) );
|
||||
selectMenu->addAction( addSelectAction );
|
||||
QAction* filterSelectAction = new QAction( tr( "Filter current selection" ), selectMenu );
|
||||
connect( filterSelectAction, SIGNAL( triggered( bool ) ), this, SLOT( searchIntersectSelection() ) );
|
||||
selectMenu->addAction( filterSelectAction );
|
||||
QAction* deselectAction = new QAction( tr( "Remove from current selection" ), selectMenu );
|
||||
connect( deselectAction, SIGNAL( triggered( bool ) ), this, SLOT( searchRemoveFromSelection() ) );
|
||||
selectMenu->addAction( deselectAction );
|
||||
selectButton->setMenu( selectMenu );
|
||||
boxLayout->addWidget( selectButton );
|
||||
|
||||
QToolButton* filterButton = new QToolButton();
|
||||
filterButton->setText( tr( "Filter features" ) );
|
||||
filterButton->setPopupMode( QToolButton::MenuButtonPopup );
|
||||
filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
|
||||
connect( filterButton, SIGNAL( clicked( bool ) ), this, SLOT( filterTriggered() ) );
|
||||
QMenu* filterMenu = new QMenu( filterButton );
|
||||
QAction* filterAndAction = new QAction( tr( "Filter within (\"AND\")" ), filterMenu );
|
||||
connect( filterAndAction, SIGNAL( triggered( bool ) ), this, SLOT( filterAndTriggered() ) );
|
||||
filterMenu->addAction( filterAndAction );
|
||||
QAction* filterOrAction = new QAction( tr( "Extend filter (\"OR\")" ), filterMenu );
|
||||
connect( filterOrAction, SIGNAL( triggered( bool ) ), this, SLOT( filterOrTriggered() ) );
|
||||
filterMenu->addAction( filterOrAction );
|
||||
filterButton->setMenu( filterMenu );
|
||||
boxLayout->addWidget( filterButton );
|
||||
|
||||
layout->addWidget( mSearchButtonBox );
|
||||
}
|
||||
mSearchButtonBox->setVisible( mMode == SearchMode );
|
||||
|
||||
connectWrappers();
|
||||
|
||||
connect( mButtonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
|
||||
@ -970,9 +1159,11 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt
|
||||
const QgsEditorWidgetConfig widgetConfig = mLayer->editFormConfig()->widgetConfig( fldIdx );
|
||||
|
||||
QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, fldIdx, widgetConfig, nullptr, this, mContext );
|
||||
QgsAttributeFormEditorWidget* w = new QgsAttributeFormEditorWidget( eww, this );
|
||||
mFormEditorWidgets.insert( fldIdx, w );
|
||||
|
||||
QWidget* w = new QgsAttributeFormEditorWidget( eww, this );
|
||||
mFormEditorWidgets.insert( fldIdx, static_cast< QgsAttributeFormEditorWidget* >( w ) );
|
||||
QgsSearchWidgetWrapper* sww = QgsEditorWidgetRegistry::instance()->createSearchWidget( widgetType, mLayer, fldIdx, widgetConfig, w->searchWidgetFrame(), mContext );
|
||||
w->setSearchWidgetWrapper( sww );
|
||||
|
||||
newWidgetInfo.widget = w;
|
||||
addWidgetWrapper( eww );
|
||||
@ -1083,6 +1274,7 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt
|
||||
QWidget* spacer = new QWidget();
|
||||
spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
|
||||
gbLayout->addWidget( spacer, ++row, 0 );
|
||||
gbLayout->setRowStretch( row, 1 );
|
||||
|
||||
newWidgetInfo.labelText = QString::null;
|
||||
newWidgetInfo.labelOnTop = true;
|
||||
@ -1240,6 +1432,7 @@ void QgsAttributeForm::layerSelectionChanged()
|
||||
{
|
||||
case SingleEditMode:
|
||||
case AddFeatureMode:
|
||||
case SearchMode:
|
||||
break;
|
||||
|
||||
case MultiEditMode:
|
||||
|
@ -42,7 +42,15 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
|
||||
AddFeatureMode, /*!< Add feature mode, for setting attributes for a new feature. In this mode the dialog will be editable even with an invalid feature and
|
||||
will add a new feature when the form is accepted. */
|
||||
MultiEditMode, /*!< Multi edit mode, for editing fields of multiple features at once */
|
||||
// TODO: SearchMode, /*!< Form values are used for searching/filtering the layer */
|
||||
SearchMode, /*!< Form values are used for searching/filtering the layer */
|
||||
};
|
||||
|
||||
//! Filter types
|
||||
enum FilterType
|
||||
{
|
||||
ReplaceFilter, /*!< Filter should replace any existing filter */
|
||||
FilterAnd, /*!< Filter should be combined using "AND" */
|
||||
FilterOr, /*!< Filter should be combined using "OR" */
|
||||
};
|
||||
|
||||
explicit QgsAttributeForm( QgsVectorLayer* vl, const QgsFeature &feature = QgsFeature(), const QgsAttributeEditorContext& context = QgsAttributeEditorContext(), QWidget *parent = nullptr );
|
||||
@ -156,6 +164,18 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
|
||||
*/
|
||||
void featureSaved( const QgsFeature& feature );
|
||||
|
||||
/** Is emitted when a filter expression is set using the form.
|
||||
* @param expression filter expression
|
||||
* @param type filter type
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void filterExpressionSet( const QString& expression, QgsAttributeForm::FilterType type );
|
||||
|
||||
/** Emitted when the form changes mode.
|
||||
* @param mode new mode
|
||||
*/
|
||||
void modeChanged( QgsAttributeForm::Mode mode );
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* Call this to change the content of a given attribute. Will update the editor(s) related to this field.
|
||||
@ -198,6 +218,11 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
|
||||
*/
|
||||
void resetValues();
|
||||
|
||||
/** Resets the search/filter form values.
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
void resetSearch();
|
||||
|
||||
/**
|
||||
* reload current feature
|
||||
*/
|
||||
@ -218,6 +243,14 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
|
||||
void resetMultiEdit( bool promptToSave = false );
|
||||
void multiEditMessageClicked( const QString& link );
|
||||
|
||||
void filterAndTriggered();
|
||||
void filterOrTriggered();
|
||||
void filterTriggered();
|
||||
|
||||
void searchSetSelection();
|
||||
void searchAddToSelection();
|
||||
void searchRemoveFromSelection();
|
||||
void searchIntersectSelection();
|
||||
|
||||
private:
|
||||
void init();
|
||||
@ -258,6 +291,10 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
|
||||
|
||||
int messageTimeout();
|
||||
void clearMultiEditMessages();
|
||||
void pushSelectedFeaturesMessage();
|
||||
void runSearchSelect( QgsVectorLayer::SelectBehaviour behaviour );
|
||||
|
||||
QString createFilterExpression() const;
|
||||
|
||||
QgsVectorLayer* mLayer;
|
||||
QgsFeature mFeature;
|
||||
@ -267,6 +304,7 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
|
||||
QList<QgsWidgetWrapper*> mWidgets;
|
||||
QgsAttributeEditorContext mContext;
|
||||
QDialogButtonBox* mButtonBox;
|
||||
QWidget* mSearchButtonBox;
|
||||
QList<QgsAttributeFormInterface*> mInterfaces;
|
||||
QMap< int, QgsAttributeFormEditorWidget* > mFormEditorWidgets;
|
||||
|
||||
|
@ -16,13 +16,18 @@
|
||||
#include "qgsattributeformeditorwidget.h"
|
||||
#include "qgsattributeform.h"
|
||||
#include "qgsmultiedittoolbutton.h"
|
||||
#include "qgssearchwidgettoolbutton.h"
|
||||
#include "qgseditorwidgetwrapper.h"
|
||||
#include "qgssearchwidgetwrapper.h"
|
||||
#include <QLayout>
|
||||
#include <QLabel>
|
||||
#include <QStackedWidget>
|
||||
|
||||
QgsAttributeFormEditorWidget::QgsAttributeFormEditorWidget( QgsEditorWidgetWrapper* editorWidget, QgsAttributeForm* form )
|
||||
QgsAttributeFormEditorWidget::QgsAttributeFormEditorWidget( QgsEditorWidgetWrapper* editorWidget,
|
||||
QgsAttributeForm* form )
|
||||
: QWidget( form )
|
||||
, mWidget( editorWidget )
|
||||
, mSearchWidget( nullptr )
|
||||
, mForm( form )
|
||||
, mMode( DefaultMode )
|
||||
, mMultiEditButton( new QgsMultiEditToolButton() )
|
||||
@ -30,13 +35,42 @@ QgsAttributeFormEditorWidget::QgsAttributeFormEditorWidget( QgsEditorWidgetWrapp
|
||||
, mIsMixed( false )
|
||||
, mIsChanged( false )
|
||||
{
|
||||
mEditPage = new QWidget();
|
||||
QHBoxLayout* l = new QHBoxLayout();
|
||||
l->setMargin( 0 );
|
||||
l->setContentsMargins( 0, 0, 0, 0 );
|
||||
mEditPage->setLayout( l );
|
||||
|
||||
l = new QHBoxLayout();
|
||||
l->setMargin( 0 );
|
||||
l->setContentsMargins( 0, 0, 0, 0 );
|
||||
mSearchFrame = new QWidget();
|
||||
mSearchFrame->setLayout( l );
|
||||
|
||||
mSearchPage = new QWidget();
|
||||
l = new QHBoxLayout();
|
||||
l->setMargin( 0 );
|
||||
l->setContentsMargins( 0, 0, 0, 0 );
|
||||
mSearchPage->setLayout( l );
|
||||
l->addWidget( mSearchFrame, 1 );
|
||||
mSearchWidgetToolButton = new QgsSearchWidgetToolButton();
|
||||
l->addWidget( mSearchWidgetToolButton, 0 );
|
||||
|
||||
|
||||
mStack = new QStackedWidget;
|
||||
mStack->addWidget( mEditPage );
|
||||
mStack->addWidget( mSearchPage );
|
||||
|
||||
l = new QHBoxLayout();
|
||||
l->setMargin( 0 );
|
||||
l->setContentsMargins( 0, 0, 0, 0 );
|
||||
setLayout( l );
|
||||
l->addWidget( mStack );
|
||||
|
||||
if ( !mWidget || !mForm )
|
||||
return;
|
||||
|
||||
QLayout* l = new QHBoxLayout();
|
||||
l->setMargin( 0 );
|
||||
l->setContentsMargins( 0, 0, 0, 0 );
|
||||
l->addWidget( mWidget->widget() );
|
||||
mEditPage->layout()->addWidget( mWidget->widget() );
|
||||
|
||||
if ( mWidget->widget() )
|
||||
{
|
||||
@ -48,7 +82,6 @@ QgsAttributeFormEditorWidget::QgsAttributeFormEditorWidget( QgsEditorWidgetWrapp
|
||||
|
||||
mMultiEditButton->setField( mWidget->field() );
|
||||
|
||||
setLayout( l );
|
||||
updateWidgets();
|
||||
}
|
||||
|
||||
@ -58,6 +91,13 @@ QgsAttributeFormEditorWidget::~QgsAttributeFormEditorWidget()
|
||||
delete mMultiEditButton;
|
||||
}
|
||||
|
||||
void QgsAttributeFormEditorWidget::setSearchWidgetWrapper( QgsSearchWidgetWrapper* wrapper )
|
||||
{
|
||||
mSearchWidget = wrapper;
|
||||
mSearchFrame->layout()->addWidget( wrapper->widget() );
|
||||
mSearchWidgetToolButton->setSearchWidgetWrapper( wrapper );
|
||||
}
|
||||
|
||||
void QgsAttributeFormEditorWidget::setMode( QgsAttributeFormEditorWidget::Mode mode )
|
||||
{
|
||||
mMode = mode;
|
||||
@ -82,6 +122,11 @@ void QgsAttributeFormEditorWidget::changesCommitted()
|
||||
mIsChanged = false;
|
||||
}
|
||||
|
||||
void QgsAttributeFormEditorWidget::resetSearch()
|
||||
{
|
||||
mSearchWidgetToolButton->setInactive();
|
||||
}
|
||||
|
||||
void QgsAttributeFormEditorWidget::initialize( const QVariant& initialValue, bool mixedValues )
|
||||
{
|
||||
if ( mWidget )
|
||||
@ -101,6 +146,19 @@ QVariant QgsAttributeFormEditorWidget::currentValue() const
|
||||
return mWidget->value();
|
||||
}
|
||||
|
||||
QString QgsAttributeFormEditorWidget::currentFilterExpression() const
|
||||
{
|
||||
if ( !mSearchWidgetToolButton->isActive() )
|
||||
return QString();
|
||||
|
||||
return mSearchWidget->createExpression( mSearchWidgetToolButton->activeFlags() );
|
||||
}
|
||||
|
||||
QWidget* QgsAttributeFormEditorWidget::searchWidgetFrame()
|
||||
{
|
||||
return mSearchFrame;
|
||||
}
|
||||
|
||||
void QgsAttributeFormEditorWidget::editorWidgetChanged( const QVariant& value )
|
||||
{
|
||||
if ( mBlockValueUpdate )
|
||||
@ -111,6 +169,7 @@ void QgsAttributeFormEditorWidget::editorWidgetChanged( const QVariant& value )
|
||||
switch ( mMode )
|
||||
{
|
||||
case DefaultMode:
|
||||
case SearchMode:
|
||||
break;
|
||||
case MultiEditMode:
|
||||
mMultiEditButton->setIsChanged( true );
|
||||
@ -130,6 +189,7 @@ void QgsAttributeFormEditorWidget::resetValue()
|
||||
switch ( mMode )
|
||||
{
|
||||
case DefaultMode:
|
||||
case SearchMode:
|
||||
break;
|
||||
case MultiEditMode:
|
||||
{
|
||||
@ -146,6 +206,11 @@ void QgsAttributeFormEditorWidget::setFieldTriggered()
|
||||
mIsChanged = true;
|
||||
}
|
||||
|
||||
QgsSearchWidgetToolButton* QgsAttributeFormEditorWidget::searchWidgetToolButton()
|
||||
{
|
||||
return mSearchWidgetToolButton;
|
||||
}
|
||||
|
||||
QgsVectorLayer* QgsAttributeFormEditorWidget::layer()
|
||||
{
|
||||
return mForm ? mForm->layer() : nullptr;
|
||||
@ -153,14 +218,15 @@ QgsVectorLayer* QgsAttributeFormEditorWidget::layer()
|
||||
|
||||
void QgsAttributeFormEditorWidget::updateWidgets()
|
||||
{
|
||||
bool hasMultiEditButton = ( layout()->indexOf( mMultiEditButton ) >= 0 );
|
||||
//first update the tool buttons
|
||||
bool hasMultiEditButton = ( mEditPage->layout()->indexOf( mMultiEditButton ) >= 0 );
|
||||
bool fieldReadOnly = layer()->editFormConfig()->readOnly( mWidget->fieldIdx() );
|
||||
|
||||
if ( hasMultiEditButton )
|
||||
{
|
||||
if ( mMode != MultiEditMode || fieldReadOnly )
|
||||
{
|
||||
layout()->removeWidget( mMultiEditButton );
|
||||
mEditPage->layout()->removeWidget( mMultiEditButton );
|
||||
mMultiEditButton->setParent( nullptr );
|
||||
}
|
||||
}
|
||||
@ -168,7 +234,23 @@ void QgsAttributeFormEditorWidget::updateWidgets()
|
||||
{
|
||||
if ( mMode == MultiEditMode && !fieldReadOnly )
|
||||
{
|
||||
layout()->addWidget( mMultiEditButton );
|
||||
mEditPage->layout()->addWidget( mMultiEditButton );
|
||||
}
|
||||
}
|
||||
|
||||
switch ( mMode )
|
||||
{
|
||||
case DefaultMode:
|
||||
case MultiEditMode:
|
||||
{
|
||||
mStack->setCurrentWidget( mEditPage );
|
||||
break;
|
||||
}
|
||||
|
||||
case SearchMode:
|
||||
{
|
||||
mStack->setCurrentWidget( mSearchPage );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,10 @@
|
||||
class QgsAttributeForm;
|
||||
class QgsEditorWidgetWrapper;
|
||||
class QgsMultiEditToolButton;
|
||||
class QgsSearchWidgetToolButton;
|
||||
class QgsVectorLayer;
|
||||
|
||||
class QgsSearchWidgetWrapper;
|
||||
class QStackedWidget;
|
||||
|
||||
/** \ingroup gui
|
||||
* \class QgsAttributeFormEditorWidget
|
||||
@ -44,17 +46,32 @@ class GUI_EXPORT QgsAttributeFormEditorWidget : public QWidget
|
||||
{
|
||||
DefaultMode, /*!< Default mode, only the editor widget is shown */
|
||||
MultiEditMode, /*!< Multi edit mode, both the editor widget and a QgsMultiEditToolButton is shown */
|
||||
// TODO: SearchMode, /*!< Layer search/filter mode */
|
||||
SearchMode, /*!< Layer search/filter mode */
|
||||
};
|
||||
|
||||
/** Constructor for QgsAttributeFormEditorWidget.
|
||||
* @param editorWidget associated editor widget wrapper
|
||||
* @param editorWidget associated editor widget wrapper (for default/edit modes)
|
||||
* @param form parent attribute form
|
||||
*/
|
||||
explicit QgsAttributeFormEditorWidget( QgsEditorWidgetWrapper* editorWidget, QgsAttributeForm* form );
|
||||
explicit QgsAttributeFormEditorWidget( QgsEditorWidgetWrapper* editorWidget,
|
||||
QgsAttributeForm* form );
|
||||
|
||||
~QgsAttributeFormEditorWidget();
|
||||
|
||||
/** Sets the search widget wrapper for the widget used when the form is in
|
||||
* search mode.
|
||||
* @param wrapper search widget wrapper.
|
||||
* @note the search widget wrapper should be created using searchWidgetFrame()
|
||||
* as its parent
|
||||
*/
|
||||
void setSearchWidgetWrapper( QgsSearchWidgetWrapper* wrapper );
|
||||
|
||||
/** Returns the widget which should be used as a parent during construction
|
||||
* of the search widget wrapper.
|
||||
* @see setSearchWidgetWrapper()
|
||||
*/
|
||||
QWidget* searchWidgetFrame();
|
||||
|
||||
/** Sets the current mode for the widget. The widget will adapt its state and visible widgets to
|
||||
* reflect the updated mode. Eg, showing multi edit tool buttons if the mode is set to MultiEditMode.
|
||||
* @param mode widget mode
|
||||
@ -82,6 +99,12 @@ class GUI_EXPORT QgsAttributeFormEditorWidget : public QWidget
|
||||
*/
|
||||
QVariant currentValue() const;
|
||||
|
||||
/** Creates an expression matching the current search filter value and
|
||||
* search properties represented in the widget.
|
||||
* @note added in QGIS 2.16
|
||||
*/
|
||||
QString currentFilterExpression() const;
|
||||
|
||||
public slots:
|
||||
|
||||
/** Sets whether the widget should be displayed in a "mixed values" mode.
|
||||
@ -93,6 +116,10 @@ class GUI_EXPORT QgsAttributeFormEditorWidget : public QWidget
|
||||
*/
|
||||
void changesCommitted();
|
||||
|
||||
/** Resets the search/filter value of the widget.
|
||||
*/
|
||||
void resetSearch();
|
||||
|
||||
signals:
|
||||
|
||||
//! Emitted when the widget's value changes
|
||||
@ -110,13 +137,28 @@ class GUI_EXPORT QgsAttributeFormEditorWidget : public QWidget
|
||||
//! Triggered when the multi edit tool button "set field value" action is selected
|
||||
void setFieldTriggered();
|
||||
|
||||
protected:
|
||||
|
||||
/** Returns a pointer to the search widget tool button in the widget.
|
||||
* @note this method is in place for unit testing only, and is not considered
|
||||
* stable API
|
||||
*/
|
||||
QgsSearchWidgetToolButton* searchWidgetToolButton();
|
||||
|
||||
private:
|
||||
|
||||
QWidget* mEditPage;
|
||||
QWidget* mSearchPage;
|
||||
QStackedWidget* mStack;
|
||||
QWidget* mSearchFrame;
|
||||
|
||||
QgsEditorWidgetWrapper* mWidget;
|
||||
QgsSearchWidgetWrapper* mSearchWidget;
|
||||
QgsAttributeForm* mForm;
|
||||
Mode mMode;
|
||||
|
||||
QgsMultiEditToolButton* mMultiEditButton;
|
||||
QgsSearchWidgetToolButton* mSearchWidgetToolButton;
|
||||
QVariant mPreviousValue;
|
||||
bool mBlockValueUpdate;
|
||||
bool mIsMixed;
|
||||
|
@ -322,6 +322,32 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="mSearchFormButton">
|
||||
<property name="toolTip">
|
||||
<string>Select/filter features using form</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../images/images.qrc">
|
||||
<normaloff>:/images/themes/default/mActionFilter2.svg</normaloff>:/images/themes/default/mActionFilter2.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="mSelectedToTopButton">
|
||||
<property name="toolTip">
|
||||
|
@ -11,6 +11,7 @@ ADD_PYTHON_TEST(PyQgsAggregateCalculator test_qgsaggregatecalculator.py)
|
||||
ADD_PYTHON_TEST(PyQgsAnalysis test_qgsanalysis.py)
|
||||
ADD_PYTHON_TEST(PyQgsApplication test_qgsapplication.py)
|
||||
ADD_PYTHON_TEST(PyQgsAtlasComposition test_qgsatlascomposition.py)
|
||||
ADD_PYTHON_TEST(PyQgsAttributeFormEditorWidget test_qgsattributeformeditorwidget.py)
|
||||
ADD_PYTHON_TEST(PyQgsAttributeTableModel test_qgsattributetablemodel.py)
|
||||
#ADD_PYTHON_TEST(PyQgsAuthenticationSystem test_qgsauthsystem.py)
|
||||
ADD_PYTHON_TEST(PyQgsBlendModes test_qgsblendmodes.py)
|
||||
@ -68,6 +69,8 @@ ADD_PYTHON_TEST(PyQgsSingleSymbolRenderer test_qgssinglesymbolrenderer.py)
|
||||
ADD_PYTHON_TEST(PyQgsShapefileProvider test_provider_shapefile.py)
|
||||
ADD_PYTHON_TEST(PyQgsTabfileProvider test_provider_tabfile.py)
|
||||
ADD_PYTHON_TEST(PyQgsOGRProvider test_provider_ogr.py)
|
||||
ADD_PYTHON_TEST(PyQgsSearchWidgetToolButton test_qgssearchwidgettoolbutton.py)
|
||||
ADD_PYTHON_TEST(PyQgsSearchWidgetWrapper test_qgssearchwidgetwrapper.py)
|
||||
ADD_PYTHON_TEST(PyQgsSpatialIndex test_qgsspatialindex.py)
|
||||
ADD_PYTHON_TEST(PyQgsSpatialiteProvider test_provider_spatialite.py)
|
||||
ADD_PYTHON_TEST(PyQgsSQLStatement test_qgssqlstatement.py)
|
||||
|
74
tests/src/python/test_qgsattributeformeditorwidget.py
Normal file
74
tests/src/python/test_qgsattributeformeditorwidget.py
Normal file
@ -0,0 +1,74 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for QgsAttributeFormEditorWidget.
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
"""
|
||||
__author__ = 'Nyall Dawson'
|
||||
__date__ = '2016-05'
|
||||
__copyright__ = 'Copyright 2016, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import qgis # NOQA
|
||||
|
||||
from qgis.gui import (QgsSearchWidgetWrapper,
|
||||
QgsAttributeFormEditorWidget,
|
||||
QgsSearchWidgetToolButton,
|
||||
QgsDefaultSearchWidgetWrapper)
|
||||
from qgis.core import (QgsVectorLayer)
|
||||
from qgis.PyQt.QtWidgets import QWidget
|
||||
from qgis.testing import start_app, unittest
|
||||
|
||||
start_app()
|
||||
|
||||
|
||||
class PyQgsAttributeFormEditorWidget(unittest.TestCase):
|
||||
|
||||
def testCurrentFilterExpression(self):
|
||||
""" Test creating an expression using the widget"""
|
||||
|
||||
layer = QgsVectorLayer("Point?field=fldint:integer", "test", "memory")
|
||||
parent = QWidget()
|
||||
w = QgsDefaultSearchWidgetWrapper(layer, 0, parent)
|
||||
af = QgsAttributeFormEditorWidget(None, None)
|
||||
af.setSearchWidgetWrapper(w)
|
||||
|
||||
# test that filter combines both current value in search widget wrapper and flags from search tool button
|
||||
w.lineEdit().setText('5.5')
|
||||
af.searchWidgetToolButton().setActiveFlags(QgsSearchWidgetWrapper.EqualTo)
|
||||
self.assertEquals(af.currentFilterExpression(), '"fldint"=5.5')
|
||||
af.searchWidgetToolButton().setActiveFlags(QgsSearchWidgetWrapper.NotEqualTo)
|
||||
self.assertEquals(af.currentFilterExpression(), '"fldint"<>5.5')
|
||||
|
||||
def testSetActive(self):
|
||||
""" Test setting the search as active - should set active flags to match search widget wrapper's defaults """
|
||||
|
||||
layer = QgsVectorLayer("Point?field=fldtext:string&field=fldint:integer", "test", "memory")
|
||||
parent = QWidget()
|
||||
w = QgsDefaultSearchWidgetWrapper(layer, 0, parent)
|
||||
af = QgsAttributeFormEditorWidget(None, None)
|
||||
af.setSearchWidgetWrapper(w)
|
||||
|
||||
sb = af.searchWidgetToolButton()
|
||||
# start with inactive
|
||||
sb.setActiveFlags(QgsSearchWidgetWrapper.FilterFlags())
|
||||
# set to inactive
|
||||
sb.setActive()
|
||||
# check that correct default flag was taken from search widget wrapper
|
||||
self.assertTrue(sb.activeFlags() & QgsSearchWidgetWrapper.Contains)
|
||||
|
||||
# try again with numeric field - default should be "EqualTo"
|
||||
w = QgsDefaultSearchWidgetWrapper(layer, 1, parent)
|
||||
af.setSearchWidgetWrapper(w)
|
||||
# start with inactive
|
||||
sb.setActiveFlags(QgsSearchWidgetWrapper.FilterFlags())
|
||||
# set to inactive
|
||||
sb.setActive()
|
||||
# check that correct default flag was taken from search widget wrapper
|
||||
self.assertTrue(sb.activeFlags() & QgsSearchWidgetWrapper.EqualTo)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
163
tests/src/python/test_qgssearchwidgettoolbutton.py
Normal file
163
tests/src/python/test_qgssearchwidgettoolbutton.py
Normal file
@ -0,0 +1,163 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for QgsSearchWidgetToolButton.
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
"""
|
||||
__author__ = 'Nyall Dawson'
|
||||
__date__ = '18/05/2016'
|
||||
__copyright__ = 'Copyright 2016, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import qgis # switch sip api
|
||||
|
||||
from qgis.gui import QgsSearchWidgetToolButton, QgsSearchWidgetWrapper
|
||||
|
||||
from qgis.testing import (start_app,
|
||||
unittest
|
||||
)
|
||||
|
||||
start_app()
|
||||
|
||||
|
||||
class TestQgsSearchWidgetToolButton(unittest.TestCase):
|
||||
|
||||
def testAvailableFlags(self):
|
||||
"""
|
||||
Test setting available flags
|
||||
"""
|
||||
w = QgsSearchWidgetToolButton()
|
||||
w.setAvailableFlags(QgsSearchWidgetWrapper.EqualTo |
|
||||
QgsSearchWidgetWrapper.NotEqualTo |
|
||||
QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
|
||||
flags = w.availableFlags()
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.EqualTo)
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.NotEqualTo)
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.Between)
|
||||
|
||||
# setting available flags should update active flags
|
||||
w.setActiveFlags(QgsSearchWidgetWrapper.NotEqualTo | QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
w.setAvailableFlags(QgsSearchWidgetWrapper.EqualTo |
|
||||
QgsSearchWidgetWrapper.NotEqualTo)
|
||||
flags = w.activeFlags()
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.EqualTo)
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.NotEqualTo)
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
|
||||
def testActiveFlags(self):
|
||||
"""
|
||||
Test setting/retrieving active flag logic
|
||||
"""
|
||||
w = QgsSearchWidgetToolButton()
|
||||
w.setAvailableFlags(QgsSearchWidgetWrapper.EqualTo |
|
||||
QgsSearchWidgetWrapper.NotEqualTo |
|
||||
QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
|
||||
w.setActiveFlags(QgsSearchWidgetWrapper.EqualTo)
|
||||
flags = w.activeFlags()
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.EqualTo)
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.NotEqualTo)
|
||||
|
||||
w.setActiveFlags(QgsSearchWidgetWrapper.EqualTo | QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
flags = w.activeFlags()
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.EqualTo)
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
|
||||
# setting a non-available flag as active
|
||||
w.setAvailableFlags(QgsSearchWidgetWrapper.EqualTo |
|
||||
QgsSearchWidgetWrapper.NotEqualTo)
|
||||
w.setActiveFlags(QgsSearchWidgetWrapper.EqualTo | QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
flags = w.activeFlags()
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.EqualTo)
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
|
||||
# setting conflicting flags
|
||||
w.setActiveFlags(QgsSearchWidgetWrapper.EqualTo | QgsSearchWidgetWrapper.NotEqualTo)
|
||||
flags = w.activeFlags()
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.EqualTo)
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.NotEqualTo)
|
||||
|
||||
def testToggleFlag(self):
|
||||
""" Test toggling flags """
|
||||
w = QgsSearchWidgetToolButton()
|
||||
w.setAvailableFlags(QgsSearchWidgetWrapper.EqualTo |
|
||||
QgsSearchWidgetWrapper.NotEqualTo |
|
||||
QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
w.setActiveFlags(QgsSearchWidgetWrapper.EqualTo)
|
||||
# should set flag
|
||||
w.toggleFlag(QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
flags = w.activeFlags()
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.EqualTo)
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.NotEqualTo)
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
# should clear flag
|
||||
w.toggleFlag(QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
flags = w.activeFlags()
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.EqualTo)
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.NotEqualTo)
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
|
||||
#toggling non-available flag should be ignored
|
||||
w.setAvailableFlags(QgsSearchWidgetWrapper.Between |
|
||||
QgsSearchWidgetWrapper.NotEqualTo)
|
||||
w.setActiveFlags(QgsSearchWidgetWrapper.Between)
|
||||
# should be ignored
|
||||
w.toggleFlag(QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
w.toggleFlag(QgsSearchWidgetWrapper.LessThan)
|
||||
flags = w.activeFlags()
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.LessThan)
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.Between)
|
||||
|
||||
# toggling exclusive flag should result in other exclusive flags being cleared
|
||||
w.setAvailableFlags(QgsSearchWidgetWrapper.Between |
|
||||
QgsSearchWidgetWrapper.NotEqualTo |
|
||||
QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
w.setActiveFlags(QgsSearchWidgetWrapper.Between | QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
w.toggleFlag(QgsSearchWidgetWrapper.Between)
|
||||
flags = w.activeFlags()
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.NotEqualTo)
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.Between)
|
||||
w.toggleFlag(QgsSearchWidgetWrapper.NotEqualTo)
|
||||
flags = w.activeFlags()
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.NotEqualTo)
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.Between)
|
||||
|
||||
def testSetInactive(self):
|
||||
""" Test setting the search as inactive """
|
||||
w = QgsSearchWidgetToolButton()
|
||||
w.setAvailableFlags(QgsSearchWidgetWrapper.EqualTo |
|
||||
QgsSearchWidgetWrapper.NotEqualTo |
|
||||
QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
w.setActiveFlags(QgsSearchWidgetWrapper.EqualTo |
|
||||
QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
self.assertTrue(w.isActive())
|
||||
w.setInactive()
|
||||
flags = w.activeFlags()
|
||||
self.assertFalse(flags & QgsSearchWidgetWrapper.EqualTo)
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
self.assertFalse(w.isActive())
|
||||
|
||||
def testSetActive(self):
|
||||
""" Test setting the search as active """
|
||||
w = QgsSearchWidgetToolButton()
|
||||
w.setAvailableFlags(QgsSearchWidgetWrapper.Between |
|
||||
QgsSearchWidgetWrapper.NotEqualTo |
|
||||
QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
w.setActiveFlags(QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
self.assertFalse(w.isActive())
|
||||
w.setActive()
|
||||
flags = w.activeFlags()
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.NotEqualTo)
|
||||
self.assertTrue(flags & QgsSearchWidgetWrapper.CaseInsensitive)
|
||||
self.assertTrue(w.isActive())
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
230
tests/src/python/test_qgssearchwidgetwrapper.py
Normal file
230
tests/src/python/test_qgssearchwidgetwrapper.py
Normal file
@ -0,0 +1,230 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for QgsSearchWidgetWrapper.
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
"""
|
||||
__author__ = 'Nyall Dawson'
|
||||
__date__ = '2016-05'
|
||||
__copyright__ = 'Copyright 2016, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import qgis # NOQA
|
||||
|
||||
from qgis.gui import (QgsSearchWidgetWrapper,
|
||||
QgsDefaultSearchWidgetWrapper,
|
||||
QgsValueMapSearchWidgetWrapper,
|
||||
QgsValueRelationSearchWidgetWrapper)
|
||||
from qgis.core import (QgsVectorLayer,
|
||||
QgsFeature,
|
||||
QgsMapLayerRegistry,
|
||||
)
|
||||
from qgis.PyQt.QtWidgets import QWidget
|
||||
|
||||
from qgis.testing import start_app, unittest
|
||||
|
||||
start_app()
|
||||
|
||||
|
||||
class PyQgsSearchWidgetWrapper(unittest.TestCase):
|
||||
|
||||
def testFlagToString(self):
|
||||
# test converting QgsSearchWidgetWrapper.FilterFlag to string
|
||||
tests = [QgsSearchWidgetWrapper.EqualTo,
|
||||
QgsSearchWidgetWrapper.NotEqualTo,
|
||||
QgsSearchWidgetWrapper.GreaterThan,
|
||||
QgsSearchWidgetWrapper.LessThan,
|
||||
QgsSearchWidgetWrapper.GreaterThanOrEqualTo,
|
||||
QgsSearchWidgetWrapper.LessThanOrEqualTo,
|
||||
QgsSearchWidgetWrapper.Between,
|
||||
QgsSearchWidgetWrapper.CaseInsensitive,
|
||||
QgsSearchWidgetWrapper.Contains,
|
||||
QgsSearchWidgetWrapper.DoesNotContain,
|
||||
QgsSearchWidgetWrapper.IsNull
|
||||
]
|
||||
for t in tests:
|
||||
self.assertTrue(len(QgsSearchWidgetWrapper.toString(t)) > 0)
|
||||
|
||||
def testExclusiveFlags(self):
|
||||
# test flag exclusive/non exclusive
|
||||
exclusive = QgsSearchWidgetWrapper.exclusiveFilterFlags()
|
||||
non_exclusive = QgsSearchWidgetWrapper.nonExclusiveFilterFlags()
|
||||
for e in exclusive:
|
||||
self.assertFalse(e in non_exclusive)
|
||||
|
||||
|
||||
class PyQgsDefaultSearchWidgetWrapper(unittest.TestCase):
|
||||
|
||||
def testCreateExpression(self):
|
||||
""" Test creating an expression using the widget"""
|
||||
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddate:datetime",
|
||||
"test", "memory")
|
||||
|
||||
parent = QWidget()
|
||||
w = QgsDefaultSearchWidgetWrapper(layer, 0)
|
||||
w.initWidget(parent)
|
||||
|
||||
line_edit = w.lineEdit()
|
||||
line_edit.setText('test')
|
||||
case_sensitive = w.caseSensitiveCheckBox()
|
||||
|
||||
case_sensitive.setChecked(False)
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), 'lower("fldtxt")=lower(\'test\')')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), 'lower("fldtxt")<>lower(\'test\')')
|
||||
case_sensitive.setChecked(True)
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldtxt"=\'test\'')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldtxt"<>\'test\'')
|
||||
case_sensitive.setChecked(False)
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.Contains), '"fldtxt" ILIKE \'%test%\'')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.DoesNotContain), 'NOT ("fldtxt" ILIKE \'%test%\')')
|
||||
case_sensitive.setChecked(True)
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.Contains), '"fldtxt" LIKE \'%test%\'')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.DoesNotContain), 'NOT ("fldtxt" LIKE \'%test%\')')
|
||||
case_sensitive.setChecked(False)
|
||||
|
||||
# numeric field
|
||||
parent = QWidget()
|
||||
w = QgsDefaultSearchWidgetWrapper(layer, 1)
|
||||
w.initWidget(parent)
|
||||
|
||||
# may need updating if widget layout changes:
|
||||
line_edit = w.lineEdit()
|
||||
line_edit.setText('5.5')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldint"=5.5')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldint"<>5.5')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.GreaterThan), '"fldint">5.5')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.LessThan), '"fldint"<5.5')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.GreaterThanOrEqualTo), '"fldint">=5.5')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.LessThanOrEqualTo), '"fldint"<=5.5')
|
||||
|
||||
# date/time/datetime
|
||||
parent = QWidget()
|
||||
w = QgsDefaultSearchWidgetWrapper(layer, 2)
|
||||
w.initWidget(parent)
|
||||
|
||||
# may need updating if widget layout changes:
|
||||
line_edit = w.lineEdit()
|
||||
line_edit.setText('2015-06-03')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"flddate"=\'2015-06-03\'')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"flddate"<>\'2015-06-03\'')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.GreaterThan), '"flddate">\'2015-06-03\'')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.LessThan), '"flddate"<\'2015-06-03\'')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.GreaterThanOrEqualTo), '"flddate">=\'2015-06-03\'')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.LessThanOrEqualTo), '"flddate"<=\'2015-06-03\'')
|
||||
|
||||
|
||||
class PyQgsValueMapSearchWidgetWrapper(unittest.TestCase):
|
||||
|
||||
def testCreateExpression(self):
|
||||
""" Test creating an expression using the widget"""
|
||||
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "test", "memory")
|
||||
|
||||
w = QgsValueMapSearchWidgetWrapper(layer, 0)
|
||||
config = {"val1": 1,
|
||||
"val2": 200}
|
||||
w.setConfig(config)
|
||||
c = w.widget()
|
||||
|
||||
# first, set it to the "select value" item
|
||||
c.setCurrentIndex(0)
|
||||
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.IsNull), '')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '')
|
||||
|
||||
c.setCurrentIndex(1)
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldtxt"=\'1\'')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldtxt"<>\'1\'')
|
||||
c.setCurrentIndex(2)
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldtxt"=\'200\'')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldtxt"<>\'200\'')
|
||||
|
||||
# try with numeric field
|
||||
w = QgsValueMapSearchWidgetWrapper(layer, 1)
|
||||
w.setConfig(config)
|
||||
c = w.widget()
|
||||
c.setCurrentIndex(1)
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldint" IS NULL')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldint"=1')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldint"<>1')
|
||||
|
||||
|
||||
class PyQgsValueRelationSearchWidgetWrapper(unittest.TestCase):
|
||||
|
||||
def testCreateExpression(self):
|
||||
""" Test creating an expression using the widget"""
|
||||
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "test", "memory")
|
||||
# setup value relation
|
||||
parent_layer = QgsVectorLayer("Point?field=stringkey:string&field=intkey:integer&field=display:string", "parent", "memory")
|
||||
f1 = QgsFeature(parent_layer.fields(), 1)
|
||||
f1.setAttributes(['a', 1, 'value a'])
|
||||
f2 = QgsFeature(parent_layer.fields(), 2)
|
||||
f2.setAttributes(['b', 2, 'value b'])
|
||||
f3 = QgsFeature(parent_layer.fields(), 3)
|
||||
f3.setAttributes(['c', 3, 'value c'])
|
||||
parent_layer.dataProvider().addFeatures([f1, f2, f3])
|
||||
QgsMapLayerRegistry.instance().addMapLayers([layer, parent_layer])
|
||||
|
||||
config = {"Layer": parent_layer.id(),
|
||||
"Key": 'stringkey',
|
||||
"Value": 'display'}
|
||||
|
||||
w = QgsValueRelationSearchWidgetWrapper(layer, 0)
|
||||
w.setConfig(config)
|
||||
c = w.widget()
|
||||
|
||||
# first, set it to the "select value" item
|
||||
c.setCurrentIndex(0)
|
||||
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '')
|
||||
|
||||
c.setCurrentIndex(1)
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldtxt"=\'a\'')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldtxt"<>\'a\'')
|
||||
c.setCurrentIndex(2)
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldtxt"=\'b\'')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldtxt"<>\'b\'')
|
||||
|
||||
# try with numeric field
|
||||
w = QgsValueRelationSearchWidgetWrapper(layer, 1)
|
||||
config['Key'] = 'intkey'
|
||||
w.setConfig(config)
|
||||
c = w.widget()
|
||||
c.setCurrentIndex(c.findText('value c'))
|
||||
self.assertEqual(c.currentIndex(), 3)
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldint" IS NULL')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldint"=3')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldint"<>3')
|
||||
|
||||
# try with allow null set
|
||||
w = QgsValueRelationSearchWidgetWrapper(layer, 1)
|
||||
config['AllowNull'] = True
|
||||
w.setConfig(config)
|
||||
c = w.widget()
|
||||
c.setCurrentIndex(c.findText('value c'))
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldint" IS NULL')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldint"=3')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldint"<>3')
|
||||
|
||||
# try with line edit
|
||||
w = QgsValueRelationSearchWidgetWrapper(layer, 1)
|
||||
config['UseCompleter'] = True
|
||||
w.setConfig(config)
|
||||
l = w.widget()
|
||||
l.setText('value b')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldint" IS NULL')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldint"=2')
|
||||
self.assertEquals(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldint"<>2')
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
x
Reference in New Issue
Block a user