mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-07 00:15:48 -04:00
allow to save/read enum/flags as map layer properties (#44329)
This commit is contained in:
parent
b0319105bc
commit
eaa4b54e20
@ -696,6 +696,8 @@ Read all custom properties from layer. Properties are stored in a map and saved
|
||||
.. versionadded:: 3.14
|
||||
%End
|
||||
|
||||
|
||||
|
||||
void removeCustomProperty( const QString &key );
|
||||
%Docstring
|
||||
Remove a custom property from layer. Properties are stored in a map and saved in project file.
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "qgsreadwritecontext.h"
|
||||
#include "qgsdataprovider.h"
|
||||
#include "qgis.h"
|
||||
#include "qgslogger.h"
|
||||
|
||||
class QgsAbstract3DRenderer;
|
||||
class QgsDataProvider;
|
||||
@ -675,6 +676,165 @@ class CORE_EXPORT QgsMapLayer : public QObject
|
||||
*/
|
||||
const QgsObjectCustomProperties &customProperties() const;
|
||||
|
||||
#ifndef SIP_RUN
|
||||
|
||||
/**
|
||||
* Returns the property value for a property based on an enum.
|
||||
* This forces the output to be a valid and existing entry of the enum.
|
||||
* Hence if the property value is incorrect, the given default value is returned.
|
||||
* This tries first with property as a string (as the enum) and then as an integer value.
|
||||
* \note The enum needs to be declared with Q_ENUM, and flags with Q_FLAG (not Q_FLAGS).
|
||||
* \see setCustomEnumProperty
|
||||
* \see customFlagProperty
|
||||
* \since QGIS 3.22
|
||||
*/
|
||||
template <class T>
|
||||
T customEnumProperty( const QString &key, const T &defaultValue )
|
||||
{
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<T>();
|
||||
Q_ASSERT( metaEnum.isValid() );
|
||||
if ( !metaEnum.isValid() )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "Invalid metaenum. Enum probably misses Q_ENUM or Q_FLAG declaration." ) );
|
||||
}
|
||||
|
||||
T v;
|
||||
bool ok = false;
|
||||
|
||||
if ( metaEnum.isValid() )
|
||||
{
|
||||
// read as string
|
||||
QByteArray ba = customProperty( key, metaEnum.valueToKey( static_cast<int>( defaultValue ) ) ).toString().toUtf8();
|
||||
const char *vs = ba.data();
|
||||
v = static_cast<T>( metaEnum.keyToValue( vs, &ok ) );
|
||||
if ( ok )
|
||||
return v;
|
||||
}
|
||||
|
||||
// if failed, try to read as int (old behavior)
|
||||
// this code shall be removed later
|
||||
// then the method could be marked as const
|
||||
v = static_cast<T>( customProperty( key, static_cast<int>( defaultValue ) ).toInt( &ok ) );
|
||||
if ( metaEnum.isValid() )
|
||||
{
|
||||
if ( !ok || !metaEnum.valueToKey( static_cast<int>( v ) ) )
|
||||
{
|
||||
v = defaultValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// found property as an integer
|
||||
// convert the property to the new form (string)
|
||||
setCustomEnumProperty( key, v );
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a property based on an enum.
|
||||
* The property will be saved as string.
|
||||
* \note The enum needs to be declared with Q_ENUM, and flags with Q_FLAG (not Q_FLAGS).
|
||||
* \see customEnumProperty
|
||||
* \see setCustomFlagProperty
|
||||
* \since QGIS 3.22
|
||||
*/
|
||||
template <class T>
|
||||
void setCustomEnumProperty( const QString &key, const T &value )
|
||||
{
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<T>();
|
||||
Q_ASSERT( metaEnum.isValid() );
|
||||
if ( metaEnum.isValid() )
|
||||
{
|
||||
setCustomProperty( key, metaEnum.valueToKey( static_cast<int>( value ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "Invalid metaenum. Enum probably misses Q_ENUM or Q_FLAG declaration." ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property value for a property based on a flag.
|
||||
* This forces the output to be a valid and existing entry of the flag.
|
||||
* Hence if the property value is incorrect, the given default value is returned.
|
||||
* This tries first with property as a string (using a byte array) and then as an integer value.
|
||||
* \note The flag needs to be declared with Q_FLAG (not Q_FLAGS).
|
||||
* \note for Python bindings, a custom implementation is achieved in Python directly.
|
||||
* \see setCustomFlagProperty
|
||||
* \see customEnumProperty
|
||||
* \since QGIS 3.22
|
||||
*/
|
||||
template <class T>
|
||||
T customFlagProperty( const QString &key, const T &defaultValue )
|
||||
{
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<T>();
|
||||
Q_ASSERT( metaEnum.isValid() );
|
||||
if ( !metaEnum.isValid() )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "Invalid metaenum. Enum probably misses Q_ENUM or Q_FLAG declaration." ) );
|
||||
}
|
||||
|
||||
T v = defaultValue;
|
||||
bool ok = false;
|
||||
|
||||
if ( metaEnum.isValid() )
|
||||
{
|
||||
// read as string
|
||||
QByteArray ba = customProperty( key, metaEnum.valueToKeys( defaultValue ) ).toString().toUtf8();
|
||||
const char *vs = ba.data();
|
||||
v = static_cast<T>( metaEnum.keysToValue( vs, &ok ) );
|
||||
}
|
||||
if ( !ok )
|
||||
{
|
||||
// if failed, try to read as int (old behavior)
|
||||
// this code shall be removed later
|
||||
// then the method could be marked as const
|
||||
v = T( customProperty( key, static_cast<int>( defaultValue ) ).toInt( &ok ) );
|
||||
if ( metaEnum.isValid() )
|
||||
{
|
||||
if ( !ok || metaEnum.valueToKeys( static_cast<int>( v ) ).isEmpty() )
|
||||
{
|
||||
v = defaultValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// found property as an integer
|
||||
// convert the property to the new form (string)
|
||||
setCustomFlagProperty( key, v );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a property based on a flag.
|
||||
* The property will be saved as string.
|
||||
* \note The flag needs to be declared with Q_FLAG (not Q_FLAGS).
|
||||
* \see customFlagProperty
|
||||
* \see customEnumProperty
|
||||
* \since QGIS 3.22
|
||||
*/
|
||||
template <class T>
|
||||
void setCustomFlagProperty( const QString &key, const T &value )
|
||||
{
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<T>();
|
||||
Q_ASSERT( metaEnum.isValid() );
|
||||
if ( metaEnum.isValid() )
|
||||
{
|
||||
setCustomProperty( key, metaEnum.valueToKeys( value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "Invalid metaenum. Enum probably misses Q_ENUM or Q_FLAG declaration." ) );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Remove a custom property from layer. Properties are stored in a map and saved in project file.
|
||||
* \see setCustomProperty()
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <qgsproviderregistry.h>
|
||||
#include "qgsvectorlayerref.h"
|
||||
#include "qgsmaplayerlistutils.h"
|
||||
#include "qgsmaplayerproxymodel.h"
|
||||
|
||||
/**
|
||||
* \ingroup UnitTests
|
||||
@ -64,6 +65,8 @@ class TestQgsMapLayer : public QObject
|
||||
|
||||
void notify();
|
||||
|
||||
void customEnumFlagProperties();
|
||||
|
||||
private:
|
||||
QgsVectorLayer *mpLayer = nullptr;
|
||||
};
|
||||
@ -387,5 +390,59 @@ void TestQgsMapLayer::notify()
|
||||
QCOMPARE( spyDataChanged.count(), 2 );
|
||||
}
|
||||
|
||||
void TestQgsMapLayer::customEnumFlagProperties()
|
||||
{
|
||||
std::unique_ptr<QgsVectorLayer> ml = std::make_unique<QgsVectorLayer>( QStringLiteral( "Point" ), QStringLiteral( "name" ), QStringLiteral( "memory" ) );
|
||||
|
||||
// assign to inexisting property
|
||||
ml->setCustomProperty( QStringLiteral( "my_property_for_units" ), -1 );
|
||||
ml->setCustomProperty( QStringLiteral( "my_property_for_units_as_string" ), QStringLiteral( "myString" ) );
|
||||
// just to be sure it really doesn't exist
|
||||
QVERIFY( static_cast<int>( QgsUnitTypes::LayoutMeters ) != -1 );
|
||||
|
||||
// standard method returns invalid property
|
||||
int v1 = ml->customProperty( QStringLiteral( "my_property_for_units" ), QgsUnitTypes::LayoutMeters ).toInt();
|
||||
QCOMPARE( v1, -1 );
|
||||
|
||||
// enum method returns default property if current property is incorrect
|
||||
QgsUnitTypes::LayoutUnit v2 = ml->customEnumProperty( QStringLiteral( "my_property_for_units" ), QgsUnitTypes::LayoutMeters );
|
||||
QCOMPARE( v2, QgsUnitTypes::LayoutMeters );
|
||||
QgsUnitTypes::LayoutUnit v2s = ml->customEnumProperty( QStringLiteral( "my_property_for_units_as_string" ), QgsUnitTypes::LayoutMeters );
|
||||
QCOMPARE( v2s, QgsUnitTypes::LayoutMeters );
|
||||
|
||||
// test a different property than default
|
||||
ml->setCustomEnumProperty( QStringLiteral( "my_property_for_units" ), QgsUnitTypes::LayoutCentimeters );
|
||||
QgsUnitTypes::LayoutUnit v3 = ml->customEnumProperty( QStringLiteral( "my_property_for_units" ), QgsUnitTypes::LayoutMeters );
|
||||
QCOMPARE( v3, QgsUnitTypes::LayoutCentimeters );
|
||||
ml->setCustomEnumProperty( QStringLiteral( "my_property_for_units" ), QgsUnitTypes::LayoutCentimeters );
|
||||
// auto conversion of old ml (int to str)
|
||||
QCOMPARE( ml->customProperty( "my_property_for_units" ).toString(), QStringLiteral( "LayoutCentimeters" ) );
|
||||
QgsUnitTypes::LayoutUnit v3s = ml->customEnumProperty( QStringLiteral( "my_property_for_units" ), QgsUnitTypes::LayoutMeters );
|
||||
QCOMPARE( v3s, QgsUnitTypes::LayoutCentimeters );
|
||||
QString v3ss = ml->customProperty( QStringLiteral( "my_property_for_units" ), QStringLiteral( "myDummyValue" ) ).toString();
|
||||
QCOMPARE( v3ss, QStringLiteral( "LayoutCentimeters" ) );
|
||||
|
||||
// Flags
|
||||
QgsMapLayerProxyModel::Filters pointAndLine = QgsMapLayerProxyModel::Filters( QgsMapLayerProxyModel::PointLayer | QgsMapLayerProxyModel::LineLayer );
|
||||
QgsMapLayerProxyModel::Filters pointAndPolygon = QgsMapLayerProxyModel::Filters( QgsMapLayerProxyModel::PointLayer | QgsMapLayerProxyModel::PolygonLayer );
|
||||
ml->setCustomProperty( QStringLiteral( "my_property_for_a_flag" ), 1e8 ); // invalid
|
||||
// this should be switched to customFlagProperty (see https://stackoverflow.com/questions/68485843/how-to-test-if-a-value-is-valid-for-a-qflags)
|
||||
QgsMapLayerProxyModel::Filters v4 = ml->customEnumProperty( QStringLiteral( "my_property_for_a_flag" ), pointAndLine );
|
||||
QCOMPARE( v4, pointAndLine );
|
||||
|
||||
ml->setCustomProperty( QStringLiteral( "my_property_for_a_flag" ), static_cast<int>( pointAndPolygon ) );
|
||||
QgsMapLayerProxyModel::Filters v5 = ml->customFlagProperty( QStringLiteral( "my_property_for_a_flag" ), pointAndLine );
|
||||
QCOMPARE( v5, pointAndPolygon );
|
||||
// auto conversion of old property (int to str)
|
||||
QCOMPARE( ml->customProperty( "my_property_for_a_flag" ).toString(), QStringLiteral( "PointLayer|PolygonLayer" ) );
|
||||
|
||||
ml->setCustomFlagProperty( QStringLiteral( "my_property_for_a_flag_as_string" ), pointAndPolygon );
|
||||
QgsMapLayerProxyModel::Filters v5s = ml->customFlagProperty( QStringLiteral( "my_property_for_a_flag_as_string" ), pointAndLine );
|
||||
QCOMPARE( v5s, pointAndPolygon );
|
||||
QString v5ss = ml->customProperty( QStringLiteral( "my_property_for_a_flag_as_string" ), QStringLiteral( "myDummyString" ) ).toString();
|
||||
QCOMPARE( v5ss, QStringLiteral( "PointLayer|PolygonLayer" ) );
|
||||
}
|
||||
|
||||
|
||||
QGSTEST_MAIN( TestQgsMapLayer )
|
||||
#include "testqgsmaplayer.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user