Skip to content
Permalink
Browse files

Find a suitable editor widget if widget and config mismatch

If a .ui file is specified and the widget specified in the .ui file is not
supported by the widgetwrapper which is configured in the layer properties
the system will automatically try to find a better suitable widgetwrapper.

To do this, widgetwrappers (respectively their factories) can return a map of
supported widget types with priority values.
The widgetwrapper which offers the heighest priority for a certain widget type
will be used in case of a mismatch.

Sponsored by OPENGIS.ch special projects team (aka gis.se troubleshooting
section)
  • Loading branch information
m-kuhn committed Jul 23, 2015
1 parent 1d888ac commit de547adc19cad7b7bba9613538389b85a6912f61
Showing with 322 additions and 88 deletions.
  1. +1 −0 python/gui/editorwidgets/core/qgseditorwidgetwrapper.sip
  2. +11 −0 python/gui/editorwidgets/core/qgswidgetwrapper.sip
  3. +3 −2 src/gui/editorwidgets/core/qgseditorwidgetfactory.cpp
  4. +9 −0 src/gui/editorwidgets/core/qgseditorwidgetfactory.h
  5. +55 −0 src/gui/editorwidgets/core/qgseditorwidgetregistry.cpp
  6. +3 −0 src/gui/editorwidgets/core/qgseditorwidgetregistry.h
  7. +11 −0 src/gui/editorwidgets/core/qgswidgetwrapper.h
  8. +5 −0 src/gui/editorwidgets/qgscheckboxwidgetwrapper.cpp
  9. +1 −0 src/gui/editorwidgets/qgscheckboxwidgetwrapper.h
  10. +5 −0 src/gui/editorwidgets/qgsclassificationwidgetwrapper.cpp
  11. +1 −0 src/gui/editorwidgets/qgsclassificationwidgetwrapper.h
  12. +5 −0 src/gui/editorwidgets/qgscolorwidgetwrapper.cpp
  13. +1 −0 src/gui/editorwidgets/qgscolorwidgetwrapper.h
  14. +5 −0 src/gui/editorwidgets/qgsdatetimeeditwrapper.cpp
  15. +1 −0 src/gui/editorwidgets/qgsdatetimeeditwrapper.h
  16. +49 −44 src/gui/editorwidgets/qgsdefaultsearchwidgetwrapper.cpp
  17. +4 −3 src/gui/editorwidgets/qgsdefaultsearchwidgetwrapper.h
  18. +5 −0 src/gui/editorwidgets/qgsenumerationwidgetwrapper.cpp
  19. +1 −0 src/gui/editorwidgets/qgsenumerationwidgetwrapper.h
  20. +5 −0 src/gui/editorwidgets/qgsfilenamewidgetwrapper.cpp
  21. +1 −0 src/gui/editorwidgets/qgsfilenamewidgetwrapper.h
  22. +5 −0 src/gui/editorwidgets/qgshiddenwidgetwrapper.cpp
  23. +1 −0 src/gui/editorwidgets/qgshiddenwidgetwrapper.h
  24. +5 −0 src/gui/editorwidgets/qgsphotowidgetwrapper.cpp
  25. +1 −0 src/gui/editorwidgets/qgsphotowidgetwrapper.h
  26. +10 −0 src/gui/editorwidgets/qgsrangewidgetfactory.cpp
  27. +1 −0 src/gui/editorwidgets/qgsrangewidgetfactory.h
  28. +56 −35 src/gui/editorwidgets/qgsrangewidgetwrapper.cpp
  29. +1 −0 src/gui/editorwidgets/qgsrangewidgetwrapper.h
  30. +5 −0 src/gui/editorwidgets/qgsrelationreferencewidgetwrapper.cpp
  31. +1 −0 src/gui/editorwidgets/qgsrelationreferencewidgetwrapper.h
  32. +5 −0 src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp
  33. +1 −0 src/gui/editorwidgets/qgsrelationwidgetwrapper.h
  34. +5 −0 src/gui/editorwidgets/qgstexteditwrapper.cpp
  35. +1 −0 src/gui/editorwidgets/qgstexteditwrapper.h
  36. +5 −0 src/gui/editorwidgets/qgsuniquevaluewidgetwrapper.cpp
  37. +1 −0 src/gui/editorwidgets/qgsuniquevaluewidgetwrapper.h
  38. +5 −0 src/gui/editorwidgets/qgsuuidwidgetwrapper.cpp
  39. +1 −0 src/gui/editorwidgets/qgsuuidwidgetwrapper.h
  40. +7 −0 src/gui/editorwidgets/qgsvaluemapwidgetfactory.cpp
  41. +1 −0 src/gui/editorwidgets/qgsvaluemapwidgetfactory.h
  42. +5 −0 src/gui/editorwidgets/qgsvaluemapwidgetwrapper.cpp
  43. +1 −0 src/gui/editorwidgets/qgsvaluemapwidgetwrapper.h
  44. +7 −2 src/gui/editorwidgets/qgsvaluerelationwidgetwrapper.cpp
  45. +3 −2 src/gui/editorwidgets/qgsvaluerelationwidgetwrapper.h
  46. +5 −0 src/gui/editorwidgets/qgswebviewwidgetwrapper.cpp
  47. +1 −0 src/gui/editorwidgets/qgswebviewwidgetwrapper.h
@@ -88,6 +88,7 @@ class QgsEditorWidgetWrapper : QObject
*/
void setEnabled( bool enabled );

virtual bool valid() = 0;
protected:
virtual QWidget* createWidget( QWidget* parent ) = 0;

@@ -111,6 +111,17 @@ class QgsWidgetWrapper : QObject
*/
static QgsWidgetWrapper* fromWidget( QWidget* widget );

/**
* Return true if the widget has been properly initialized.
* This acts as hint for the calling party if this wrapper can be used
* after initializing it.
* If it cannot be used this is a hint tothe caller that he may try to find
* another suitable widget type instead.
*
* @return Validity status of this widget.
*/
virtual bool valid() = 0;

protected:
/**
* This method should create a new widget with the provided parent. This will only be called
@@ -30,8 +30,9 @@ QgsEditorWidgetFactory::~QgsEditorWidgetFactory()
{
}

/** Override in own factory to get something different than the default (a simple QgsFilterLineEdit)
*
/**
* By default a simple QgsFilterLineEdit is returned as search widget.
* Override in own factory to get something different than the default.
*/
QgsSearchWidgetWrapper* QgsEditorWidgetFactory::createSearchWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent ) const
{
@@ -115,6 +115,15 @@ class GUI_EXPORT QgsEditorWidgetFactory
*/
inline bool supportsField( QgsVectorLayer* vl, int fieldIdx ) { return isFieldSupported( vl, fieldIdx ); }

/**
* Returns a list of widget types which this editor widget supports.
* Each widget type can have a priority value attached, the factory with the highest one
* will be used.
*
* @return A map of widget type names and weight values
*/
virtual QMap<const char*, int> supportedWidgetTypes() { return QMap<const char*, int>(); }

/**
* Create a pretty String representation of the value.
*
@@ -95,9 +95,21 @@ QgsEditorWidgetWrapper* QgsEditorWidgetRegistry::create( const QString& widgetId
// Make sure that there is a widget created at this point
// so setValue() et al won't crash
ww->widget();

// If we tried to set a widget which is not supported by this wrapper
if ( !ww->valid() )
{
delete ww;
QString wid = findSuitableWrapper( editor );
ww = mWidgetFactories[wid]->create( vl, fieldIdx, editor, parent );
ww->setConfig( config );
ww->setContext( context );
}

return ww;
}
}

return 0;
}

@@ -164,6 +176,20 @@ bool QgsEditorWidgetRegistry::registerWidget( const QString& widgetId, QgsEditor
else
{
mWidgetFactories.insert( widgetId, widgetFactory );

// Use this factory as default where it provides the heighest priority
QMap<const char*, int> types = widgetFactory->supportedWidgetTypes();
QMap<const char*, int>::ConstIterator it;
it = types.constBegin();

for ( ; it != types.constEnd(); ++it )
{
if ( it.value() > mFactoriesByType[it.key()].first )
{
mFactoriesByType[it.key()] = qMakePair( it.value(), widgetFactory );
}
}

return true;
}
}
@@ -316,3 +342,32 @@ void QgsEditorWidgetRegistry::writeSymbology( QDomElement& element, QDomDocument

writeMapLayer( vl, element, doc );
}

QString QgsEditorWidgetRegistry::findSuitableWrapper( QWidget* editor )
{
QMap<const char*, QPair<int, QgsEditorWidgetFactory*> >::ConstIterator it;

QString widgetid;
int weight = 0;

it = mFactoriesByType.constBegin();
for ( ; it != mFactoriesByType.constEnd(); ++it )
{
if ( editor->staticMetaObject.className() == it.key() )
{
// if it's a perfect match: return it directly
return it.value().second->name();
}
else if ( editor->inherits( it.key() ) )
{
// if it's a subclass, continue evaluating, maybe we find a more-specific or one with more weight
if ( it.value().first > weight )
{
weight = it.value().first;
widgetid = it.value().second->name();
}
}
}

return widgetid;
}
@@ -193,7 +193,10 @@ class GUI_EXPORT QgsEditorWidgetRegistry : public QObject
void writeSymbology( QDomElement& element, QDomDocument& doc, QString& errorMessage );

private:
QString findSuitableWrapper( QWidget* editor );

QMap<QString, QgsEditorWidgetFactory*> mWidgetFactories;
QMap<const char*, QPair<int, QgsEditorWidgetFactory*> > mFactoriesByType;
};

#endif // QGSEDITORWIDGETREGISTRY_H
@@ -119,6 +119,17 @@ class GUI_EXPORT QgsWidgetWrapper : public QObject
*/
static QgsWidgetWrapper* fromWidget( QWidget* widget );

/**
* Return true if the widget has been properly initialized.
* This acts as hint for the calling party if this wrapper can be used
* after initializing it.
* If it cannot be used this is a hint tothe caller that he may try to find
* another suitable widget type instead.
*
* @return Validity status of this widget.
*/
virtual bool valid() = 0;

protected:
/**
* This method should create a new widget with the provided parent. This will only be called
@@ -53,6 +53,11 @@ void QgsCheckboxWidgetWrapper::initWidget( QWidget* editor )
connect( mGroupBox, SIGNAL( toggled( bool ) ), this, SLOT( valueChanged( bool ) ) );
}

bool QgsCheckboxWidgetWrapper::valid()
{
return mCheckBox || mGroupBox;
}

void QgsCheckboxWidgetWrapper::setValue( const QVariant& value )
{
if ( mGroupBox )
@@ -45,6 +45,7 @@ class GUI_EXPORT QgsCheckboxWidgetWrapper : public QgsEditorWidgetWrapper
protected:
QWidget* createWidget( QWidget* parent ) override;
void initWidget( QWidget* editor ) override;
bool valid() override;

public slots:
void setValue( const QVariant& value ) override;
@@ -59,6 +59,11 @@ void QgsClassificationWidgetWrapper::initWidget( QWidget* editor )
}
}

bool QgsClassificationWidgetWrapper::valid()
{
return mComboBox;
}

void QgsClassificationWidgetWrapper::setValue( const QVariant& value )
{
mComboBox->setCurrentIndex( mComboBox->findData( value ) );
@@ -33,6 +33,7 @@ class GUI_EXPORT QgsClassificationWidgetWrapper : public QgsEditorWidgetWrapper
protected:
QWidget*createWidget( QWidget* parent ) override;
void initWidget( QWidget* editor ) override;
bool valid() override;

public slots:
void setValue( const QVariant& value ) override;
@@ -46,6 +46,11 @@ void QgsColorWidgetWrapper::initWidget( QWidget* editor )
connect( mColorButton, SIGNAL( colorChanged( QColor ) ), this, SLOT( valueChanged() ) );
}

bool QgsColorWidgetWrapper::valid()
{
return mColorButton;
}

void QgsColorWidgetWrapper::setValue( const QVariant& value )
{
if ( mColorButton )
@@ -39,6 +39,7 @@ class GUI_EXPORT QgsColorWidgetWrapper : public QgsEditorWidgetWrapper
protected:
QWidget* createWidget( QWidget* parent ) override;
void initWidget( QWidget* editor ) override;
bool valid() override;

public slots:
void setValue( const QVariant& value ) override;
@@ -90,6 +90,11 @@ void QgsDateTimeEditWrapper::initWidget( QWidget *editor )
}
}

bool QgsDateTimeEditWrapper::valid()
{
return mQgsDateTimeEdit || mQDateTimeEdit;
}

void QgsDateTimeEditWrapper::dateTimeChanged( const QDateTime& dateTime )
{
const QString fieldFormat = config( "field_format", QGSDATETIMEEDIT_DATEFORMAT ).toString();
@@ -54,6 +54,7 @@ class GUI_EXPORT QgsDateTimeEditWrapper : public QgsEditorWidgetWrapper
QVariant value() override;
QWidget *createWidget( QWidget *parent ) override;
void initWidget( QWidget *editor ) override;
bool valid() override;

public slots:
void setValue( const QVariant &value ) override;
@@ -26,7 +26,7 @@ QgsDefaultSearchWidgetWrapper::QgsDefaultSearchWidgetWrapper( QgsVectorLayer* vl
, mLineEdit( NULL )
, mCheckbox( NULL )
, mContainer( NULL )
, mCaseString(QString("LIKE"))
, mCaseString( QString( "LIKE" ) )
{
}

@@ -36,67 +36,72 @@ QString QgsDefaultSearchWidgetWrapper::expression()
return mExpression;
}

void QgsDefaultSearchWidgetWrapper::setCaseString(int caseSensitiveCheckState)
void QgsDefaultSearchWidgetWrapper::setCaseString( int caseSensitiveCheckState )
{
if ( caseSensitiveCheckState == Qt::Checked)
{
mCaseString = "LIKE";
}
else
{
mCaseString = "ILIKE";
}
// need to update also the line edit
setExpression(mLineEdit->text());
if ( caseSensitiveCheckState == Qt::Checked )
{
mCaseString = "LIKE";
}
else
{
mCaseString = "ILIKE";
}
// need to update also the line edit
setExpression( mLineEdit->text() );
}

void QgsDefaultSearchWidgetWrapper::setExpression(QString exp)
void QgsDefaultSearchWidgetWrapper::setExpression( QString exp )
{
QVariant::Type fldType = layer()->pendingFields()[mFieldIdx].type();
bool numeric = ( fldType == QVariant::Int || fldType == QVariant::Double || fldType == QVariant::LongLong );
QSettings settings;
QString nullValue = settings.value( "qgis/nullValue", "NULL" ).toString();
QString fieldName = layer()->pendingFields()[mFieldIdx].name();
QString str;
if ( exp == nullValue )
{
str = QString( "%1 IS NULL" ).arg( QgsExpression::quotedColumnRef( fieldName ) );
}
else
{
str = QString( "%1 %2 '%3'" )
.arg( QgsExpression::quotedColumnRef( fieldName ) )
.arg( numeric ? "=" : mCaseString )
.arg( numeric
? exp.replace( "'", "''" )
:
"%" + exp.replace( "'", "''" ) + "%" ); // escape quotes
}
mExpression = str;
emit expressionChanged(mExpression);
QVariant::Type fldType = layer()->pendingFields()[mFieldIdx].type();
bool numeric = ( fldType == QVariant::Int || fldType == QVariant::Double || fldType == QVariant::LongLong );

QSettings settings;
QString nullValue = settings.value( "qgis/nullValue", "NULL" ).toString();
QString fieldName = layer()->pendingFields()[mFieldIdx].name();
QString str;
if ( exp == nullValue )
{
str = QString( "%1 IS NULL" ).arg( QgsExpression::quotedColumnRef( fieldName ) );
}
else
{
str = QString( "%1 %2 '%3'" )
.arg( QgsExpression::quotedColumnRef( fieldName ) )
.arg( numeric ? "=" : mCaseString )
.arg( numeric
? exp.replace( "'", "''" )
:
"%" + exp.replace( "'", "''" ) + "%" ); // escape quotes
}
mExpression = str;
emit expressionChanged( mExpression );
}

QWidget* QgsDefaultSearchWidgetWrapper::createWidget( QWidget* parent )
{
return new QWidget( parent );
}

bool QgsDefaultSearchWidgetWrapper::applyDirectly()
bool QgsDefaultSearchWidgetWrapper::applyDirectly()
{
return false;
return false;
}

void QgsDefaultSearchWidgetWrapper::initWidget( QWidget* widget )
{
mContainer = widget;
mContainer->setLayout(new QHBoxLayout() );
mContainer->setLayout( new QHBoxLayout() );
mLineEdit = new QgsFilterLineEdit();
mCheckbox = new QCheckBox("Case sensitive");
mContainer->layout()->addWidget(mLineEdit);
mContainer->layout()->addWidget(mCheckbox);
mCheckbox = new QCheckBox( "Case sensitive" );
mContainer->layout()->addWidget( mLineEdit );
mContainer->layout()->addWidget( mCheckbox );
connect( mLineEdit, SIGNAL( textChanged( QString ) ), this, SLOT( setExpression( QString ) ) );
connect( mCheckbox, SIGNAL( stateChanged( int ) ), this, SLOT( setCaseString(int) ) );
mCheckbox->setChecked(Qt::Unchecked);
connect( mCheckbox, SIGNAL( stateChanged( int ) ), this, SLOT( setCaseString( int ) ) );
mCheckbox->setChecked( Qt::Unchecked );
mCaseString = "ILIKE";
}

bool QgsDefaultSearchWidgetWrapper::valid()
{
return true;
}
@@ -37,14 +37,15 @@ class GUI_EXPORT QgsDefaultSearchWidgetWrapper : public QgsSearchWidgetWrapper
QString expression() override;
bool applyDirectly() override;
protected slots:
void setExpression(QString exp) override;
void setExpression( QString exp ) override;

private slots:
void setCaseString(int);
void setCaseString( int );

protected:
QWidget* createWidget( QWidget* parent ) override;
void initWidget( QWidget* editor ) override;
bool valid() override;

private:
QgsFilterLineEdit* mLineEdit;
@@ -57,6 +57,11 @@ void QgsEnumerationWidgetWrapper::initWidget( QWidget* editor )
}
}

bool QgsEnumerationWidgetWrapper::valid()
{
return mComboBox;
}

void QgsEnumerationWidgetWrapper::setValue( const QVariant& value )
{
if ( mComboBox )

4 comments on commit de547ad

@3nids

This comment has been minimized.

Copy link
Member

@3nids 3nids replied Jul 24, 2015

This has been implemented for value map and range widgets only, correct?

@m-kuhn

This comment has been minimized.

Copy link
Member Author

@m-kuhn m-kuhn replied Jul 24, 2015

Yes. Do you see another one which should be autodetected?

@3nids

This comment has been minimized.

Copy link
Member

@3nids 3nids replied Jul 24, 2015

I did not look in details, but would Date/time widget (QgsDateTimeEditWidget, QDateTimeWidget) or relation reference (QgsRelationReferenceWidget, QWidget) would be candidates?

@m-kuhn

This comment has been minimized.

Copy link
Member Author

@m-kuhn m-kuhn replied Jul 24, 2015

Please sign in to comment.
You can’t perform that action at this time.