Skip to content

Commit

Permalink
Apply same logic regarding joined fields as is used in field calculator
Browse files Browse the repository at this point in the history
to QgsFieldProxyModel set to hiding read only fields

I.e. show editable joined fields, but only if they are set to a non-hidden
editor widget
  • Loading branch information
nyalldawson committed Aug 6, 2019
1 parent 4a11150 commit 126ccb9
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 2 deletions.
2 changes: 2 additions & 0 deletions python/core/auto_generated/qgsfieldmodel.sip.in
Expand Up @@ -36,6 +36,8 @@ It can be associated with a QgsMapLayerModel to dynamically display a layer and
FieldTypeRole,
FieldOriginRole,
IsEmptyRole,
EditorWidgetType,
JoinedFieldIsEditable,
};

explicit QgsFieldModel( QObject *parent /TransferThis/ = 0 );
Expand Down
28 changes: 28 additions & 0 deletions src/core/qgsfieldmodel.cpp
Expand Up @@ -22,6 +22,7 @@
#include "qgslogger.h"
#include "qgsapplication.h"
#include "qgsvectorlayer.h"
#include "qgsvectorlayerjoinbuffer.h"

QgsFieldModel::QgsFieldModel( QObject *parent )
: QAbstractItemModel( parent )
Expand Down Expand Up @@ -359,6 +360,33 @@ QVariant QgsFieldModel::data( const QModelIndex &index, int role ) const
return isEmpty;
}

case EditorWidgetType:
{
if ( exprIdx < 0 && !isEmpty )
{
return mFields.at( index.row() - fieldOffset ).editorWidgetSetup().type();
}
return QVariant();
}

case JoinedFieldIsEditable:
{
if ( exprIdx < 0 && !isEmpty )
{
if ( mLayer && mFields.fieldOrigin( index.row() - fieldOffset ) == QgsFields::OriginJoin )
{
int srcFieldIndex;
const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( index.row() - fieldOffset, mLayer->fields(), srcFieldIndex );

if ( !info || !info->isEditable() )
return false;

return true;
}
}
return QVariant();
}

case Qt::DisplayRole:
case Qt::EditRole:
case Qt::ToolTipRole:
Expand Down
2 changes: 2 additions & 0 deletions src/core/qgsfieldmodel.h
Expand Up @@ -55,6 +55,8 @@ class CORE_EXPORT QgsFieldModel : public QAbstractItemModel
FieldTypeRole = Qt::UserRole + 6, //!< Return the field type (if a field, return QVariant if expression)
FieldOriginRole = Qt::UserRole + 7, //!< Return the field origin (if a field, returns QVariant if expression)
IsEmptyRole = Qt::UserRole + 8, //!< Return if the index corresponds to the empty value
EditorWidgetType = Qt::UserRole + 9, //!< Editor widget type
JoinedFieldIsEditable = Qt::UserRole + 10, //!< TRUE if a joined field is editable (returns QVariant if not a joined field)
};

/**
Expand Down
12 changes: 11 additions & 1 deletion src/core/qgsfieldproxymodel.cpp
Expand Up @@ -44,8 +44,18 @@ bool QgsFieldProxyModel::isReadOnly( const QModelIndex &index ) const
QgsFields::FieldOrigin origin = static_cast< QgsFields::FieldOrigin >( originVariant.toInt() );
switch ( origin )
{
case QgsFields::OriginUnknown:
case QgsFields::OriginJoin:
{
// show joined fields (e.g. auxiliary fields) only if they have a non-hidden editor widget.
// This enables them to be bulk field-calculated when a user needs to, but hides them by default
// (since there's often MANY of these, e.g. after using the label properties tool on a layer)
if ( sourceModel()->data( index, QgsFieldModel::EditorWidgetType ).toString() == QLatin1String( "Hidden" ) )
return true;

return !sourceModel()->data( index, QgsFieldModel::JoinedFieldIsEditable ).toBool();
}

case QgsFields::OriginUnknown:
case QgsFields::OriginExpression:
//read only
return true;
Expand Down
88 changes: 87 additions & 1 deletion tests/src/python/test_qgsfieldmodel.py
Expand Up @@ -15,7 +15,11 @@
from qgis.core import (QgsField,
QgsFields,
QgsVectorLayer,
QgsFieldModel)
QgsFieldModel,
QgsFieldProxyModel,
QgsEditorWidgetSetup,
QgsProject,
QgsVectorLayerJoinInfo)
from qgis.PyQt.QtCore import QVariant, Qt

from qgis.testing import start_app, unittest
Expand All @@ -26,6 +30,8 @@
def create_layer():
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
"addfeat", "memory")
layer.setEditorWidgetSetup(0, QgsEditorWidgetSetup('Hidden', {}))
layer.setEditorWidgetSetup(1, QgsEditorWidgetSetup('ValueMap', {}))
assert layer.isValid()
return layer

Expand Down Expand Up @@ -245,6 +251,86 @@ def testDisplayRole(self):
m.setAllowEmptyFieldName(True)
self.assertFalse(m.data(m.indexFromName(None), Qt.DisplayRole))

def testEditorWidgetTypeRole(self):
l, m = create_model()
self.assertEqual(m.data(m.indexFromName('fldtxt'), QgsFieldModel.EditorWidgetType), 'Hidden')
self.assertEqual(m.data(m.indexFromName('fldint'), QgsFieldModel.EditorWidgetType), 'ValueMap')
self.assertIsNone(m.data(m.indexFromName('an expression'), QgsFieldModel.EditorWidgetType))
self.assertIsNone(m.data(m.indexFromName(None), QgsFieldModel.EditorWidgetType))
m.setAllowExpression(True)
m.setExpression('an expression')
self.assertIsNone(m.data(m.indexFromName('an expression'), QgsFieldModel.EditorWidgetType))
m.setAllowEmptyFieldName(True)
self.assertIsNone(m.data(m.indexFromName(None), QgsFieldModel.EditorWidgetType))

def testJoinedFieldIsEditableRole(self):
layer = QgsVectorLayer("Point?field=id_a:integer",
"addfeat", "memory")
layer2 = QgsVectorLayer("Point?field=id_b:integer&field=value_b",
"addfeat", "memory")
QgsProject.instance().addMapLayers([layer, layer2])

# editable join
join_info = QgsVectorLayerJoinInfo()
join_info.setTargetFieldName("id_a")
join_info.setJoinLayer(layer2)
join_info.setJoinFieldName("id_b")
join_info.setPrefix("B_")
join_info.setEditable(True)
join_info.setUpsertOnEdit(True)
layer.addJoin(join_info)

m = QgsFieldModel()
m.setLayer(layer)

self.assertIsNone(m.data(m.indexFromName('id_a'), QgsFieldModel.JoinedFieldIsEditable))
self.assertTrue(m.data(m.indexFromName('B_value_b'), QgsFieldModel.JoinedFieldIsEditable))
self.assertIsNone(m.data(m.indexFromName('an expression'), QgsFieldModel.JoinedFieldIsEditable))
self.assertIsNone(m.data(m.indexFromName(None), QgsFieldModel.JoinedFieldIsEditable))
m.setAllowExpression(True)
m.setExpression('an expression')
self.assertIsNone(m.data(m.indexFromName('an expression'), QgsFieldModel.JoinedFieldIsEditable))
m.setAllowEmptyFieldName(True)
self.assertIsNone(m.data(m.indexFromName(None), QgsFieldModel.JoinedFieldIsEditable))

proxy_m = QgsFieldProxyModel()
proxy_m.setFilters(QgsFieldProxyModel.AllTypes | QgsFieldProxyModel.HideReadOnly)
proxy_m.sourceFieldModel().setLayer(layer)
self.assertEqual(proxy_m.rowCount(), 2)
self.assertEqual(proxy_m.data(proxy_m.index(0, 0)), 'id_a')
self.assertEqual(proxy_m.data(proxy_m.index(1, 0)), 'B_value_b')

# not editable join
layer3 = QgsVectorLayer("Point?field=id_a:integer",
"addfeat", "memory")
QgsProject.instance().addMapLayers([layer3])
join_info = QgsVectorLayerJoinInfo()
join_info.setTargetFieldName("id_a")
join_info.setJoinLayer(layer2)
join_info.setJoinFieldName("id_b")
join_info.setPrefix("B_")
join_info.setEditable(False)

layer3.addJoin(join_info)
m = QgsFieldModel()
m.setLayer(layer3)

self.assertIsNone(m.data(m.indexFromName('id_a'), QgsFieldModel.JoinedFieldIsEditable))
self.assertFalse(m.data(m.indexFromName('B_value_b'), QgsFieldModel.JoinedFieldIsEditable))
self.assertIsNone(m.data(m.indexFromName('an expression'), QgsFieldModel.JoinedFieldIsEditable))
self.assertIsNone(m.data(m.indexFromName(None), QgsFieldModel.JoinedFieldIsEditable))
m.setAllowExpression(True)
m.setExpression('an expression')
self.assertIsNone(m.data(m.indexFromName('an expression'), QgsFieldModel.JoinedFieldIsEditable))
m.setAllowEmptyFieldName(True)
self.assertIsNone(m.data(m.indexFromName(None), QgsFieldModel.JoinedFieldIsEditable))

proxy_m = QgsFieldProxyModel()
proxy_m.sourceFieldModel().setLayer(layer3)
proxy_m.setFilters(QgsFieldProxyModel.AllTypes | QgsFieldProxyModel.HideReadOnly)
self.assertEqual(proxy_m.rowCount(), 1)
self.assertEqual(proxy_m.data(proxy_m.index(0, 0)), 'id_a')

def testFieldTooltip(self):
f = QgsField('my_string', QVariant.String, 'string')
self.assertEqual(QgsFieldModel.fieldToolTip(f), '<b>my_string</b><p>string</p>')
Expand Down

0 comments on commit 126ccb9

Please sign in to comment.