Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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 de547ad
Show file tree
Hide file tree
Showing 47 changed files with 322 additions and 88 deletions.
1 change: 1 addition & 0 deletions python/gui/editorwidgets/core/qgseditorwidgetwrapper.sip
Expand Up @@ -88,6 +88,7 @@ class QgsEditorWidgetWrapper : QObject
*/
void setEnabled( bool enabled );

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

Expand Down
11 changes: 11 additions & 0 deletions python/gui/editorwidgets/core/qgswidgetwrapper.sip
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions src/gui/editorwidgets/core/qgseditorwidgetfactory.cpp
Expand Up @@ -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
{
Expand Down
9 changes: 9 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetfactory.h
Expand Up @@ -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.
*
Expand Down
55 changes: 55 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetregistry.cpp
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
}
3 changes: 3 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetregistry.h
Expand Up @@ -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
11 changes: 11 additions & 0 deletions src/gui/editorwidgets/core/qgswidgetwrapper.h
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/gui/editorwidgets/qgscheckboxwidgetwrapper.cpp
Expand Up @@ -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 )
Expand Down
1 change: 1 addition & 0 deletions src/gui/editorwidgets/qgscheckboxwidgetwrapper.h
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/gui/editorwidgets/qgsclassificationwidgetwrapper.cpp
Expand Up @@ -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 ) );
Expand Down
1 change: 1 addition & 0 deletions src/gui/editorwidgets/qgsclassificationwidgetwrapper.h
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/gui/editorwidgets/qgscolorwidgetwrapper.cpp
Expand Up @@ -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 )
Expand Down
1 change: 1 addition & 0 deletions src/gui/editorwidgets/qgscolorwidgetwrapper.h
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/gui/editorwidgets/qgsdatetimeeditwrapper.cpp
Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions src/gui/editorwidgets/qgsdatetimeeditwrapper.h
Expand Up @@ -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;
Expand Down
93 changes: 49 additions & 44 deletions src/gui/editorwidgets/qgsdefaultsearchwidgetwrapper.cpp
Expand Up @@ -26,7 +26,7 @@ QgsDefaultSearchWidgetWrapper::QgsDefaultSearchWidgetWrapper( QgsVectorLayer* vl
, mLineEdit( NULL )
, mCheckbox( NULL )
, mContainer( NULL )
, mCaseString(QString("LIKE"))
, mCaseString( QString( "LIKE" ) )
{
}

Expand All @@ -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;
}
7 changes: 4 additions & 3 deletions src/gui/editorwidgets/qgsdefaultsearchwidgetwrapper.h
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/gui/editorwidgets/qgsenumerationwidgetwrapper.cpp
Expand Up @@ -57,6 +57,11 @@ void QgsEnumerationWidgetWrapper::initWidget( QWidget* editor )
}
}

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

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

4 comments on commit de547ad

@3nids
Copy link
Member

@3nids 3nids commented on de547ad Jul 24, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@m-kuhn
Copy link
Member Author

@m-kuhn m-kuhn commented on de547ad Jul 24, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@3nids
Copy link
Member

@3nids 3nids commented on de547ad Jul 24, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@m-kuhn
Copy link
Member Author

@m-kuhn m-kuhn commented on de547ad Jul 24, 2015 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.