[FEATURE] Search widget for relations using aggregates

For each child relations, the subform is visible.

Each attribute of the children has a tool button option to define to which
aggregate the specified value should be compared. This allows for searching
things like

 * Each city where the highest building is more than 300 m
 * Each sensor where the median value is lower than 50 ppm
 * Each feature with a child with a missing value
 * ...
This commit is contained in:
Matthias Kuhn 2017-11-01 21:14:34 +01:00
parent f5559b5143
commit fdd00870ee
35 changed files with 1246 additions and 263 deletions

View File

@ -26,6 +26,13 @@ class QgsAggregateCalculator
%End
public:
struct AggregateInfo
{
QString function;
QString name;
QSet<QVariant::Type> supportedTypes;
};
enum Aggregate
{
Count,
@ -138,6 +145,11 @@ class QgsAggregateCalculator
:rtype: Aggregate
%End
static QList< QgsAggregateCalculator::AggregateInfo > aggregates();
%Docstring
:rtype: list of QgsAggregateCalculator.AggregateInfo
%End
};

View File

@ -169,6 +169,25 @@ class QgsSearchWidgetWrapper : QgsWidgetWrapper
:rtype: str
%End
QString createFieldIdentifier() const;
%Docstring
:rtype: str
%End
QString aggregate() const;
%Docstring
:rtype: str
%End
void setAggregate( const QString &aggregate );
QgsRelation aggregateRelation() const;
%Docstring
:rtype: QgsRelation
%End
void setAggregateRelation( const QgsRelation &aggregateRelation );
public slots:
virtual void clearWidget();

View File

@ -0,0 +1,53 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/editorwidgets/qgsrelationaggregatesearchwidgetwrapper.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsRelationAggregateSearchWidgetWrapper : QgsSearchWidgetWrapper
{
%Docstring
*************************************************************************
qgsrelationaggregatesearchwidget.h
-----------------------------
Date : Nov 2017
Copyright : (C) 2017 Matthias Kuhn
Email : matthias@opengis.ch
**************************************************************************
*
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. *
*
**************************************************************************
%End
%TypeHeaderCode
#include "qgsrelationaggregatesearchwidgetwrapper.h"
%End
public:
explicit QgsRelationAggregateSearchWidgetWrapper( QgsVectorLayer *vl, QgsRelationWidgetWrapper *wrapper, QWidget *parent /TransferThis/ = 0 );
virtual QString expression() const;
virtual bool valid() const;
virtual QWidget *createWidget( QWidget *parent );
virtual bool applyDirectly();
virtual void setExpression( const QString &value );
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/editorwidgets/qgsrelationaggregatesearchwidgetwrapper.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -71,6 +71,11 @@ class QgsRelationWidgetWrapper : QgsWidgetWrapper
.. versionadded:: 2.18
%End
QgsRelation relation() const;
%Docstring
:rtype: QgsRelation
%End
protected:
virtual QWidget *createWidget( QWidget *parent );

View File

@ -52,9 +52,11 @@
%Include qgisinterface.sip
%Include qgsactionmenu.sip
%Include qgsadvanceddigitizingdockwidget.sip
%Include qgsaggregatetoolbutton.sip
%Include qgsattributedialog.sip
%Include qgsattributeform.sip
%Include qgsattributeformeditorwidget.sip
%Include qgsattributeformrelationeditorwidget.sip
%Include qgsattributeformwidget.sip
%Include qgsattributetypeloaddialog.sip
%Include qgsblendmodecombobox.sip
@ -279,6 +281,7 @@
%Include editorwidgets/qgsrelationreferencewidget.sip
%Include editorwidgets/qgsrelationreferencewidgetwrapper.sip
%Include editorwidgets/qgsrelationwidgetwrapper.sip
%Include editorwidgets/qgsrelationaggregatesearchwidgetwrapper.sip
%Include editorwidgets/qgssearchwidgettoolbutton.sip
%Include editorwidgets/qgsspinbox.sip
%Include editorwidgets/qgsvaluemapsearchwidgetwrapper.sip

View File

@ -0,0 +1,68 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsaggregatetoolbutton.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsAggregateToolButton : QToolButton
{
%Docstring
*************************************************************************
qgsaggregatetoolbutton.h
--------------------------------------
Date : Nov 2017
Copyright : (C) 2017 Matthias Kuhn
Email : matthias@opengis.ch
**************************************************************************
*
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. *
*
**************************************************************************
%End
%TypeHeaderCode
#include "qgsaggregatetoolbutton.h"
%End
public:
QgsAggregateToolButton();
void setType( QVariant::Type type );
QVariant::Type type() const;
%Docstring
:rtype: QVariant.Type
%End
void setActive( bool active );
bool active() const;
%Docstring
:rtype: bool
%End
QString aggregate() const;
%Docstring
:rtype: str
%End
void setAggregate( const QString &aggregate );
signals:
void aggregateChanged();
void activeChanged();
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsaggregatetoolbutton.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -25,6 +25,7 @@ class QgsAttributeForm : QWidget
AddFeatureMode,
MultiEditMode,
SearchMode,
AggregateSearchMode,
};
enum FilterType
@ -123,6 +124,11 @@ class QgsAttributeForm : QWidget
.. versionadded:: 2.16
%End
QString aggregateFilter() const;
%Docstring
:rtype: str
%End
signals:
void attributeChanged( const QString &attribute, const QVariant &value );

View File

@ -56,15 +56,6 @@ class QgsAttributeFormEditorWidget : QgsAttributeFormWidget
:rtype: QVariant
%End
virtual QString currentFilterExpression() const;
%Docstring
Creates an expression matching the current search filter value and
search properties represented in the widget.
.. versionadded:: 2.16
:rtype: str
%End
void setConstraintStatus( const QString &constraint, const QString &description, const QString &err, QgsEditorWidgetWrapper::ConstraintResult result );
%Docstring
Set the constraint status for this widget.
@ -88,11 +79,6 @@ class QgsAttributeFormEditorWidget : QgsAttributeFormWidget
Called when field values have been committed;
%End
void resetSearch();
%Docstring
Resets the search/filter value of the widget.
%End
signals:
void valueChanged( const QVariant &value );
@ -101,55 +87,6 @@ class QgsAttributeFormEditorWidget : QgsAttributeFormWidget
\param value new widget value
%End
protected:
QgsSearchWidgetToolButton *searchWidgetToolButton();
%Docstring
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
:rtype: QgsSearchWidgetToolButton
%End
void setSearchWidgetWrapper( QgsSearchWidgetWrapper *wrapper );
%Docstring
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
.. note::
this method is in place for unit testing only, and is not considered
stable API
%End
QWidget *searchWidgetFrame();
%Docstring
Returns the widget which should be used as a parent during construction
of the search widget wrapper.
.. note::
this method is in place for unit testing only, and is not considered
stable AP
:rtype: QWidget
%End
QList< QgsSearchWidgetWrapper * > searchWidgetWrappers();
%Docstring
Returns the search widget wrapper used in this widget. The wrapper must
first be created using createSearchWidgetWrapper()
.. note::
this method is in place for unit testing only, and is not considered
stable API
:rtype: list of QgsSearchWidgetWrapper
%End
};
/************************************************************************

View File

@ -0,0 +1,48 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsattributeformrelationeditorwidget.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsAttributeFormRelationEditorWidget : QgsAttributeFormWidget
{
%Docstring
*************************************************************************
qgsattributeformrelationeditorwidget.h
--------------------------------------
Date : Nov 2017
Copyright : (C) 2017 Matthias Kuhn
Email : matthias@opengis.ch
**************************************************************************
*
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. *
*
**************************************************************************
%End
%TypeHeaderCode
#include "qgsattributeformrelationeditorwidget.h"
%End
public:
explicit QgsAttributeFormRelationEditorWidget( QgsRelationWidgetWrapper *wrapper, QgsAttributeForm *form );
virtual void createSearchWidgetWrappers();
virtual QString currentFilterExpression() const;
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsattributeformrelationeditorwidget.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -22,6 +22,7 @@ class QgsAttributeFormWidget : QWidget /Abstract/
DefaultMode,
MultiEditMode,
SearchMode,
AggregateSearchMode,
};
explicit QgsAttributeFormWidget( QgsWidgetWrapper *widget, QgsAttributeForm *form );
@ -34,7 +35,7 @@ class QgsAttributeFormWidget : QWidget /Abstract/
\param context editor context (not available in Python bindings)
%End
virtual QString currentFilterExpression() const = 0;
virtual QString currentFilterExpression() const;
%Docstring
Creates an expression matching the current search filter value and
search properties represented in the widget.
@ -68,6 +69,55 @@ class QgsAttributeFormWidget : QWidget /Abstract/
:rtype: QgsAttributeForm
%End
void setSearchWidgetWrapper( QgsSearchWidgetWrapper *wrapper );
%Docstring
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
.. note::
this method is in place for unit testing only, and is not considered
stable API
%End
void addAdditionalSearchWidgetWrapper( QgsSearchWidgetWrapper *wrapper );
QList< QgsSearchWidgetWrapper * > searchWidgetWrappers();
%Docstring
Returns the search widget wrapper used in this widget. The wrapper must
first be created using createSearchWidgetWrapper()
.. note::
this method is in place for unit testing only, and is not considered
stable API
:rtype: list of QgsSearchWidgetWrapper
%End
void resetSearch();
%Docstring
Resets the search/filter value of the widget.
%End
protected:
QgsSearchWidgetToolButton *searchWidgetToolButton();
%Docstring
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
:rtype: QgsSearchWidgetToolButton
%End
};
/************************************************************************

View File

@ -174,6 +174,232 @@ QgsAggregateCalculator::Aggregate QgsAggregateCalculator::stringToAggregate( con
return Count;
}
QList<QgsAggregateCalculator::AggregateInfo> QgsAggregateCalculator::aggregates()
{
QList< AggregateInfo > aggregates;
aggregates
<< AggregateInfo
{
QStringLiteral( "count" ),
QCoreApplication::tr( "Count" ),
QSet<QVariant::Type>()
<< QVariant::DateTime
<< QVariant::Date
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::String
}
<< AggregateInfo
{
QStringLiteral( "count_distinct" ),
QCoreApplication::tr( "Count Distinct" ),
QSet<QVariant::Type>()
<< QVariant::DateTime
<< QVariant::Date
<< QVariant::UInt
<< QVariant::Int
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::String
}
<< AggregateInfo
{
QStringLiteral( "count_missing" ),
QCoreApplication::tr( "Count Missing" ),
QSet<QVariant::Type>()
<< QVariant::DateTime
<< QVariant::Date
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::String
}
<< AggregateInfo
{
QStringLiteral( "min" ),
QCoreApplication::tr( "Min" ),
QSet<QVariant::Type>()
<< QVariant::DateTime
<< QVariant::Date
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::String
}
<< AggregateInfo
{
QStringLiteral( "max" ),
QCoreApplication::tr( "Max" ),
QSet<QVariant::Type>()
<< QVariant::DateTime
<< QVariant::Date
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::String
}
<< AggregateInfo
{
QStringLiteral( "sum" ),
QCoreApplication::tr( "Sum" ),
QSet<QVariant::Type>()
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::Double
}
<< AggregateInfo
{
QStringLiteral( "mean" ),
QCoreApplication::tr( "Mean" ),
QSet<QVariant::Type>()
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::Double
}
<< AggregateInfo
{
QStringLiteral( "median" ),
QCoreApplication::tr( "Median" ),
QSet<QVariant::Type>()
<< QVariant::Int
<< QVariant::UInt
<< QVariant::Double
}
<< AggregateInfo
{
QStringLiteral( "stdev" ),
QCoreApplication::tr( "Stdev" ),
QSet<QVariant::Type>()
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::Double
}
<< AggregateInfo
{
QStringLiteral( "stdevsample" ),
QCoreApplication::tr( "Stdev Sample" ),
QSet<QVariant::Type>()
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::Double
}
<< AggregateInfo
{
QStringLiteral( "range" ),
QCoreApplication::tr( "Range" ),
QSet<QVariant::Type>()
<< QVariant::Date
<< QVariant::DateTime
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::Double
}
<< AggregateInfo
{
QStringLiteral( "minority" ),
QCoreApplication::tr( "Minority" ),
QSet<QVariant::Type>()
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::Double
}
<< AggregateInfo
{
QStringLiteral( "majority" ),
QCoreApplication::tr( "Majority" ),
QSet<QVariant::Type>()
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::Double
}
<< AggregateInfo
{
QStringLiteral( "q1" ),
QCoreApplication::tr( "Q1" ),
QSet<QVariant::Type>()
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::Double
}
<< AggregateInfo
{
QStringLiteral( "q3" ),
QCoreApplication::tr( "Q3" ),
QSet<QVariant::Type>()
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::Double
}
<< AggregateInfo
{
QStringLiteral( "iqr" ),
QCoreApplication::tr( "InterQuartileRange" ),
QSet<QVariant::Type>()
<< QVariant::Int
<< QVariant::UInt
<< QVariant::LongLong
<< QVariant::ULongLong
<< QVariant::Double
}
<< AggregateInfo
{
QStringLiteral( "min_length" ),
QCoreApplication::tr( "Min Length" ),
QSet<QVariant::Type>()
<< QVariant::String
}
<< AggregateInfo
{
QStringLiteral( "max_length" ),
QCoreApplication::tr( "Max Length" ),
QSet<QVariant::Type>()
<< QVariant::String
}
<< AggregateInfo
{
QStringLiteral( "concatenate" ),
QCoreApplication::tr( "Concatenate" ),
QSet<QVariant::Type>()
<< QVariant::String
}
<< AggregateInfo
{
QStringLiteral( "collect" ),
QCoreApplication::tr( "Collect" ),
QSet<QVariant::Type>()
}
<< AggregateInfo
{
QStringLiteral( "array_agg" ),
QCoreApplication::tr( "Array Aggregate" ),
QSet<QVariant::Type>()
};
return aggregates;
}
QVariant QgsAggregateCalculator::calculate( QgsAggregateCalculator::Aggregate aggregate, QgsFeatureIterator &fit, QVariant::Type resultType,
int attr, QgsExpression *expression, const QString &delimiter, QgsExpressionContext *context, bool *ok )
{

View File

@ -43,6 +43,13 @@ class CORE_EXPORT QgsAggregateCalculator
{
public:
struct AggregateInfo
{
QString function;
QString name;
QSet<QVariant::Type> supportedTypes;
};
/**
* Available aggregates to calculate. Not all aggregates are available for all field
* types.
@ -155,6 +162,8 @@ class CORE_EXPORT QgsAggregateCalculator
*/
static Aggregate stringToAggregate( const QString &string, bool *ok = nullptr );
static QList< QgsAggregateCalculator::AggregateInfo > aggregates();
private:
//! Source layer

View File

@ -125,6 +125,7 @@ SET(QGIS_GUI_SRCS
editorwidgets/qgsrangeconfigdlg.cpp
editorwidgets/qgsrangewidgetwrapper.cpp
editorwidgets/qgsrangewidgetfactory.cpp
editorwidgets/qgsrelationaggregatesearchwidgetwrapper.cpp
editorwidgets/qgssearchwidgettoolbutton.cpp
editorwidgets/qgsspinbox.cpp
editorwidgets/qgsrelationwidgetwrapper.cpp
@ -191,6 +192,7 @@ SET(QGIS_GUI_SRCS
qgsactionmenu.cpp
qgsadvanceddigitizingcanvasitem.cpp
qgsadvanceddigitizingdockwidget.cpp
qgsaggregatetoolbutton.cpp
qgsattributedialog.cpp
qgsattributeform.cpp
qgsattributeformeditorwidget.cpp
@ -198,6 +200,7 @@ SET(QGIS_GUI_SRCS
qgsattributeformlegacyinterface.cpp
qgsattributetypeloaddialog.cpp
qgsattributeformwidget.cpp
qgsattributeformrelationeditorwidget.cpp
qgsblendmodecombobox.cpp
qgsbrowsertreeview.cpp
qgsbrowserdockwidget.cpp
@ -368,9 +371,11 @@ SET(QGIS_GUI_MOC_HDRS
qgisinterface.h
qgsactionmenu.h
qgsadvanceddigitizingdockwidget.h
qgsaggregatetoolbutton.h
qgsattributedialog.h
qgsattributeform.h
qgsattributeformeditorwidget.h
qgsattributeformrelationeditorwidget.h
qgsattributeformwidget.h
qgsattributetypeloaddialog.h
qgsblendmodecombobox.h
@ -647,6 +652,7 @@ SET(QGIS_GUI_MOC_HDRS
editorwidgets/qgsrelationreferencewidget.h
editorwidgets/qgsrelationreferencewidgetwrapper.h
editorwidgets/qgsrelationwidgetwrapper.h
editorwidgets/qgsrelationaggregatesearchwidgetwrapper.h
editorwidgets/qgssearchwidgettoolbutton.h
editorwidgets/qgsspinbox.h
editorwidgets/qgstexteditconfigdlg.h

View File

@ -100,6 +100,15 @@ QgsSearchWidgetWrapper::FilterFlags QgsSearchWidgetWrapper::defaultFlags() const
return FilterFlags();
}
QString QgsSearchWidgetWrapper::createFieldIdentifier() const
{
QString field = QgsExpression::quotedColumnRef( layer()->fields().at( mFieldIdx ).name() );
if ( mAggregate.isEmpty() )
return field;
else
return QStringLiteral( "relation_aggregate('%1','%2',%3)" ).arg( context().relation().id(), mAggregate, field );
}
void QgsSearchWidgetWrapper::setFeature( const QgsFeature &feature )
{
Q_UNUSED( feature )
@ -110,3 +119,23 @@ void QgsSearchWidgetWrapper::clearExpression()
mExpression = QStringLiteral( "TRUE" );
}
QgsRelation QgsSearchWidgetWrapper::aggregateRelation() const
{
return mAggregateRelation;
}
void QgsSearchWidgetWrapper::setAggregateRelation( const QgsRelation &aggregateRelation )
{
mAggregateRelation = aggregateRelation;
}
QString QgsSearchWidgetWrapper::aggregate() const
{
return mAggregate;
}
void QgsSearchWidgetWrapper::setAggregate( const QString &aggregate )
{
mAggregate = aggregate;
}

View File

@ -188,6 +188,16 @@ class GUI_EXPORT QgsSearchWidgetWrapper : public QgsWidgetWrapper
// TODO QGIS 3.0 - make pure virtual
virtual QString createExpression( FilterFlags flags ) const { Q_UNUSED( flags ); return QStringLiteral( "TRUE" ); }
QString createFieldIdentifier() const;
QString aggregate() const;
void setAggregate( const QString &aggregate );
QgsRelation aggregateRelation() const;
void setAggregateRelation( const QgsRelation &aggregateRelation );
public slots:
/**
@ -239,6 +249,9 @@ class GUI_EXPORT QgsSearchWidgetWrapper : public QgsWidgetWrapper
QString mExpression;
int mFieldIdx;
private:
QString mAggregate;
QgsRelation mAggregateRelation;
};
// We'll use this class inside a QVariant in the widgets properties
Q_DECLARE_METATYPE( QgsSearchWidgetWrapper * )

View File

@ -61,7 +61,7 @@ QgsSearchWidgetWrapper::FilterFlags QgsCheckboxSearchWidgetWrapper::defaultFlags
QString QgsCheckboxSearchWidgetWrapper::createExpression( QgsSearchWidgetWrapper::FilterFlags flags ) const
{
QVariant::Type fldType = layer()->fields().at( mFieldIdx ).type();
QString fieldName = QgsExpression::quotedColumnRef( layer()->fields().at( mFieldIdx ).name() );
QString fieldName = createFieldIdentifier();
//clear any unsupported flags
flags &= supportedFlags();

View File

@ -62,7 +62,7 @@ QgsSearchWidgetWrapper::FilterFlags QgsDateTimeSearchWidgetWrapper::defaultFlags
QString QgsDateTimeSearchWidgetWrapper::createExpression( QgsSearchWidgetWrapper::FilterFlags flags ) const
{
QString fieldName = QgsExpression::quotedColumnRef( layer()->fields().at( mFieldIdx ).name() );
QString fieldName = createFieldIdentifier();
//clear any unsupported flags
flags &= supportedFlags();

View File

@ -148,7 +148,7 @@ QString QgsDefaultSearchWidgetWrapper::createExpression( QgsSearchWidgetWrapper:
flags &= supportedFlags();
QVariant::Type fldType = layer()->fields().at( mFieldIdx ).type();
QString fieldName = QgsExpression::quotedColumnRef( layer()->fields().at( mFieldIdx ).name() );
QString fieldName = createFieldIdentifier();
if ( flags & IsNull )
return fieldName + " IS NULL";

View File

@ -0,0 +1,61 @@
/***************************************************************************
qgsrelationaggregatesearchwidget.cpp
-----------------------------
Date : Nov 2017
Copyright : (C) 2017 Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* 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 "qgsrelationaggregatesearchwidgetwrapper.h"
#include "qgsattributeform.h"
#include "qgsrelationwidgetwrapper.h"
#include <QLabel>
QgsRelationAggregateSearchWidgetWrapper::QgsRelationAggregateSearchWidgetWrapper( QgsVectorLayer *vl, QgsRelationWidgetWrapper *wrapper, QWidget *parent )
: QgsSearchWidgetWrapper( vl, -1, parent )
, mWrapper( wrapper )
{
setContext( mWrapper->context() );
}
QString QgsRelationAggregateSearchWidgetWrapper::expression() const
{
QString aggregateFilter = mAttributeForm->aggregateFilter();
if ( aggregateFilter.isEmpty() )
return QStringLiteral( "TRUE" );
else
return aggregateFilter;
}
bool QgsRelationAggregateSearchWidgetWrapper::valid() const
{
return true;
}
QWidget *QgsRelationAggregateSearchWidgetWrapper::createWidget( QWidget *parent )
{
QgsAttributeEditorContext subContext = QgsAttributeEditorContext( context(), mWrapper->relation(), QgsAttributeEditorContext::Multiple, QgsAttributeEditorContext::Embed );
mAttributeForm = new QgsAttributeForm( mWrapper->relation().referencingLayer(), QgsFeature(), subContext, parent );
mAttributeForm->setMode( QgsAttributeForm::AggregateSearchMode );
return mAttributeForm;
}
bool QgsRelationAggregateSearchWidgetWrapper::applyDirectly()
{
return true;
}
void QgsRelationAggregateSearchWidgetWrapper::setExpression( const QString &value )
{
}

View File

@ -0,0 +1,47 @@
/***************************************************************************
qgsrelationaggregatesearchwidget.h
-----------------------------
Date : Nov 2017
Copyright : (C) 2017 Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* 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 QGSRELATIONAGGREGATESEARCHWIDGETWRAPPER_H
#define QGSRELATIONAGGREGATESEARCHWIDGETWRAPPER_H
#include "qgis_gui.h"
#include "qgssearchwidgetwrapper.h"
#include "qgsattributeform.h"
class QgsRelationWidgetWrapper;
class GUI_EXPORT QgsRelationAggregateSearchWidgetWrapper : public QgsSearchWidgetWrapper
{
Q_OBJECT
public:
explicit QgsRelationAggregateSearchWidgetWrapper( QgsVectorLayer *vl, QgsRelationWidgetWrapper *wrapper, QWidget *parent SIP_TRANSFERTHIS = 0 );
virtual QString expression() const override;
virtual bool valid() const override;
virtual QWidget *createWidget( QWidget *parent ) override;
virtual bool applyDirectly() override;
virtual void setExpression( const QString &value ) override;
private:
QgsRelationWidgetWrapper *mWrapper;
QgsAttributeForm *mAttributeForm;
};
#endif // QGSRELATIONAGGREGATESEARCHWIDGETWRAPPER_H

View File

@ -63,7 +63,7 @@ QgsSearchWidgetWrapper::FilterFlags QgsRelationReferenceSearchWidgetWrapper::def
QString QgsRelationReferenceSearchWidgetWrapper::createExpression( QgsSearchWidgetWrapper::FilterFlags flags ) const
{
QString fieldName = QgsExpression::quotedColumnRef( layer()->fields().at( mFieldIdx ).name() );
QString fieldName = createFieldIdentifier();
//clear any unsupported flags
flags &= supportedFlags();

View File

@ -46,6 +46,11 @@ void QgsRelationWidgetWrapper::setVisible( bool visible )
mWidget->setVisible( visible );
}
QgsRelation QgsRelationWidgetWrapper::relation() const
{
return mRelation;
}
bool QgsRelationWidgetWrapper::showUnlinkButton() const
{
return mWidget->showUnlinkButton();

View File

@ -79,6 +79,8 @@ class GUI_EXPORT QgsRelationWidgetWrapper : public QgsWidgetWrapper
*/
void setShowUnlinkButton( bool showUnlinkButton );
QgsRelation relation() const;
protected:
QWidget *createWidget( QWidget *parent ) override;
void initWidget( QWidget *editor ) override;

View File

@ -88,7 +88,7 @@ QString QgsValueMapSearchWidgetWrapper::createExpression( QgsSearchWidgetWrapper
flags &= supportedFlags();
QVariant::Type fldType = layer()->fields().at( mFieldIdx ).type();
QString fieldName = QgsExpression::quotedColumnRef( layer()->fields().at( mFieldIdx ).name() );
QString fieldName = createFieldIdentifier();
if ( flags & IsNull )
return fieldName + " IS NULL";

View File

@ -94,7 +94,7 @@ QgsSearchWidgetWrapper::FilterFlags QgsValueRelationSearchWidgetWrapper::default
QString QgsValueRelationSearchWidgetWrapper::createExpression( QgsSearchWidgetWrapper::FilterFlags flags ) const
{
QString fieldName = QgsExpression::quotedColumnRef( layer()->fields().at( mFieldIdx ).name() );
QString fieldName = createFieldIdentifier();
//clear any unsupported flags
flags &= supportedFlags();

View File

@ -0,0 +1,115 @@
/***************************************************************************
qgsaggregatetoolbutton.cpp
--------------------------------------
Date : Nov 2017
Copyright : (C) 2017 Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* 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 "qgsaggregatetoolbutton.h"
#include "qgsaggregatecalculator.h"
#include <QMenu>
QgsAggregateToolButton::QgsAggregateToolButton()
{
setFocusPolicy( Qt::StrongFocus );
setPopupMode( QToolButton::InstantPopup );
mMenu = new QMenu( this );
connect( mMenu, &QMenu::aboutToShow, this, &QgsAggregateToolButton::aboutToShowMenu );
setMenu( mMenu );
setText( tr( "Exclude" ) );
}
void QgsAggregateToolButton::setType( QVariant::Type type )
{
if ( mType == type )
return;
mType = type;
updateAvailableAggregates();
}
void QgsAggregateToolButton::aboutToShowMenu()
{
mMenu->clear();
for ( const auto &aggregate : qgis::as_const( mAvailableAggregates ) )
{
mMenu->addAction( aggregate.name, this, [ this, aggregate ]
{
setText( aggregate.name );
setAggregate( aggregate.function );
} );
}
}
void QgsAggregateToolButton::updateAvailableAggregates()
{
QList<QgsAggregateCalculator::AggregateInfo> aggregates = QgsAggregateCalculator::aggregates();
for ( const auto &aggregate : aggregates )
{
if ( aggregate.supportedTypes.contains( mType ) )
{
mAvailableAggregates.append( aggregate );
}
}
}
void QgsAggregateToolButton::setActive( bool active )
{
if ( active == mActive )
return;
mActive = active;
if ( !active )
setText( tr( "Exclude" ) );
emit activeChanged();
}
QString QgsAggregateToolButton::aggregate() const
{
return mAggregate;
}
void QgsAggregateToolButton::setAggregate( const QString &aggregate )
{
if ( aggregate == mAggregate )
return;
mAggregate = QString();
for ( const auto &agg : qgis::as_const( mAvailableAggregates ) )
{
if ( agg.function == aggregate )
{
mAggregate = aggregate;
break;
}
}
setActive( !mAggregate.isEmpty() );
emit aggregateChanged();
}
bool QgsAggregateToolButton::active() const
{
return mActive;
}
QVariant::Type QgsAggregateToolButton::type() const
{
return mType;
}

View File

@ -0,0 +1,58 @@
/***************************************************************************
qgsaggregatetoolbutton.h
--------------------------------------
Date : Nov 2017
Copyright : (C) 2017 Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* 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 QGSAGGREGATETOOLBUTTON_H
#define QGSAGGREGATETOOLBUTTON_H
#include <QToolButton>
#include <QVariant>
#include "qgsaggregatecalculator.h"
#include "qgis_gui.h"
class GUI_EXPORT QgsAggregateToolButton : public QToolButton
{
Q_OBJECT
public:
QgsAggregateToolButton();
void setType( QVariant::Type type );
QVariant::Type type() const;
void setActive( bool active );
bool active() const;
QString aggregate() const;
void setAggregate( const QString &aggregate );
signals:
void aggregateChanged();
void activeChanged();
private slots:
void aboutToShowMenu();
private:
void updateAvailableAggregates();
QMenu *mMenu = nullptr;
QVariant::Type mType;
bool mActive = false;
QString mAggregate;
QList<QgsAggregateCalculator::AggregateInfo> mAvailableAggregates;
};
#endif // QGSAGGREGATETOOLBUTTON_H

View File

@ -17,6 +17,7 @@
#include "qgsattributeforminterface.h"
#include "qgsattributeformlegacyinterface.h"
#include "qgsattributeformrelationeditorwidget.h"
#include "qgseditorwidgetregistry.h"
#include "qgsfeatureiterator.h"
#include "qgsproject.h"
@ -151,7 +152,7 @@ void QgsAttributeForm::setMode( QgsAttributeForm::Mode mode )
}
//update all form editor widget modes to match
for ( QgsAttributeFormWidget *w : findChildren< QgsAttributeFormWidget * >() )
for ( QgsAttributeFormWidget *w : qgis::as_const( mFormWidgets ) )
{
switch ( mode )
{
@ -170,11 +171,15 @@ void QgsAttributeForm::setMode( QgsAttributeForm::Mode mode )
case QgsAttributeForm::SearchMode:
w->setMode( QgsAttributeFormWidget::SearchMode );
break;
case QgsAttributeForm::AggregateSearchMode:
w->setMode( QgsAttributeFormWidget::AggregateSearchMode );
break;
}
}
bool relationWidgetsVisible = ( mMode == QgsAttributeForm::SingleEditMode || mMode == QgsAttributeForm::AddFeatureMode );
Q_FOREACH ( QgsRelationWidgetWrapper *w, findChildren< QgsRelationWidgetWrapper * >() )
bool relationWidgetsVisible = ( mMode != QgsAttributeForm::MultiEditMode && mMode != QgsAttributeForm::AggregateSearchMode );
for ( QgsAttributeFormRelationEditorWidget *w : findChildren< QgsAttributeFormRelationEditorWidget * >() )
{
w->setVisible( relationWidgetsVisible );
}
@ -201,6 +206,11 @@ void QgsAttributeForm::setMode( QgsAttributeForm::Mode mode )
mSearchButtonBox->setVisible( true );
hideButtonBox();
break;
case QgsAttributeForm::AggregateSearchMode:
mSearchButtonBox->setVisible( false );
hideButtonBox();
break;
}
emit modeChanged( mMode );
@ -240,6 +250,7 @@ void QgsAttributeForm::setFeature( const QgsFeature &feature )
}
case MultiEditMode:
case SearchMode:
case AggregateSearchMode:
{
//ignore setFeature
break;
@ -572,6 +583,7 @@ bool QgsAttributeForm::save()
case SingleEditMode:
case AddFeatureMode:
case SearchMode:
case AggregateSearchMode:
success = saveEdits();
break;
@ -621,7 +633,7 @@ void QgsAttributeForm::clearMultiEditMessages()
QString QgsAttributeForm::createFilterExpression() const
{
QStringList filters;
Q_FOREACH ( QgsAttributeFormEditorWidget *w, findChildren< QgsAttributeFormEditorWidget * >() )
for ( QgsAttributeFormWidget *w : qgis::as_const( mFormWidgets ) )
{
QString filter = w->currentFilterExpression();
if ( !filter.isEmpty() )
@ -676,6 +688,7 @@ void QgsAttributeForm::onAttributeChanged( const QVariant &value )
break;
}
case SearchMode:
case AggregateSearchMode:
//nothing to do
break;
}
@ -1224,6 +1237,7 @@ void QgsAttributeForm::init()
QgsAttributeFormEditorWidget *formWidget = new QgsAttributeFormEditorWidget( eww, widgetSetup.type(), this );
w = formWidget;
mFormEditorWidgets.insert( idx, formWidget );
mFormWidgets.append( formWidget );
formWidget->createSearchWidgetWrappers( mContext );
l->setBuddy( eww->widget() );
@ -1264,7 +1278,12 @@ void QgsAttributeForm::init()
rww->setConfig( setup.config() );
rww->setContext( mContext );
gridLayout->addWidget( rww->widget(), row++, 0, 1, 2 );
QgsAttributeFormRelationEditorWidget *formWidget = new QgsAttributeFormRelationEditorWidget( rww, this );
formWidget->createSearchWidgetWrappers( mContext );
mWidgets.append( rww );
mFormWidgets.append( formWidget );
}
if ( QgsProject::instance()->relationManager()->referencedRelations( mLayer ).isEmpty() )
@ -1523,12 +1542,13 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt
const QgsEditorWidgetSetup widgetSetup = QgsGui::editorWidgetRegistry()->findBest( mLayer, fieldDef->name() );
QgsEditorWidgetWrapper *eww = QgsGui::editorWidgetRegistry()->create( widgetSetup.type(), mLayer, fldIdx, widgetSetup.config(), nullptr, this, mContext );
QgsAttributeFormEditorWidget *w = new QgsAttributeFormEditorWidget( eww, widgetSetup.type(), this );
mFormEditorWidgets.insert( fldIdx, w );
QgsAttributeFormEditorWidget *formWidget = new QgsAttributeFormEditorWidget( eww, widgetSetup.type(), this );
mFormEditorWidgets.insert( fldIdx, formWidget );
mFormWidgets.append( formWidget );
w->createSearchWidgetWrappers( mContext );
formWidget->createSearchWidgetWrappers( mContext );
newWidgetInfo.widget = w;
newWidgetInfo.widget = formWidget;
addWidgetWrapper( eww );
newWidgetInfo.widget->setObjectName( fields.at( fldIdx ).name() );
@ -1549,11 +1569,17 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt
QgsRelationWidgetWrapper *rww = new QgsRelationWidgetWrapper( mLayer, relDef->relation(), nullptr, this );
rww->setConfig( mLayer->editFormConfig().widgetConfig( relDef->relation().id() ) );
rww->setContext( context );
newWidgetInfo.widget = rww->widget();
rww->setShowLabel( relDef->showLabel() );
rww->setShowLinkButton( relDef->showLinkButton() );
rww->setShowUnlinkButton( relDef->showUnlinkButton() );
QgsAttributeFormRelationEditorWidget *formWidget = new QgsAttributeFormRelationEditorWidget( rww, this );
formWidget->createSearchWidgetWrappers( mContext );
mWidgets.append( rww );
mFormWidgets.append( formWidget );
newWidgetInfo.widget = formWidget;
newWidgetInfo.labelText = QString();
newWidgetInfo.labelOnTop = true;
break;
@ -1818,6 +1844,7 @@ void QgsAttributeForm::layerSelectionChanged()
case SingleEditMode:
case AddFeatureMode:
case SearchMode:
case AggregateSearchMode:
break;
case MultiEditMode:
@ -1881,6 +1908,24 @@ void QgsAttributeForm::setMessageBar( QgsMessageBar *messageBar )
mMessageBar = messageBar;
}
QString QgsAttributeForm::aggregateFilter() const
{
if ( mMode != AggregateSearchMode )
{
Q_ASSERT( false );
}
QStringList filters;
for ( QgsAttributeFormWidget *widget : mFormWidgets )
{
QString filter = widget->currentFilterExpression();
if ( !filter.isNull() )
filters << '(' + filter + ')';
}
return filters.join( QStringLiteral( " AND " ) );
}
int QgsAttributeForm::messageTimeout()
{
QgsSettings settings;

View File

@ -54,6 +54,7 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
will add a new feature when the form is accepted. */
MultiEditMode, //!< Multi edit mode, for editing fields of multiple features at once
SearchMode, //!< Form values are used for searching/filtering the layer
AggregateSearchMode,
};
//! Filter types
@ -160,6 +161,8 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
*/
void setMessageBar( QgsMessageBar *messageBar );
QString aggregateFilter() const;
signals:
/**
@ -351,6 +354,7 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
QWidget *mSearchButtonBox = nullptr;
QList<QgsAttributeFormInterface *> mInterfaces;
QMap< int, QgsAttributeFormEditorWidget * > mFormEditorWidgets;
QList< QgsAttributeFormWidget *> mFormWidgets;
QgsExpressionContext mExpressionContext;
QMap<const QgsVectorLayerJoinInfo *, QgsFeature> mJoinedFeatures;

View File

@ -21,7 +21,9 @@
#include "qgssearchwidgetwrapper.h"
#include "qgsattributeeditorcontext.h"
#include "qgseditorwidgetregistry.h"
#include "qgsaggregatetoolbutton.h"
#include "qgsgui.h"
#include <QLayout>
#include <QLabel>
#include <QStackedWidget>
@ -40,50 +42,18 @@ QgsAttributeFormEditorWidget::QgsAttributeFormEditorWidget( QgsEditorWidgetWrapp
mConstraintResultLabel->setObjectName( QStringLiteral( "ConstraintStatus" ) );
mConstraintResultLabel->setSizePolicy( QSizePolicy::Fixed, mConstraintResultLabel->sizePolicy().verticalPolicy() );
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();
connect( mSearchWidgetToolButton, &QgsSearchWidgetToolButton::activeFlagsChanged,
this, &QgsAttributeFormEditorWidget::searchWidgetFlagsChanged );
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;
mEditPage->layout()->addWidget( mWidget->widget() );
mMultiEditButton->setField( mWidget->field() );
mAggregateButton = new QgsAggregateToolButton();
mAggregateButton->setType( editorWidget->field().type() );
connect( mAggregateButton, &QgsAggregateToolButton::aggregateChanged, this, &QgsAttributeFormEditorWidget::onAggregateChanged );
if ( mWidget->widget() )
{
mWidget->widget()->setObjectName( mWidget->field().name() );
}
connect( mWidget, static_cast<void ( QgsEditorWidgetWrapper::* )( const QVariant &value )>( &QgsEditorWidgetWrapper::valueChanged ), this, &QgsAttributeFormEditorWidget::editorWidgetChanged );
connect( mMultiEditButton, &QgsMultiEditToolButton::resetFieldValueTriggered, this, &QgsAttributeFormEditorWidget::resetValue );
connect( mMultiEditButton, &QgsMultiEditToolButton::setFieldValueTriggered, this, &QgsAttributeFormEditorWidget::setFieldTriggered );
@ -105,42 +75,19 @@ void QgsAttributeFormEditorWidget::createSearchWidgetWrappers( const QgsAttribut
const int fieldIdx = mWidget->fieldIdx();
QgsSearchWidgetWrapper *sww = QgsGui::editorWidgetRegistry()->createSearchWidget( mWidgetType, layer(), fieldIdx, config,
mSearchFrame, context );
searchWidgetFrame(), context );
setSearchWidgetWrapper( sww );
searchWidgetFrame()->layout()->addWidget( mAggregateButton );
if ( sww->supportedFlags() & QgsSearchWidgetWrapper::Between ||
sww->supportedFlags() & QgsSearchWidgetWrapper::IsNotBetween )
{
// create secondary widget for between type searches
QgsSearchWidgetWrapper *sww2 = QgsGui::editorWidgetRegistry()->createSearchWidget( mWidgetType, layer(), fieldIdx, config,
mSearchFrame, context );
mSearchWidgets << sww2;
mSearchFrame->layout()->addWidget( sww2->widget() );
sww2->widget()->hide();
searchWidgetFrame(), context );
addAdditionalSearchWidgetWrapper( sww2 );
}
}
void QgsAttributeFormEditorWidget::setSearchWidgetWrapper( QgsSearchWidgetWrapper *wrapper )
{
mSearchWidgets.clear();
mSearchWidgets << wrapper;
mSearchFrame->layout()->addWidget( wrapper->widget() );
mSearchWidgetToolButton->setAvailableFlags( wrapper->supportedFlags() );
mSearchWidgetToolButton->setActiveFlags( QgsSearchWidgetWrapper::FilterFlags() );
mSearchWidgetToolButton->setDefaultFlags( wrapper->defaultFlags() );
connect( wrapper, &QgsSearchWidgetWrapper::valueChanged, mSearchWidgetToolButton, &QgsSearchWidgetToolButton::setActive );
connect( wrapper, &QgsSearchWidgetWrapper::valueCleared, mSearchWidgetToolButton, &QgsSearchWidgetToolButton::setInactive );
}
QWidget *QgsAttributeFormEditorWidget::searchWidgetFrame()
{
return mSearchFrame;
}
QList< QgsSearchWidgetWrapper * > QgsAttributeFormEditorWidget::searchWidgetWrappers()
{
return mSearchWidgets;
}
void QgsAttributeFormEditorWidget::setConstraintStatus( const QString &constraint, const QString &description, const QString &err, QgsEditorWidgetWrapper::ConstraintResult result )
{
switch ( result )
@ -185,14 +132,7 @@ void QgsAttributeFormEditorWidget::changesCommitted()
mIsChanged = false;
}
void QgsAttributeFormEditorWidget::resetSearch()
{
mSearchWidgetToolButton->setInactive();
Q_FOREACH ( QgsSearchWidgetWrapper *widget, mSearchWidgets )
{
widget->clearWidget();
}
}
void QgsAttributeFormEditorWidget::initialize( const QVariant &initialValue, bool mixedValues )
{
@ -213,31 +153,7 @@ QVariant QgsAttributeFormEditorWidget::currentValue() const
return mWidget->value();
}
QString QgsAttributeFormEditorWidget::currentFilterExpression() const
{
if ( mSearchWidgets.isEmpty() )
return QString();
if ( !mSearchWidgetToolButton->isActive() )
return QString();
if ( mSearchWidgetToolButton->activeFlags() & QgsSearchWidgetWrapper::Between )
{
// special case: Between search
QString filter1 = mSearchWidgets.at( 0 )->createExpression( QgsSearchWidgetWrapper::GreaterThanOrEqualTo );
QString filter2 = mSearchWidgets.at( 1 )->createExpression( QgsSearchWidgetWrapper::LessThanOrEqualTo );
return QStringLiteral( "%1 AND %2" ).arg( filter1, filter2 );
}
else if ( mSearchWidgetToolButton->activeFlags() & QgsSearchWidgetWrapper::IsNotBetween )
{
// special case: Is Not Between search
QString filter1 = mSearchWidgets.at( 0 )->createExpression( QgsSearchWidgetWrapper::LessThan );
QString filter2 = mSearchWidgets.at( 1 )->createExpression( QgsSearchWidgetWrapper::GreaterThan );
return QStringLiteral( "%1 OR %2" ).arg( filter1, filter2 );
}
return mSearchWidgets.at( 0 )->createExpression( mSearchWidgetToolButton->activeFlags() );
}
void QgsAttributeFormEditorWidget::editorWidgetChanged( const QVariant &value )
{
@ -250,6 +166,7 @@ void QgsAttributeFormEditorWidget::editorWidgetChanged( const QVariant &value )
{
case DefaultMode:
case SearchMode:
case AggregateSearchMode:
break;
case MultiEditMode:
mMultiEditButton->setIsChanged( true );
@ -270,6 +187,7 @@ void QgsAttributeFormEditorWidget::resetValue()
{
case DefaultMode:
case SearchMode:
case AggregateSearchMode:
break;
case MultiEditMode:
{
@ -286,41 +204,23 @@ void QgsAttributeFormEditorWidget::setFieldTriggered()
mIsChanged = true;
}
void QgsAttributeFormEditorWidget::searchWidgetFlagsChanged( QgsSearchWidgetWrapper::FilterFlags flags )
void QgsAttributeFormEditorWidget::onAggregateChanged()
{
Q_FOREACH ( QgsSearchWidgetWrapper *widget, mSearchWidgets )
{
widget->setEnabled( !( flags & QgsSearchWidgetWrapper::IsNull )
&& !( flags & QgsSearchWidgetWrapper::IsNotNull ) );
if ( !mSearchWidgetToolButton->isActive() )
{
widget->clearWidget();
}
}
if ( mSearchWidgets.count() >= 2 )
{
mSearchWidgets.at( 1 )->widget()->setVisible( flags & QgsSearchWidgetWrapper::Between ||
flags & QgsSearchWidgetWrapper::IsNotBetween );
}
}
QgsSearchWidgetToolButton *QgsAttributeFormEditorWidget::searchWidgetToolButton()
{
return mSearchWidgetToolButton;
for ( QgsSearchWidgetWrapper *searchWidget : searchWidgetWrappers() )
searchWidget->setAggregate( mAggregateButton->aggregate() );
}
void QgsAttributeFormEditorWidget::updateWidgets()
{
//first update the tool buttons
bool hasMultiEditButton = ( mEditPage->layout()->indexOf( mMultiEditButton ) >= 0 );
bool hasMultiEditButton = ( editPage()->layout()->indexOf( mMultiEditButton ) >= 0 );
bool fieldReadOnly = layer()->editFormConfig().readOnly( mWidget->fieldIdx() );
if ( hasMultiEditButton )
{
if ( mode() != MultiEditMode || fieldReadOnly )
{
mEditPage->layout()->removeWidget( mMultiEditButton );
editPage()->layout()->removeWidget( mMultiEditButton );
mMultiEditButton->setParent( nullptr );
}
}
@ -328,7 +228,7 @@ void QgsAttributeFormEditorWidget::updateWidgets()
{
if ( mode() == MultiEditMode && !fieldReadOnly )
{
mEditPage->layout()->addWidget( mMultiEditButton );
editPage()->layout()->addWidget( mMultiEditButton );
}
}
@ -337,16 +237,24 @@ void QgsAttributeFormEditorWidget::updateWidgets()
case DefaultMode:
case MultiEditMode:
{
mStack->setCurrentWidget( mEditPage );
stack()->setCurrentWidget( editPage() );
mEditPage->layout()->addWidget( mConstraintResultLabel );
editPage()->layout()->addWidget( mConstraintResultLabel );
break;
}
case AggregateSearchMode:
{
mAggregateButton->setVisible( true );
stack()->setCurrentWidget( searchPage() );
break;
}
case SearchMode:
{
mStack->setCurrentWidget( mSearchPage );
mAggregateButton->setVisible( true );
stack()->setCurrentWidget( searchPage() );
break;
}
}

View File

@ -28,6 +28,7 @@ class QgsVectorLayer;
class QStackedWidget;
class QgsAttributeEditorContext;
class QLabel;
class QgsAggregateToolButton;
/**
* \ingroup gui
@ -74,13 +75,6 @@ class GUI_EXPORT QgsAttributeFormEditorWidget : public QgsAttributeFormWidget
*/
QVariant currentValue() const;
/**
* Creates an expression matching the current search filter value and
* search properties represented in the widget.
* \since QGIS 2.16
*/
QString currentFilterExpression() const override;
/**
* Set the constraint status for this widget.
*/
@ -104,11 +98,6 @@ class GUI_EXPORT QgsAttributeFormEditorWidget : public QgsAttributeFormWidget
*/
void changesCommitted();
/**
* Resets the search/filter value of the widget.
*/
void resetSearch();
signals:
/**
@ -128,60 +117,16 @@ class GUI_EXPORT QgsAttributeFormEditorWidget : public QgsAttributeFormWidget
//! Triggered when the multi edit tool button "set field value" action is selected
void setFieldTriggered();
//! Triggered when search button flags are changed
void searchWidgetFlagsChanged( QgsSearchWidgetWrapper::FilterFlags flags );
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();
/**
* 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
* \note this method is in place for unit testing only, and is not considered
* stable API
*/
void setSearchWidgetWrapper( QgsSearchWidgetWrapper *wrapper );
/**
* Returns the widget which should be used as a parent during construction
* of the search widget wrapper.
* \note this method is in place for unit testing only, and is not considered
* stable AP
*/
QWidget *searchWidgetFrame();
/**
* Returns the search widget wrapper used in this widget. The wrapper must
* first be created using createSearchWidgetWrapper()
* \note this method is in place for unit testing only, and is not considered
* stable API
*/
QList< QgsSearchWidgetWrapper * > searchWidgetWrappers();
void onAggregateChanged();
private:
QWidget *mEditPage = nullptr;
QWidget *mSearchPage = nullptr;
QStackedWidget *mStack = nullptr;
QWidget *mSearchFrame = nullptr;
QString mWidgetType;
QgsEditorWidgetWrapper *mWidget = nullptr;
QList< QgsSearchWidgetWrapper * > mSearchWidgets;
QgsAttributeForm *mForm = nullptr;
QLabel *mConstraintResultLabel = nullptr;
QgsMultiEditToolButton *mMultiEditButton = nullptr;
QgsSearchWidgetToolButton *mSearchWidgetToolButton = nullptr;
QgsAggregateToolButton *mAggregateButton = nullptr;
QVariant mPreviousValue;
bool mBlockValueUpdate;
bool mIsMixed;

View File

@ -0,0 +1,38 @@
/***************************************************************************
qgsattributeformrelationeditorwidget.cpp
--------------------------------------
Date : Nov 2017
Copyright : (C) 2017 Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* 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 "qgsattributeformrelationeditorwidget.h"
#include "qgsrelationaggregatesearchwidgetwrapper.h"
#include "qgsattributeform.h"
#include "qgsrelationwidgetwrapper.h"
QgsAttributeFormRelationEditorWidget::QgsAttributeFormRelationEditorWidget( QgsRelationWidgetWrapper *wrapper, QgsAttributeForm *form )
: QgsAttributeFormWidget( wrapper, form )
, mWrapper( wrapper )
{
}
void QgsAttributeFormRelationEditorWidget::createSearchWidgetWrappers( const QgsAttributeEditorContext &context )
{
mSearchWidget = new QgsRelationAggregateSearchWidgetWrapper( layer(), mWrapper, form() );
setSearchWidgetWrapper( mSearchWidget );
}
QString QgsAttributeFormRelationEditorWidget::currentFilterExpression() const
{
return mSearchWidget->expression();
}

View File

@ -0,0 +1,40 @@
/***************************************************************************
qgsattributeformrelationeditorwidget.h
--------------------------------------
Date : Nov 2017
Copyright : (C) 2017 Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* 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 QGSATTRIBUTEFORMRELATIONEDITORWIDGET_H
#define QGSATTRIBUTEFORMRELATIONEDITORWIDGET_H
#include "qgis_gui.h"
#include "qgsattributeformwidget.h"
class QgsRelationWidgetWrapper;
class QgsRelationAggregateSearchWidgetWrapper;
class GUI_EXPORT QgsAttributeFormRelationEditorWidget : public QgsAttributeFormWidget
{
Q_OBJECT
public:
explicit QgsAttributeFormRelationEditorWidget( QgsRelationWidgetWrapper *wrapper, QgsAttributeForm *form );
virtual void createSearchWidgetWrappers( const QgsAttributeEditorContext &context SIP_PYARGREMOVE = QgsAttributeEditorContext() ) override;
virtual QString currentFilterExpression() const override;
private:
QgsRelationAggregateSearchWidgetWrapper *mSearchWidget;
QgsRelationWidgetWrapper *mWrapper;
};
#endif // QGSATTRIBUTEFORMRELATIONEDITORWIDGET_H

View File

@ -1,13 +1,56 @@
#include "qgsattributeformwidget.h"
#include <QHBoxLayout>
#include <QStackedWidget>
#include "qgsattributeform.h"
#include "qgssearchwidgettoolbutton.h"
QgsAttributeFormWidget::QgsAttributeFormWidget( QgsWidgetWrapper *widget, QgsAttributeForm *form )
: QWidget( form )
, mMode( DefaultMode )
, mForm( form )
, mWidget( widget )
{
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();
connect( mSearchWidgetToolButton, &QgsSearchWidgetToolButton::activeFlagsChanged,
this, &QgsAttributeFormWidget::searchWidgetFlagsChanged );
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;
mEditPage->layout()->addWidget( mWidget->widget() );
updateWidgets();
}
void QgsAttributeFormWidget::setMode( QgsAttributeFormWidget::Mode mode )
@ -21,8 +64,131 @@ QgsAttributeForm *QgsAttributeFormWidget::form() const
return mForm;
}
QWidget *QgsAttributeFormWidget::searchWidgetFrame()
{
return mSearchFrame;
}
void QgsAttributeFormWidget::setSearchWidgetWrapper( QgsSearchWidgetWrapper *wrapper ) SIP_SKIP
{
mSearchWidgets.clear();
mSearchWidgets << wrapper;
mSearchFrame->layout()->addWidget( wrapper->widget() );
mSearchWidgetToolButton->setAvailableFlags( wrapper->supportedFlags() );
mSearchWidgetToolButton->setActiveFlags( QgsSearchWidgetWrapper::FilterFlags() );
mSearchWidgetToolButton->setDefaultFlags( wrapper->defaultFlags() );
connect( wrapper, &QgsSearchWidgetWrapper::valueChanged, mSearchWidgetToolButton, &QgsSearchWidgetToolButton::setActive );
connect( wrapper, &QgsSearchWidgetWrapper::valueCleared, mSearchWidgetToolButton, &QgsSearchWidgetToolButton::setInactive );
}
void QgsAttributeFormWidget::addAdditionalSearchWidgetWrapper( QgsSearchWidgetWrapper *wrapper )
{
mSearchWidgets << wrapper;
mSearchFrame->layout()->addWidget( wrapper->widget() );
wrapper->widget()->hide();
}
QList<QgsSearchWidgetWrapper *> QgsAttributeFormWidget::searchWidgetWrappers() SIP_SKIP
{
return mSearchWidgets;
}
QString QgsAttributeFormWidget::currentFilterExpression() const
{
if ( mSearchWidgets.isEmpty() )
return QString();
if ( !mSearchWidgetToolButton->isActive() )
return QString();
if ( mSearchWidgetToolButton->activeFlags() & QgsSearchWidgetWrapper::Between )
{
// special case: Between search
QString filter1 = mSearchWidgets.at( 0 )->createExpression( QgsSearchWidgetWrapper::GreaterThanOrEqualTo );
QString filter2 = mSearchWidgets.at( 1 )->createExpression( QgsSearchWidgetWrapper::LessThanOrEqualTo );
return QStringLiteral( "%1 AND %2" ).arg( filter1, filter2 );
}
else if ( mSearchWidgetToolButton->activeFlags() & QgsSearchWidgetWrapper::IsNotBetween )
{
// special case: Is Not Between search
QString filter1 = mSearchWidgets.at( 0 )->createExpression( QgsSearchWidgetWrapper::LessThan );
QString filter2 = mSearchWidgets.at( 1 )->createExpression( QgsSearchWidgetWrapper::GreaterThan );
return QStringLiteral( "%1 OR %2" ).arg( filter1, filter2 );
}
return mSearchWidgets.at( 0 )->createExpression( mSearchWidgetToolButton->activeFlags() );
}
void QgsAttributeFormWidget::resetSearch()
{
mSearchWidgetToolButton->setInactive();
Q_FOREACH ( QgsSearchWidgetWrapper *widget, mSearchWidgets )
{
widget->clearWidget();
}
}
QgsSearchWidgetToolButton *QgsAttributeFormWidget::searchWidgetToolButton() SIP_SKIP
{
return mSearchWidgetToolButton;
}
QgsVectorLayer *QgsAttributeFormWidget::layer()
{
QgsAttributeForm *aform = form();
return aform ? aform->layer() : nullptr;
}
void QgsAttributeFormWidget::searchWidgetFlagsChanged( QgsSearchWidgetWrapper::FilterFlags flags )
{
Q_FOREACH ( QgsSearchWidgetWrapper *widget, mSearchWidgets )
{
widget->setEnabled( !( flags & QgsSearchWidgetWrapper::IsNull )
&& !( flags & QgsSearchWidgetWrapper::IsNotNull ) );
if ( !mSearchWidgetToolButton->isActive() )
{
widget->clearWidget();
}
}
if ( mSearchWidgets.count() >= 2 )
{
mSearchWidgets.at( 1 )->widget()->setVisible( flags & QgsSearchWidgetWrapper::Between ||
flags & QgsSearchWidgetWrapper::IsNotBetween );
}
}
void QgsAttributeFormWidget::updateWidgets()
{
switch ( mMode )
{
case DefaultMode:
case MultiEditMode:
mStack->setCurrentWidget( mEditPage );
break;
case SearchMode:
case AggregateSearchMode:
{
mStack->setCurrentWidget( mSearchPage );
break;
}
}
}
QWidget *QgsAttributeFormWidget::searchPage() const
{
return mSearchPage;
}
QStackedWidget *QgsAttributeFormWidget::stack() const
{
return mStack;
}
QWidget *QgsAttributeFormWidget::editPage() const
{
return mEditPage;
}

View File

@ -10,6 +10,8 @@
#include <QVariant>
class QgsAttributeForm;
class QStackedWidget;
class QgsSearchWidgetToolButton;
class GUI_EXPORT QgsAttributeFormWidget : public QWidget // SIP_ABSTRACT
{
@ -23,6 +25,7 @@ class GUI_EXPORT QgsAttributeFormWidget : public QWidget // SIP_ABSTRACT
DefaultMode, //!< Default mode, only the editor widget is shown
MultiEditMode, //!< Multi edit mode, both the editor widget and a QgsMultiEditToolButton is shown
SearchMode, //!< Layer search/filter mode
AggregateSearchMode,
};
explicit QgsAttributeFormWidget( QgsWidgetWrapper *widget, QgsAttributeForm *form );
@ -40,7 +43,7 @@ class GUI_EXPORT QgsAttributeFormWidget : public QWidget // SIP_ABSTRACT
* search properties represented in the widget.
* \since QGIS 2.16
*/
virtual QString currentFilterExpression() const = 0;
virtual QString currentFilterExpression() const;
/**
@ -61,10 +64,72 @@ class GUI_EXPORT QgsAttributeFormWidget : public QWidget // SIP_ABSTRACT
QgsAttributeForm *form() const;
/**
* Returns the widget which should be used as a parent during construction
* of the search widget wrapper.
* \note this method is in place for unit testing only, and is not considered
* stable API
*/
QWidget *searchWidgetFrame() SIP_SKIP;
/**
* 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
* \note this method is in place for unit testing only, and is not considered
* stable API
*/
void setSearchWidgetWrapper( QgsSearchWidgetWrapper *wrapper );
void addAdditionalSearchWidgetWrapper( QgsSearchWidgetWrapper *wrapper );
/**
* Returns the search widget wrapper used in this widget. The wrapper must
* first be created using createSearchWidgetWrapper()
* \note this method is in place for unit testing only, and is not considered
* stable API
*/
QList< QgsSearchWidgetWrapper * > searchWidgetWrappers();
/**
* Resets the search/filter value of the widget.
*/
void resetSearch();
protected:
QWidget *editPage() const SIP_SKIP;
QStackedWidget *stack() const SIP_SKIP;
QWidget *searchPage() const SIP_SKIP;
/**
* 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 slots:
//! Triggered when search button flags are changed
void searchWidgetFlagsChanged( QgsSearchWidgetWrapper::FilterFlags flags );
private:
virtual void updateWidgets();
QgsAttributeFormWidget::Mode mMode = DefaultMode;
virtual void updateWidgets() = 0;
QgsSearchWidgetToolButton *mSearchWidgetToolButton = nullptr;
QWidget *mEditPage = nullptr;
QWidget *mSearchPage = nullptr;
QStackedWidget *mStack = nullptr;
QWidget *mSearchFrame = nullptr;
QgsAttributeForm *mForm = nullptr;
QList< QgsSearchWidgetWrapper * > mSearchWidgets;
QgsWidgetWrapper *mWidget = nullptr;
};
#endif // QGSATTRIBUTEFORMWIDGET_H