mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
[FEATURE] Allow array values as a valid result for data defined offset
or size properties Previously only string values of the format 'x,y' would be permitted. But I've seen MANY bug reports and questions about this, so also allow arrays of doubles as a valid result. E.g. "array(3,5)". In any case, it's just nicer. Fixes #31444
This commit is contained in:
parent
60e2b70ee3
commit
ddbb1b175f
@ -92,6 +92,23 @@ Decodes a QSizeF from a string.
|
||||
.. seealso:: :py:func:`encodePoint`
|
||||
|
||||
.. seealso:: :py:func:`decodeSize`
|
||||
%End
|
||||
|
||||
static QPointF toPoint( const QVariant &value, bool *ok /Out/ = 0 );
|
||||
%Docstring
|
||||
Converts a ``value`` to a point.
|
||||
|
||||
:param value: value to convert
|
||||
|
||||
:return: - converted point
|
||||
- ok: will be set to ``True`` if value was successfully converted
|
||||
|
||||
|
||||
.. seealso:: :py:func:`decodePoint`
|
||||
|
||||
.. seealso:: :py:func:`toSize`
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
static QString encodeSize( QSizeF size );
|
||||
@ -114,6 +131,23 @@ Decodes a QSizeF from a string.
|
||||
.. seealso:: :py:func:`decodePoint`
|
||||
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
static QSizeF toSize( const QVariant &value, bool *ok /Out/ = 0 );
|
||||
%Docstring
|
||||
Converts a ``value`` to a size.
|
||||
|
||||
:param value: value to convert
|
||||
|
||||
:return: - converted size
|
||||
- ok: will be set to ``True`` if value was successfully converted
|
||||
|
||||
|
||||
.. seealso:: :py:func:`decodeSize`
|
||||
|
||||
.. seealso:: :py:func:`toPoint`
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
static QString encodeMapUnitScale( const QgsMapUnitScale &mapUnitScale );
|
||||
|
@ -1912,17 +1912,12 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
|
||||
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::CurvedCharAngleInOut ) )
|
||||
{
|
||||
exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::CurvedCharAngleInOut, context.expressionContext() );
|
||||
if ( exprVal.isValid() )
|
||||
bool ok = false;
|
||||
const QPointF maxcharanglePt = QgsSymbolLayerUtils::toPoint( exprVal, &ok );
|
||||
if ( ok )
|
||||
{
|
||||
QString ptstr = exprVal.toString().trimmed();
|
||||
QgsDebugMsgLevel( QStringLiteral( "exprVal CurvedCharAngleInOut:%1" ).arg( ptstr ), 4 );
|
||||
|
||||
if ( !ptstr.isEmpty() )
|
||||
{
|
||||
QPointF maxcharanglePt = QgsSymbolLayerUtils::decodePoint( ptstr );
|
||||
maxcharanglein = qBound( 20.0, static_cast< double >( maxcharanglePt.x() ), 60.0 );
|
||||
maxcharangleout = qBound( 20.0, static_cast< double >( maxcharanglePt.y() ), 95.0 );
|
||||
}
|
||||
maxcharanglein = qBound( 20.0, static_cast< double >( maxcharanglePt.x() ), 60.0 );
|
||||
maxcharangleout = qBound( 20.0, static_cast< double >( maxcharanglePt.y() ), 95.0 );
|
||||
}
|
||||
}
|
||||
// make sure maxcharangleout is always negative
|
||||
@ -2134,15 +2129,12 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
|
||||
{
|
||||
context.expressionContext().setOriginalValueVariable( QgsSymbolLayerUtils::encodePoint( QPointF( xOffset, yOffset ) ) );
|
||||
exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::OffsetXY, context.expressionContext() );
|
||||
if ( exprVal.isValid() )
|
||||
bool ok = false;
|
||||
const QPointF ddOffPt = QgsSymbolLayerUtils::toPoint( exprVal, &ok );
|
||||
if ( ok )
|
||||
{
|
||||
QString ptstr = exprVal.toString().trimmed();
|
||||
if ( !ptstr.isEmpty() )
|
||||
{
|
||||
QPointF ddOffPt = QgsSymbolLayerUtils::decodePoint( ptstr );
|
||||
xOff = ddOffPt.x();
|
||||
yOff = ddOffPt.y();
|
||||
}
|
||||
xOff = ddOffPt.x();
|
||||
yOff = ddOffPt.y();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2774,22 +2766,22 @@ bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
|
||||
}
|
||||
case DDPointF:
|
||||
{
|
||||
QString ptstr = exprVal.toString().trimmed();
|
||||
|
||||
if ( !ptstr.isEmpty() )
|
||||
bool ok = false;
|
||||
const QPointF res = QgsSymbolLayerUtils::toPoint( exprVal, &ok );
|
||||
if ( ok )
|
||||
{
|
||||
dataDefinedValues.insert( p, QVariant( QgsSymbolLayerUtils::decodePoint( ptstr ) ) );
|
||||
dataDefinedValues.insert( p, res );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case DDSizeF:
|
||||
{
|
||||
QString ptstr = exprVal.toString().trimmed();
|
||||
|
||||
if ( !ptstr.isEmpty() )
|
||||
bool ok = false;
|
||||
const QSizeF res = QgsSymbolLayerUtils::toSize( exprVal, &ok );
|
||||
if ( ok )
|
||||
{
|
||||
dataDefinedValues.insert( p, QVariant( QgsSymbolLayerUtils::decodeSize( ptstr ) ) );
|
||||
dataDefinedValues.insert( p, res );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1053,10 +1053,11 @@ void QgsTextBackgroundSettings::updateDataDefinedProperties( QgsRenderContext &c
|
||||
exprVal = properties.value( QgsPalLayerSettings::ShapeOffset, context.expressionContext() );
|
||||
if ( exprVal.isValid() )
|
||||
{
|
||||
QString offset = exprVal.toString();
|
||||
if ( !offset.isEmpty() )
|
||||
bool ok = false;
|
||||
const QPointF res = QgsSymbolLayerUtils::toPoint( exprVal, &ok );
|
||||
if ( ok )
|
||||
{
|
||||
d->offset = QgsSymbolLayerUtils::decodePoint( offset );
|
||||
d->offset = res;
|
||||
}
|
||||
}
|
||||
exprVal = properties.value( QgsPalLayerSettings::ShapeOffsetUnits, context.expressionContext() );
|
||||
@ -1075,10 +1076,11 @@ void QgsTextBackgroundSettings::updateDataDefinedProperties( QgsRenderContext &c
|
||||
exprVal = properties.value( QgsPalLayerSettings::ShapeRadii, context.expressionContext() );
|
||||
if ( exprVal.isValid() )
|
||||
{
|
||||
QString ptstr = exprVal.toString();
|
||||
if ( !ptstr.isEmpty() )
|
||||
bool ok = false;
|
||||
const QSizeF res = QgsSymbolLayerUtils::toSize( exprVal, &ok );
|
||||
if ( ok )
|
||||
{
|
||||
d->radii = QgsSymbolLayerUtils::decodeSize( ptstr );
|
||||
d->radii = res;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2391,9 +2391,10 @@ bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFa
|
||||
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOffset ) )
|
||||
{
|
||||
context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePoint( mOffset ) );
|
||||
QString offsetString = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString(), &ok );
|
||||
const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
|
||||
const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
|
||||
if ( ok )
|
||||
offset = QgsSymbolLayerUtils::decodePoint( offsetString );
|
||||
offset = res;
|
||||
}
|
||||
double offsetX = offset.x();
|
||||
double offsetY = offset.y();
|
||||
|
@ -467,9 +467,10 @@ void QgsMarkerSymbolLayer::markerOffset( QgsSymbolRenderContext &context, double
|
||||
{
|
||||
context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePoint( mOffset ) );
|
||||
QVariant exprVal = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext() );
|
||||
if ( exprVal.isValid() )
|
||||
bool ok = false;
|
||||
const QPointF offset = QgsSymbolLayerUtils::toPoint( exprVal, &ok );
|
||||
if ( ok )
|
||||
{
|
||||
QPointF offset = QgsSymbolLayerUtils::decodePoint( exprVal.toString() );
|
||||
offsetX = offset.x();
|
||||
offsetY = offset.y();
|
||||
}
|
||||
|
@ -440,6 +440,57 @@ QPointF QgsSymbolLayerUtils::decodePoint( const QString &str )
|
||||
return QPointF( lst[0].toDouble(), lst[1].toDouble() );
|
||||
}
|
||||
|
||||
QPointF QgsSymbolLayerUtils::toPoint( const QVariant &value, bool *ok )
|
||||
{
|
||||
if ( ok )
|
||||
*ok = false;
|
||||
|
||||
if ( value.isNull() )
|
||||
return QPoint();
|
||||
|
||||
if ( value.type() == QVariant::List )
|
||||
{
|
||||
const QVariantList list = value.toList();
|
||||
if ( list.size() != 2 )
|
||||
{
|
||||
return QPointF();
|
||||
}
|
||||
bool convertOk = false;
|
||||
double x = list.at( 0 ).toDouble( &convertOk );
|
||||
if ( convertOk )
|
||||
{
|
||||
double y = list.at( 1 ).toDouble( &convertOk );
|
||||
if ( convertOk )
|
||||
{
|
||||
if ( ok )
|
||||
*ok = true;
|
||||
return QPointF( x, y );
|
||||
}
|
||||
}
|
||||
return QPointF();
|
||||
}
|
||||
else
|
||||
{
|
||||
// can't use decodePoint here -- has no OK handling
|
||||
const QStringList list = value.toString().trimmed().split( ',' );
|
||||
if ( list.count() != 2 )
|
||||
return QPointF();
|
||||
bool convertOk = false;
|
||||
double x = list.at( 0 ).toDouble( &convertOk );
|
||||
if ( convertOk )
|
||||
{
|
||||
double y = list.at( 1 ).toDouble( &convertOk );
|
||||
if ( convertOk )
|
||||
{
|
||||
if ( ok )
|
||||
*ok = true;
|
||||
return QPointF( x, y );
|
||||
}
|
||||
}
|
||||
return QPointF();
|
||||
}
|
||||
}
|
||||
|
||||
QString QgsSymbolLayerUtils::encodeSize( QSizeF size )
|
||||
{
|
||||
return QStringLiteral( "%1,%2" ).arg( qgsDoubleToString( size.width() ), qgsDoubleToString( size.height() ) );
|
||||
@ -453,6 +504,57 @@ QSizeF QgsSymbolLayerUtils::decodeSize( const QString &string )
|
||||
return QSizeF( lst[0].toDouble(), lst[1].toDouble() );
|
||||
}
|
||||
|
||||
QSizeF QgsSymbolLayerUtils::toSize( const QVariant &value, bool *ok )
|
||||
{
|
||||
if ( ok )
|
||||
*ok = false;
|
||||
|
||||
if ( value.isNull() )
|
||||
return QSizeF();
|
||||
|
||||
if ( value.type() == QVariant::List )
|
||||
{
|
||||
const QVariantList list = value.toList();
|
||||
if ( list.size() != 2 )
|
||||
{
|
||||
return QSizeF();
|
||||
}
|
||||
bool convertOk = false;
|
||||
double x = list.at( 0 ).toDouble( &convertOk );
|
||||
if ( convertOk )
|
||||
{
|
||||
double y = list.at( 1 ).toDouble( &convertOk );
|
||||
if ( convertOk )
|
||||
{
|
||||
if ( ok )
|
||||
*ok = true;
|
||||
return QSizeF( x, y );
|
||||
}
|
||||
}
|
||||
return QSizeF();
|
||||
}
|
||||
else
|
||||
{
|
||||
// can't use decodePoint here -- has no OK handling
|
||||
const QStringList list = value.toString().trimmed().split( ',' );
|
||||
if ( list.count() != 2 )
|
||||
return QSizeF();
|
||||
bool convertOk = false;
|
||||
double x = list.at( 0 ).toDouble( &convertOk );
|
||||
if ( convertOk )
|
||||
{
|
||||
double y = list.at( 1 ).toDouble( &convertOk );
|
||||
if ( convertOk )
|
||||
{
|
||||
if ( ok )
|
||||
*ok = true;
|
||||
return QSizeF( x, y );
|
||||
}
|
||||
}
|
||||
return QSizeF();
|
||||
}
|
||||
}
|
||||
|
||||
QString QgsSymbolLayerUtils::encodeMapUnitScale( const QgsMapUnitScale &mapUnitScale )
|
||||
{
|
||||
return QStringLiteral( "3x:%1,%2,%3,%4,%5,%6" ).arg( qgsDoubleToString( mapUnitScale.minScale ),
|
||||
|
@ -122,6 +122,20 @@ class CORE_EXPORT QgsSymbolLayerUtils
|
||||
*/
|
||||
static QPointF decodePoint( const QString &string );
|
||||
|
||||
/**
|
||||
* Converts a \a value to a point.
|
||||
*
|
||||
* \param value value to convert
|
||||
* \param ok if specified, will be set to TRUE if value was successfully converted
|
||||
*
|
||||
* \returns converted point
|
||||
*
|
||||
* \see decodePoint()
|
||||
* \see toSize()
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
static QPointF toPoint( const QVariant &value, bool *ok SIP_OUT = nullptr );
|
||||
|
||||
/**
|
||||
* Encodes a QSizeF to a string.
|
||||
* \see decodeSize()
|
||||
@ -138,6 +152,20 @@ class CORE_EXPORT QgsSymbolLayerUtils
|
||||
*/
|
||||
static QSizeF decodeSize( const QString &string );
|
||||
|
||||
/**
|
||||
* Converts a \a value to a size.
|
||||
*
|
||||
* \param value value to convert
|
||||
* \param ok if specified, will be set to TRUE if value was successfully converted
|
||||
*
|
||||
* \returns converted size
|
||||
*
|
||||
* \see decodeSize()
|
||||
* \see toPoint()
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
static QSizeF toSize( const QVariant &value, bool *ok SIP_OUT = nullptr );
|
||||
|
||||
static QString encodeMapUnitScale( const QgsMapUnitScale &mapUnitScale );
|
||||
static QgsMapUnitScale decodeMapUnitScale( const QString &str );
|
||||
|
||||
|
@ -38,6 +38,51 @@ class PyQgsSymbolLayerUtils(unittest.TestCase):
|
||||
s2 = QgsSymbolLayerUtils.decodeSize('')
|
||||
self.assertEqual(s2, QSizeF(0, 0))
|
||||
|
||||
def testToSize(self):
|
||||
s2, ok = QgsSymbolLayerUtils.toSize(None)
|
||||
self.assertFalse(ok)
|
||||
|
||||
s2, ok = QgsSymbolLayerUtils.toSize(4)
|
||||
self.assertFalse(ok)
|
||||
|
||||
s2, ok = QgsSymbolLayerUtils.toSize('4')
|
||||
self.assertFalse(ok)
|
||||
|
||||
# arrays
|
||||
s2, ok = QgsSymbolLayerUtils.toSize([4])
|
||||
self.assertFalse(ok)
|
||||
|
||||
s2, ok = QgsSymbolLayerUtils.toSize([])
|
||||
self.assertFalse(ok)
|
||||
|
||||
s2, ok = QgsSymbolLayerUtils.toSize([4, 5, 6])
|
||||
self.assertFalse(ok)
|
||||
|
||||
s2, ok = QgsSymbolLayerUtils.toSize([4,5])
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(s2, QSizeF(4,5))
|
||||
|
||||
s2, ok = QgsSymbolLayerUtils.toSize(['4','5'])
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(s2, QSizeF(4,5))
|
||||
|
||||
# string values
|
||||
s = QSizeF()
|
||||
string = QgsSymbolLayerUtils.encodeSize(s)
|
||||
s2, ok = QgsSymbolLayerUtils.toSize(string)
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(s2, s)
|
||||
s = QSizeF(1.5, 2.5)
|
||||
string = QgsSymbolLayerUtils.encodeSize(s)
|
||||
s2, ok = QgsSymbolLayerUtils.toSize(string)
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(s2, s)
|
||||
|
||||
# bad string
|
||||
s2, ok = QgsSymbolLayerUtils.toSize('')
|
||||
self.assertFalse(ok)
|
||||
self.assertEqual(s2, QSizeF())
|
||||
|
||||
def testEncodeDecodePoint(self):
|
||||
s = QPointF()
|
||||
string = QgsSymbolLayerUtils.encodePoint(s)
|
||||
@ -52,6 +97,51 @@ class PyQgsSymbolLayerUtils(unittest.TestCase):
|
||||
s2 = QgsSymbolLayerUtils.decodePoint('')
|
||||
self.assertEqual(s2, QPointF())
|
||||
|
||||
def testToPoint(self):
|
||||
s2, ok = QgsSymbolLayerUtils.toPoint(None)
|
||||
self.assertFalse(ok)
|
||||
|
||||
s2, ok = QgsSymbolLayerUtils.toPoint(4)
|
||||
self.assertFalse(ok)
|
||||
|
||||
s2, ok = QgsSymbolLayerUtils.toPoint('4')
|
||||
self.assertFalse(ok)
|
||||
|
||||
# arrays
|
||||
s2, ok = QgsSymbolLayerUtils.toPoint([4])
|
||||
self.assertFalse(ok)
|
||||
|
||||
s2, ok = QgsSymbolLayerUtils.toPoint([])
|
||||
self.assertFalse(ok)
|
||||
|
||||
s2, ok = QgsSymbolLayerUtils.toPoint([4, 5, 6])
|
||||
self.assertFalse(ok)
|
||||
|
||||
s2, ok = QgsSymbolLayerUtils.toPoint([4,5])
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(s2, QPointF(4,5))
|
||||
|
||||
s2, ok = QgsSymbolLayerUtils.toPoint(['4','5'])
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(s2, QPointF(4,5))
|
||||
|
||||
# string values
|
||||
s = QPointF()
|
||||
string = QgsSymbolLayerUtils.encodePoint(s)
|
||||
s2, ok = QgsSymbolLayerUtils.toPoint(string)
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(s2, s)
|
||||
s = QPointF(1.5, 2.5)
|
||||
string = QgsSymbolLayerUtils.encodePoint(s)
|
||||
s2, ok = QgsSymbolLayerUtils.toPoint(string)
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(s2, s)
|
||||
|
||||
# bad string
|
||||
s2, ok = QgsSymbolLayerUtils.toPoint('')
|
||||
self.assertFalse(ok)
|
||||
self.assertEqual(s2, QPointF())
|
||||
|
||||
def testDecodeArrowHeadType(self):
|
||||
type, ok = QgsSymbolLayerUtils.decodeArrowHeadType(0)
|
||||
self.assertTrue(ok)
|
||||
|
Loading…
x
Reference in New Issue
Block a user