From 126ccb97363c7245a4da99a9940a928c3000155a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 4 Aug 2019 14:14:56 +1000 Subject: [PATCH] Apply same logic regarding joined fields as is used in field calculator 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 --- .../core/auto_generated/qgsfieldmodel.sip.in | 2 + src/core/qgsfieldmodel.cpp | 28 ++++++ src/core/qgsfieldmodel.h | 2 + src/core/qgsfieldproxymodel.cpp | 12 ++- tests/src/python/test_qgsfieldmodel.py | 88 ++++++++++++++++++- 5 files changed, 130 insertions(+), 2 deletions(-) diff --git a/python/core/auto_generated/qgsfieldmodel.sip.in b/python/core/auto_generated/qgsfieldmodel.sip.in index cd0b7bb65ee..55d7ed0e5eb 100644 --- a/python/core/auto_generated/qgsfieldmodel.sip.in +++ b/python/core/auto_generated/qgsfieldmodel.sip.in @@ -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 ); diff --git a/src/core/qgsfieldmodel.cpp b/src/core/qgsfieldmodel.cpp index 7224014f492..996b14ba6e4 100644 --- a/src/core/qgsfieldmodel.cpp +++ b/src/core/qgsfieldmodel.cpp @@ -22,6 +22,7 @@ #include "qgslogger.h" #include "qgsapplication.h" #include "qgsvectorlayer.h" +#include "qgsvectorlayerjoinbuffer.h" QgsFieldModel::QgsFieldModel( QObject *parent ) : QAbstractItemModel( parent ) @@ -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: diff --git a/src/core/qgsfieldmodel.h b/src/core/qgsfieldmodel.h index 4161305e8ab..2a5f50d29ca 100644 --- a/src/core/qgsfieldmodel.h +++ b/src/core/qgsfieldmodel.h @@ -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) }; /** diff --git a/src/core/qgsfieldproxymodel.cpp b/src/core/qgsfieldproxymodel.cpp index 52dd5309a12..c9b9cba151e 100644 --- a/src/core/qgsfieldproxymodel.cpp +++ b/src/core/qgsfieldproxymodel.cpp @@ -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; diff --git a/tests/src/python/test_qgsfieldmodel.py b/tests/src/python/test_qgsfieldmodel.py index 14a088621fe..614e0c89f28 100644 --- a/tests/src/python/test_qgsfieldmodel.py +++ b/tests/src/python/test_qgsfieldmodel.py @@ -15,7 +15,11 @@ import qgis # NOQA 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 @@ -26,6 +30,8 @@ start_app() 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 @@ -245,6 +251,86 @@ class TestQgsFieldModel(unittest.TestCase): 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), 'my_string

string

')