Skip to content

Commit

Permalink
Fixes #45290 : fix array display in expression builder
Browse files Browse the repository at this point in the history
  • Loading branch information
troopa81 authored and nyalldawson committed Nov 1, 2021
1 parent bb0718b commit 9ba9692
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 1 deletion.
14 changes: 14 additions & 0 deletions src/core/qgsfield.cpp
Expand Up @@ -342,6 +342,20 @@ QString QgsField::displayString( const QVariant &v ) const
{
return QObject::tr( "BLOB" );
}
else if ( d->type == QVariant::StringList || d->type == QVariant::List )
{
// we return an empty string if the list is empty, not a null string
QString result( "" );
const QVariantList list = v.toList();
for ( const QVariant &var : list )
{
if ( !result.isEmpty() )
result.append( ", " );
result.append( var.toString() );
}
return result;
}

// Fallback if special rules do not apply
return v.toString();
}
Expand Down
17 changes: 16 additions & 1 deletion src/gui/qgsexpressionbuilderwidget.cpp
Expand Up @@ -538,15 +538,30 @@ void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, int
for ( const QVariant &value : qgis::as_const( values ) )
{
QString strValue;
bool forceRepresentedValue = false;
if ( value.isNull() )
strValue = QStringLiteral( "NULL" );
else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
strValue = value.toString();
else if ( value.type() == QVariant::StringList )
{
QString result;
const QStringList strList = value.toStringList();
for ( QString str : strList )
{
if ( !result.isEmpty() )
result.append( ", " );

result.append( '\'' + str.replace( '\'', QLatin1String( "''" ) ) + '\'' );
}
strValue = QString( "array(%1)" ).arg( result );
forceRepresentedValue = true;
}
else
strValue = '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'';

QString representedValue = formatter->representValue( mLayer, fieldIndex, setup.config(), QVariant(), value );
if ( representedValue != value.toString() )
if ( forceRepresentedValue || representedValue != value.toString() )
representedValue = representedValue + QStringLiteral( " [" ) + strValue + ']';

QStandardItem *item = new QStandardItem( representedValue );
Expand Down
6 changes: 6 additions & 0 deletions tests/src/core/testqgsfield.cpp
Expand Up @@ -446,6 +446,12 @@ void TestQgsField::displayString()
QString testBAString( QStringLiteral( "test string" ) );
QByteArray testBA( testBAString.toLocal8Bit() );
QCOMPARE( binaryField.displayString( testBA ), QStringLiteral( "BLOB" ) );

// array field
const QgsField stringArrayField( QStringLiteral( "stringArray" ), QVariant::StringList, QStringLiteral( "StringArray" ) );
QCOMPARE( stringArrayField.displayString( QStringList() << "A" << "B" << "C" ), QStringLiteral( "A, B, C" ) );
const QgsField intArrayField( QStringLiteral( "intArray" ), QVariant::List, QStringLiteral( "IntArray" ) );
QCOMPARE( intArrayField.displayString( QVariantList() << 1 << 2 << 3 ), QStringLiteral( "1, 2, 3" ) );
}

void TestQgsField::convertCompatible()
Expand Down
83 changes: 83 additions & 0 deletions tests/src/python/test_qgsexpressionbuilderwidget.py
Expand Up @@ -13,6 +13,7 @@
import qgis # NOQA

from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import QListView
from qgis.testing import start_app, unittest
from qgis.gui import QgsExpressionBuilderWidget
from qgis.core import (QgsExpressionContext,
Expand Down Expand Up @@ -214,6 +215,88 @@ def testLayerVariables(self):

p.removeMapLayer(layer)

def testValuesList(self):
"""
Test the content of values list widget
"""

w = QgsExpressionBuilderWidget()

valuesList = w.findChild(QListView, 'mValuesListView')
self.assertTrue(valuesList)

valuesModel = valuesList.model()
self.assertTrue(valuesModel)

layer = QgsVectorLayer(
"None?field=myarray:string[]&field=mystr:string&field=myint:integer",
"arraylayer", "memory")

self.assertTrue(layer.isValid())

# add some features, one has invalid geometry
pr = layer.dataProvider()
f1 = QgsFeature(1)
f1.setAttributes([["one 'item'", 'B'], "another 'item'", 0])
f2 = QgsFeature(2)
f2.setAttributes([['C'], "", 1])
f3 = QgsFeature(3)
f3.setAttributes([[], "test", 2])
f4 = QgsFeature(4)
self.assertTrue(pr.addFeatures([f1, f2, f3, f4]))

w.setLayer(layer)

# test array
items = w.expressionTree().findExpressions("myarray")
self.assertEqual(len(items), 1)
currentIndex = w.expressionTree().model().mapFromSource(items[0].index())
self.assertTrue(currentIndex.isValid())
w.expressionTree().setCurrentIndex(currentIndex)
self.assertTrue(w.expressionTree().currentItem())

w.loadAllValues()

datas = sorted([(valuesModel.data(valuesModel.index(i, 0), Qt.DisplayRole), valuesModel.data(valuesModel.index(i, 0), Qt.UserRole + 1)) for i in range(4)])
self.assertEqual(datas, [(" [array()]", "array()"),
("C [array('C')]", "array('C')"),
("NULL [NULL]", "NULL"),
("one 'item', B [array('one ''item''', 'B')]", "array('one ''item''', 'B')")])

# test string
items = w.expressionTree().findExpressions("mystr")
self.assertEqual(len(items), 1)
currentIndex = w.expressionTree().model().mapFromSource(items[0].index())
self.assertTrue(currentIndex.isValid())
w.expressionTree().setCurrentIndex(currentIndex)
self.assertTrue(w.expressionTree().currentItem())

w.loadAllValues()

datas = sorted([(valuesModel.data(valuesModel.index(i, 0), Qt.DisplayRole), valuesModel.data(valuesModel.index(i, 0), Qt.UserRole + 1)) for i in range(4)])

self.assertEqual(datas, [("", "''"),
("NULL [NULL]", "NULL"),
("another 'item'", "'another ''item'''"),
("test", "'test'")])

# test int
items = w.expressionTree().findExpressions("myint")
self.assertEqual(len(items), 1)
currentIndex = w.expressionTree().model().mapFromSource(items[0].index())
self.assertTrue(currentIndex.isValid())
w.expressionTree().setCurrentIndex(currentIndex)
self.assertTrue(w.expressionTree().currentItem())

w.loadAllValues()

datas = sorted([(valuesModel.data(valuesModel.index(i, 0), Qt.DisplayRole), valuesModel.data(valuesModel.index(i, 0), Qt.UserRole + 1)) for i in range(4)])

self.assertEqual(datas, [("0", "0"),
("1", "1"),
("2", "2"),
("NULL [NULL]", "NULL")])


if __name__ == '__main__':
unittest.main()

0 comments on commit 9ba9692

Please sign in to comment.