Revert "Allowing storing GPGK raster layer styles to DB"

This commit is contained in:
Nyall Dawson 2023-10-06 12:15:03 +10:00
parent 3642c9cc40
commit 9d8a722568
51 changed files with 1426 additions and 1486 deletions

View File

@ -3660,15 +3660,6 @@ Qgis.DatabaseProviderConnectionCapability2.baseClass = Qgis
Qgis.DatabaseProviderConnectionCapabilities2.baseClass = Qgis
DatabaseProviderConnectionCapabilities2 = Qgis # dirty hack since SIP seems to introduce the flags in module
# monkey patching scoped based enum
Qgis.ProviderStyleStorageCapability.SaveToDatabase.__doc__ = ""
Qgis.ProviderStyleStorageCapability.LoadFromDatabase.__doc__ = ""
Qgis.ProviderStyleStorageCapability.DeleteFromDatabase.__doc__ = ""
Qgis.ProviderStyleStorageCapability.__doc__ = "The StorageCapability enum represents the style storage operations supported by the provider.\n\n.. versionadded:: 3.34\n\n" + '* ``SaveToDatabase``: ' + Qgis.ProviderStyleStorageCapability.SaveToDatabase.__doc__ + '\n' + '* ``LoadFromDatabase``: ' + Qgis.ProviderStyleStorageCapability.LoadFromDatabase.__doc__ + '\n' + '* ``DeleteFromDatabase``: ' + Qgis.ProviderStyleStorageCapability.DeleteFromDatabase.__doc__
# --
Qgis.ProviderStyleStorageCapability.baseClass = Qgis
Qgis.ProviderStyleStorageCapabilities.baseClass = Qgis
ProviderStyleStorageCapabilities = Qgis # dirty hack since SIP seems to introduce the flags in module
# monkey patching scoped based enum
Qgis.UserProfileSelectionPolicy.LastProfile.__doc__ = "Open the last closed profile (only mode supported prior to QGIS 3.32)"
Qgis.UserProfileSelectionPolicy.DefaultProfile.__doc__ = "Open a specific profile"
Qgis.UserProfileSelectionPolicy.AskUser.__doc__ = "Let the user choose which profile to open"

View File

@ -467,13 +467,6 @@ String sequence used for separating components of sublayers strings.
.. seealso:: :py:func:`subLayers`
.. versionadded:: 3.12
%End
virtual Qgis::ProviderStyleStorageCapabilities styleStorageCapabilities() const;
%Docstring
Returns the style storage capabilities.
.. versionadded:: 3.34
%End
signals:

View File

@ -2121,15 +2121,6 @@ The development version
typedef QFlags<Qgis::DatabaseProviderConnectionCapability2> DatabaseProviderConnectionCapabilities2;
enum class ProviderStyleStorageCapability
{
SaveToDatabase,
LoadFromDatabase,
DeleteFromDatabase
};
typedef QFlags<Qgis::ProviderStyleStorageCapability> ProviderStyleStorageCapabilities;
enum class UserProfileSelectionPolicy
{
LastProfile,

View File

@ -728,74 +728,6 @@ Read all custom properties from layer. Properties are stored in a map and saved
.. versionadded:: 3.14
%End
virtual int listStylesInDatabase( QStringList &ids /Out/, QStringList &names /Out/,
QStringList &descriptions /Out/, QString &msgError /Out/ );
%Docstring
Lists all the style in db split into related to the layer and not related to
:param ids: the list in which will be stored the style db ids
:param names: the list in which will be stored the style names
:return: - the number of styles related to current layer (-1 on not implemented)
- descriptions: the list in which will be stored the style descriptions
- msgError: will be set to a descriptive error message if any occurs
.. note::
Since QGIS 3.2 Styles related to the layer are ordered with the default style first then by update time for Postgres, MySQL and Spatialite.
%End
virtual QString getStyleFromDatabase( const QString &styleId, QString &msgError /Out/ );
%Docstring
Returns the named style corresponding to style id provided
%End
virtual bool deleteStyleFromDatabase( const QString &styleId, QString &msgError /Out/ );
%Docstring
Deletes a style from the database
:param styleId: the provider's layer_styles table id of the style to delete
:return: - ``True`` in case of success
- msgError: will be set to a descriptive error message if any occurs
.. versionadded:: 3.0
%End
virtual void saveStyleToDatabase( const QString &name, const QString &description,
bool useAsDefault, const QString &uiFileContent,
QString &msgError /Out/,
QgsMapLayer::StyleCategories categories = QgsMapLayer::AllStyleCategories );
%Docstring
Saves named and sld style of the layer to the style table in the db.
:param name: Style name
:param description: A description of the style
:param useAsDefault: Set to ``True`` if style should be used as the default style for the layer
:param uiFileContent:
:param msgError: will be set to a descriptive error message if any occurs
:param categories: the style categories to be saved.
.. note::
Prior to QGIS 3.24, this method would show a message box warning when a
style with the same ``styleName`` already existed to confirm replacing the style with the user.
Since 3.24, calling this method will ALWAYS overwrite any existing style with the same name.
Use :py:func:`QgsProviderRegistry.styleExists()` to test in advance if a style already exists and handle this appropriately
in your client code.
%End
virtual QString loadNamedStyle( const QString &theURI, bool &resultFlag /Out/, bool loadFromLocalDb,
QgsMapLayer::StyleCategories categories = QgsMapLayer::AllStyleCategories );
%Docstring
Loads a named style from file/local db/datasource db
:param theURI: the URI of the style or the URI of the layer
:param resultFlag: will be set to ``True`` if a named style is correctly loaded
:param loadFromLocalDb: if ``True`` forces to load from local db instead of datasource one
:param categories: the style categories to be loaded.
%End
void removeCustomProperty( const QString &key );

View File

@ -541,6 +541,18 @@ Clear recorded errors
QStringList errors() const;
%Docstring
Gets recorded errors
%End
virtual bool isSaveAndLoadStyleToDatabaseSupported() const;
%Docstring
It returns ``False`` by default.
Must be implemented by providers that support saving and loading styles to db returning ``True``
%End
virtual bool isDeleteStyleFromDatabaseSupported() const;
%Docstring
It returns ``False`` by default.
Must be implemented by providers that support delete styles from db returning ``True``
%End
virtual QgsFeatureRenderer *createRenderer( const QVariantMap &configuration = QVariantMap() ) const /Factory/;

View File

@ -939,6 +939,81 @@ Writes vector layer specific state to project file Dom node.
Resolves references to other layers (kept as layer IDs after reading XML) into layer objects.
.. versionadded:: 3.0
%End
virtual void saveStyleToDatabase( const QString &name, const QString &description,
bool useAsDefault, const QString &uiFileContent,
QString &msgError /Out/,
QgsMapLayer::StyleCategories categories = QgsMapLayer::AllStyleCategories );
%Docstring
Saves named and sld style of the layer to the style table in the db.
:param name: Style name
:param description: A description of the style
:param useAsDefault: Set to ``True`` if style should be used as the default style for the layer
:param uiFileContent:
:param msgError: will be set to a descriptive error message if any occurs
:param categories: the style categories to be saved.
.. note::
Prior to QGIS 3.24, this method would show a message box warning when a
style with the same ``styleName`` already existed to confirm replacing the style with the user.
Since 3.24, calling this method will ALWAYS overwrite any existing style with the same name.
Use :py:func:`QgsProviderRegistry.styleExists()` to test in advance if a style already exists and handle this appropriately
in your client code.
%End
virtual int listStylesInDatabase( QStringList &ids /Out/, QStringList &names /Out/,
QStringList &descriptions /Out/, QString &msgError /Out/ );
%Docstring
Lists all the style in db split into related to the layer and not related to
:param ids: the list in which will be stored the style db ids
:param names: the list in which will be stored the style names
:return: - the number of styles related to current layer (-1 on not implemented)
- descriptions: the list in which will be stored the style descriptions
- msgError: will be set to a descriptive error message if any occurs
.. note::
Since QGIS 3.2 Styles related to the layer are ordered with the default style first then by update time for Postgres, MySQL and Spatialite.
%End
virtual QString getStyleFromDatabase( const QString &styleId, QString &msgError /Out/ );
%Docstring
Returns the named style corresponding to style id provided
%End
virtual bool deleteStyleFromDatabase( const QString &styleId, QString &msgError /Out/ );
%Docstring
Deletes a style from the database
:param styleId: the provider's layer_styles table id of the style to delete
:return: - ``True`` in case of success
- msgError: will be set to a descriptive error message if any occurs
.. versionadded:: 3.0
%End
virtual QString loadNamedStyle( const QString &theURI, bool &resultFlag /Out/, bool loadFromLocalDb,
QgsMapLayer::StyleCategories categories = QgsMapLayer::AllStyleCategories );
%Docstring
Loads a named style from file/local db/datasource db
:param theURI: the URI of the style or the URI of the layer
:param resultFlag: will be set to ``True`` if a named style is correctly loaded
:param loadFromLocalDb: if ``True`` forces to load from local db instead of datasource one
:param categories: the style categories to be loaded.
%End
virtual QString loadNamedStyle( const QString &theURI, bool &resultFlag /Out/,
QgsMapLayer::StyleCategories categories = QgsMapLayer::AllStyleCategories ) ${SIP_FINAL};
%Docstring
Calls loadNamedStyle( theURI, resultFlag, ``False`` );
Retained for backward compatibility
%End
bool loadAuxiliaryLayer( const QgsAuxiliaryStorage &storage, const QString &key = QString() );

View File

@ -27,7 +27,6 @@ Base class for "layer properties" dialogs, containing common utilities for handl
%End
public:
QgsLayerPropertiesDialog( QgsMapLayer *layer, QgsMapCanvas *canvas, const QString &settingsKey, QWidget *parent /TransferThis/ = 0, Qt::WindowFlags fl = Qt::WindowFlags(), QgsSettings *settings = 0 );
%Docstring
Constructor for QgsLayerPropertiesDialog.
@ -50,27 +49,6 @@ This must be called in order for the standard metadata loading/saving functional
virtual void addPropertiesPageFactory( const QgsMapLayerConfigWidgetFactory *factory );
%Docstring
Adds properties page from a ``factory``.
%End
void saveDefaultStyle();
%Docstring
Saves the default style when appropriate button is pressed
.. versionadded:: 3.30
%End
void loadStyle();
%Docstring
Triggers a dialog to load a saved style
.. versionadded:: 3.30
%End
void saveStyleAs();
%Docstring
Saves a style when appriate button is pressed
.. versionadded:: 3.30
%End
public slots:

View File

@ -52,6 +52,21 @@ Saves the default style when appropriate button is pressed
use :py:func:`~QgsRasterLayerProperties.saveStyleAsDefault` instead
%End
void loadStyle() /Deprecated/;
%Docstring
Loads a saved style when appropriate button is pressed
.. deprecated::
use :py:func:`~QgsRasterLayerProperties.loadStyleFromFile` instead.
%End
void saveStyleAs();
%Docstring
Saves a style when appriate button is pressed
.. versionadded:: 3.30
%End
protected slots:
virtual void optionsStackedWidget_CurrentChanged( int index ) ${SIP_FINAL};

View File

@ -25,6 +25,34 @@ class QgsVectorLayerProperties : QgsLayerPropertiesDialog
virtual bool eventFilter( QObject *obj, QEvent *ev );
void loadDefaultStyle();
%Docstring
Loads the default style when appropriate button is pressed
.. versionadded:: 3.30
%End
void saveDefaultStyle();
%Docstring
Saves the default style when appropriate button is pressed
.. versionadded:: 3.30
%End
void loadStyle();
%Docstring
Loads a saved style when appropriate button is pressed
.. versionadded:: 3.30
%End
void saveStyleAs();
%Docstring
Saves a style when appriate button is pressed
.. versionadded:: 3.30
%End
protected slots:
void optionsStackedWidget_CurrentChanged( int index ) final;
virtual void syncToLayer() ${SIP_FINAL};

View File

@ -4202,18 +4202,6 @@ GDALRasterBandH QgsGdalProvider::getBand( int bandNo ) const
return GDALGetRasterBand( mGdalDataset, bandNo );
}
Qgis::ProviderStyleStorageCapabilities QgsGdalProvider::styleStorageCapabilities() const
{
Qgis::ProviderStyleStorageCapabilities storageCapabilities;
if ( isValid() && mDriverName == QLatin1String( "GPKG" ) )
{
storageCapabilities |= Qgis::ProviderStyleStorageCapability::SaveToDatabase;
storageCapabilities |= Qgis::ProviderStyleStorageCapability::LoadFromDatabase;
storageCapabilities |= Qgis::ProviderStyleStorageCapability::DeleteFromDatabase;
}
return storageCapabilities;
}
// pyramids resampling
// see http://www.gdal.org/gdaladdo.html
@ -4552,95 +4540,6 @@ QList<Qgis::LayerType> QgsGdalProviderMetadata::supportedLayerTypes() const
return { Qgis::LayerType::Raster };
}
int QgsGdalProviderMetadata::listStyles( const QString &uri, QStringList &ids, QStringList &names,
QStringList &descriptions, QString &errCause )
{
gdal::dataset_unique_ptr ds;
ds.reset( QgsGdalProviderBase::gdalOpen( uri, GDAL_OF_READONLY ) );
if ( !ds )
{
errCause = QObject::tr( "Cannot open %1." ).arg( uri );
return -1;
}
QVariantMap uriParts = QgsGdalProviderBase::decodeGdalUri( uri );
QString layerName = uriParts["layerName"].toString();
return QgsOgrUtils::listStyles( ds.get(), layerName, "", ids, names, descriptions, errCause );
}
bool QgsGdalProviderMetadata::styleExists( const QString &uri, const QString &styleId, QString &errCause )
{
gdal::dataset_unique_ptr ds;
ds.reset( QgsGdalProviderBase::gdalOpen( uri, GDAL_OF_READONLY ) );
if ( !ds )
{
errCause = QObject::tr( "Cannot open %1." ).arg( uri );
return false;
}
QVariantMap uriParts = QgsGdalProviderBase::decodeGdalUri( uri );
QString layerName = uriParts["layerName"] .toString();
return QgsOgrUtils::styleExists( ds.get(), layerName, "", styleId, errCause );
}
QString QgsGdalProviderMetadata::getStyleById( const QString &uri, const QString &styleId, QString &errCause )
{
gdal::dataset_unique_ptr ds;
ds.reset( QgsGdalProviderBase::gdalOpen( uri, GDAL_OF_READONLY ) );
if ( !ds )
{
errCause = QObject::tr( "Cannot open %1." ).arg( uri );
return QString();
}
return QgsOgrUtils::getStyleById( ds.get(), styleId, errCause );
}
bool QgsGdalProviderMetadata::deleteStyleById( const QString &uri, const QString &styleId, QString &errCause )
{
gdal::dataset_unique_ptr ds;
ds.reset( QgsGdalProviderBase::gdalOpen( uri, GDAL_OF_READONLY ) );
if ( !ds )
{
errCause = QObject::tr( "Cannot open %1." ).arg( uri );
return false;
}
return QgsOgrUtils::deleteStyleById( ds.get(), styleId, errCause );
}
bool QgsGdalProviderMetadata::saveStyle( const QString &uri, const QString &qmlStyle, const QString &sldStyle,
const QString &styleName, const QString &styleDescription,
const QString &uiFileContent, bool useAsDefault, QString &errCause )
{
gdal::dataset_unique_ptr ds;
ds.reset( QgsGdalProviderBase::gdalOpen( uri, GDAL_OF_UPDATE ) );
if ( !ds )
{
errCause = QObject::tr( "Cannot open %1." ).arg( uri );
return false;
}
QVariantMap uriParts = QgsGdalProviderBase::decodeGdalUri( uri );
QString layerName = uriParts["layerName"].toString();
return QgsOgrUtils::saveStyle( ds.get(), layerName, "", qmlStyle, sldStyle, styleName, styleDescription, uiFileContent, useAsDefault, errCause );
}
QString QgsGdalProviderMetadata::loadStyle( const QString &uri, QString &errCause )
{
QString name;
return loadStoredStyle( uri, name, errCause );
}
QString QgsGdalProviderMetadata::loadStoredStyle( const QString &uri, QString &styleName, QString &errCause )
{
gdal::dataset_unique_ptr ds;
ds.reset( QgsGdalProviderBase::gdalOpen( uri, GDAL_OF_READONLY ) );
if ( !ds )
{
errCause = QObject::tr( "Cannot open %1." ).arg( uri );
return QString();
}
QVariantMap uriParts = QgsGdalProviderBase::decodeGdalUri( uri );
QString layerName = uriParts["layerName"].toString();
return QgsOgrUtils::loadStoredStyle( ds.get(), layerName, "", styleName, errCause );
}
QgsGdalProviderMetadata::QgsGdalProviderMetadata():
QgsProviderMetadata( PROVIDER_KEY, PROVIDER_DESCRIPTION )
{

View File

@ -218,8 +218,6 @@ class QgsGdalProvider final: public QgsRasterDataProvider, QgsGdalProviderBase
bool setZoomedOutResamplingMethod( ResamplingMethod method ) override { mZoomedOutResamplingMethod = method; return true; }
bool setMaxOversampling( double factor ) override { mMaxOversampling = factor; return true; }
Qgis::ProviderStyleStorageCapabilities styleStorageCapabilities() const override;
private:
QgsGdalProvider( const QgsGdalProvider &other );
QgsGdalProvider &operator=( const QgsGdalProvider & ) = delete;
@ -404,17 +402,6 @@ class QgsGdalProviderMetadata final: public QgsProviderMetadata
QList< QgsProviderSublayerDetails > querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags = Qgis::SublayerQueryFlags(), QgsFeedback *feedback = nullptr ) const override;
QStringList sidecarFilesForUri( const QString &uri ) const override;
QList< Qgis::LayerType > supportedLayerTypes() const override;
int listStyles( const QString &uri, QStringList &ids, QStringList &names,
QStringList &descriptions, QString &errCause ) override;
bool styleExists( const QString &uri, const QString &styleId, QString &errCause SIP_OUT ) override;
QString getStyleById( const QString &uri, const QString &styleId, QString &errCause ) override;
bool deleteStyleById( const QString &uri, const QString &styleId, QString &errCause ) override;
bool saveStyle( const QString &uri, const QString &qmlStyle, const QString &sldStyle,
const QString &styleName, const QString &styleDescription,
const QString &uiFileContent, bool useAsDefault, QString &errCause ) override;
QString loadStyle( const QString &uri, QString &errCause ) override;
QString loadStoredStyle( const QString &uri, QString &styleName, QString &errCause ) override;
};
///@endcond

View File

@ -4083,16 +4083,17 @@ bool QgsOgrProvider::leaveUpdateMode()
return true;
}
Qgis::ProviderStyleStorageCapabilities QgsOgrProvider::styleStorageCapabilities() const
bool QgsOgrProvider::isSaveAndLoadStyleToDatabaseSupported() const
{
Qgis::ProviderStyleStorageCapabilities storageCapabilities;
if ( isValid() && ( mGDALDriverName == QLatin1String( "GPKG" ) || mGDALDriverName == QLatin1String( "SQLite" ) ) )
{
storageCapabilities |= Qgis::ProviderStyleStorageCapability::SaveToDatabase;
storageCapabilities |= Qgis::ProviderStyleStorageCapability::LoadFromDatabase;
storageCapabilities |= Qgis::ProviderStyleStorageCapability::DeleteFromDatabase;
}
return storageCapabilities;
// We could potentially extend support for styling to other drivers
// with multiple layer support.
return mGDALDriverName == QLatin1String( "GPKG" ) ||
mGDALDriverName == QLatin1String( "SQLite" );
}
bool QgsOgrProvider::isDeleteStyleFromDatabaseSupported() const
{
return isSaveAndLoadStyleToDatabaseSupported();
}
QString QgsOgrProvider::fileVectorFilters() const

View File

@ -115,7 +115,8 @@ class QgsOgrProvider final: public QgsVectorDataProvider
void setEncoding( const QString &e ) override;
bool enterUpdateMode() override { return _enterUpdateMode(); }
bool leaveUpdateMode() override;
Qgis::ProviderStyleStorageCapabilities styleStorageCapabilities() const override;
bool isSaveAndLoadStyleToDatabaseSupported() const override;
bool isDeleteStyleFromDatabaseSupported() const override;
QString fileVectorFilters() const override;
bool isValid() const override;
QVariant minimumValue( int index ) const override;

View File

@ -339,7 +339,7 @@ QList<QgsDataItemProvider *> QgsOgrProviderMetadata::dataItemProviders() const
return providers;
}
static QgsOgrLayerUniquePtr LoadDataSourceAndLayer( const QString &uri, bool updateMode, QString &filePath, QString &errCause )
static QgsOgrLayerUniquePtr LoadDataSourceAndLayer( const QString &uri, QString &errCause )
{
bool isSubLayer;
int layerIndex;
@ -347,87 +347,344 @@ static QgsOgrLayerUniquePtr LoadDataSourceAndLayer( const QString &uri, bool upd
QString subsetString;
OGRwkbGeometryType ogrGeometryType;
QStringList openOptions;
filePath = QgsOgrProviderUtils::analyzeURI( uri,
isSubLayer,
layerIndex,
layerName,
subsetString,
ogrGeometryType,
openOptions );
QString filePath = QgsOgrProviderUtils::analyzeURI( uri,
isSubLayer,
layerIndex,
layerName,
subsetString,
ogrGeometryType,
openOptions );
if ( updateMode )
if ( !layerName.isEmpty() )
{
if ( !layerName.isEmpty() )
{
return QgsOgrProviderUtils::getLayer( filePath, true, QStringList(), layerName, errCause, true );
}
else
{
return QgsOgrProviderUtils::getLayer( filePath, true, QStringList(), layerIndex, errCause, true );
}
return QgsOgrProviderUtils::getLayer( filePath, true, QStringList(), layerName, errCause, true );
}
else
{
if ( !layerName.isEmpty() )
{
return QgsOgrProviderUtils::getLayer( filePath, layerName, errCause );
}
else
{
return QgsOgrProviderUtils::getLayer( filePath, layerIndex, errCause );
}
return QgsOgrProviderUtils::getLayer( filePath, true, QStringList(), layerIndex, errCause, true );
}
}
bool QgsOgrProviderMetadata::styleExists( const QString &uri, const QString &styleId, QString &errorCause )
{
QString filePath;
QgsOgrLayerUniquePtr userLayer = LoadDataSourceAndLayer( uri, false, filePath, errorCause );
errorCause.clear();
QgsOgrLayerUniquePtr userLayer = LoadDataSourceAndLayer( uri, errorCause );
if ( !userLayer )
return false;
QRecursiveMutex *mutex = nullptr;
OGRLayerH hUserLayer = userLayer->getHandleAndMutex( mutex );
GDALDatasetH hDS = userLayer->getDatasetHandleAndMutex( mutex );
QMutexLocker locker( mutex );
QString layerName = QString( OGR_L_GetName( hUserLayer ) );
QString geomColumn = QString( OGR_L_GetGeometryColumn( hUserLayer ) );
return QgsOgrUtils::styleExists( hDS, layerName, geomColumn, styleId, errorCause );
// check if layer_styles table exists
OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
if ( !hLayer )
return false;
const QString realStyleId = styleId.isEmpty() ? QString( OGR_L_GetName( hUserLayer ) ) : styleId;
const QString checkQuery = QStringLiteral( "f_table_schema=''"
" AND f_table_name=%1"
" AND f_geometry_column=%2"
" AND styleName=%3" )
.arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetName( hUserLayer ) ) ),
QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetGeometryColumn( hUserLayer ) ) ),
QgsOgrProviderUtils::quotedValue( realStyleId ) );
OGR_L_SetAttributeFilter( hLayer, checkQuery.toUtf8().constData() );
OGR_L_ResetReading( hLayer );
gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
OGR_L_ResetReading( hLayer );
if ( hFeature )
return true;
return false;
}
bool QgsOgrProviderMetadata::saveStyle(
const QString &uri, const QString &qmlStyle, const QString &sldStyle,
const QString &styleName, const QString &styleDescription,
const QString &uiFileContent, bool useAsDefault, QString &errCause )
{
QString filePath;
QgsOgrLayerUniquePtr userLayer = LoadDataSourceAndLayer( uri, true, filePath, errCause );
QgsOgrLayerUniquePtr userLayer = LoadDataSourceAndLayer( uri, errCause );
if ( !userLayer )
return false;
QRecursiveMutex *mutex = nullptr;
OGRLayerH hUserLayer = userLayer->getHandleAndMutex( mutex );
GDALDatasetH hDS = userLayer->getDatasetHandleAndMutex( mutex );
QMutexLocker locker( mutex );
QString layerName = QString( OGR_L_GetName( hUserLayer ) );
QString geomColumn = QString( OGR_L_GetGeometryColumn( hUserLayer ) );
return QgsOgrUtils::saveStyle( hDS, layerName, geomColumn, qmlStyle, sldStyle, styleName, styleDescription, uiFileContent, useAsDefault, errCause );
// check if layer_styles table already exist
OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
if ( !hLayer )
{
// if not create it
// Note: we use the same schema as in the SpatiaLite and postgres providers
//for cross interoperability
char **options = nullptr;
// TODO: might need change if other drivers than GPKG / SQLite
options = CSLSetNameValue( options, "FID", "id" );
hLayer = GDALDatasetCreateLayer( hDS, "layer_styles", nullptr, wkbNone, options );
QgsOgrProviderUtils::invalidateCachedDatasets( QString::fromUtf8( GDALGetDescription( hDS ) ) );
CSLDestroy( options );
if ( !hLayer )
{
errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database." );
return false;
}
bool ok = true;
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_table_catalog", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 256 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_table_schema", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 256 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_table_name", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 256 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_geometry_column", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 256 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "styleName", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 30 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "styleQML", OFTString ) );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "styleSLD", OFTString ) );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "useAsDefault", OFTInteger ) );
OGR_Fld_SetSubType( fld.get(), OFSTBoolean );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "description", OFTString ) );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "owner", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 30 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "ui", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 30 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "update_time", OFTDateTime ) );
OGR_Fld_SetDefault( fld.get(), "CURRENT_TIMESTAMP" );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
if ( !ok )
{
errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database." );
return false;
}
}
QString realStyleName =
styleName.isEmpty() ? QString( OGR_L_GetName( hUserLayer ) ) : styleName;
OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
if ( useAsDefault )
{
QString oldDefaultQuery = QStringLiteral( "useAsDefault = 1 AND f_table_schema=''"
" AND f_table_name=%1"
" AND f_geometry_column=%2" )
.arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetName( hUserLayer ) ) ) )
.arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetGeometryColumn( hUserLayer ) ) ) );
OGR_L_SetAttributeFilter( hLayer, oldDefaultQuery.toUtf8().constData() );
gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
if ( hFeature )
{
OGR_F_SetFieldInteger( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "useAsDefault" ),
0 );
bool ok = OGR_L_SetFeature( hLayer, hFeature.get() ) == 0;
if ( !ok )
{
QgsDebugError( QStringLiteral( "Could not unset previous useAsDefault style" ) );
}
}
}
QString checkQuery = QStringLiteral( "f_table_schema=''"
" AND f_table_name=%1"
" AND f_geometry_column=%2"
" AND styleName=%3" )
.arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetName( hUserLayer ) ) ) )
.arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetGeometryColumn( hUserLayer ) ) ) )
.arg( QgsOgrProviderUtils::quotedValue( realStyleName ) );
OGR_L_SetAttributeFilter( hLayer, checkQuery.toUtf8().constData() );
OGR_L_ResetReading( hLayer );
gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
OGR_L_ResetReading( hLayer );
bool bNew = true;
if ( hFeature )
{
bNew = false;
}
else
{
hFeature.reset( OGR_F_Create( hLayerDefn ) );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "f_table_catalog" ),
"" );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "f_table_schema" ),
"" );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "f_table_name" ),
OGR_L_GetName( hUserLayer ) );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "f_geometry_column" ),
OGR_L_GetGeometryColumn( hUserLayer ) );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ),
realStyleName.toUtf8().constData() );
if ( !uiFileContent.isEmpty() )
{
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "ui" ),
uiFileContent.toUtf8().constData() );
}
}
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ),
qmlStyle.toUtf8().constData() );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "styleSLD" ),
sldStyle.toUtf8().constData() );
OGR_F_SetFieldInteger( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "useAsDefault" ),
useAsDefault ? 1 : 0 );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "description" ),
( styleDescription.isEmpty() ? QDateTime::currentDateTime().toString() : styleDescription ).toUtf8().constData() );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "owner" ),
"" );
bool bFeatureOK;
if ( bNew )
bFeatureOK = OGR_L_CreateFeature( hLayer, hFeature.get() ) == OGRERR_NONE;
else
bFeatureOK = OGR_L_SetFeature( hLayer, hFeature.get() ) == OGRERR_NONE;
if ( !bFeatureOK )
{
QgsMessageLog::logMessage( QObject::tr( "Error updating style" ) );
errCause = QObject::tr( "Error looking for style. The query was logged" );
return false;
}
return true;
}
bool QgsOgrProviderMetadata::deleteStyleById( const QString &uri, const QString &styleId, QString &errCause )
{
QString filePath;
QgsOgrLayerUniquePtr userLayer = LoadDataSourceAndLayer( uri, false, filePath, errCause );
QgsDataSourceUri dsUri( uri );
bool deleted;
QgsOgrLayerUniquePtr userLayer = LoadDataSourceAndLayer( uri, errCause );
if ( !userLayer )
return false;
QRecursiveMutex *mutex = nullptr;
GDALDatasetH hDS = userLayer->getDatasetHandleAndMutex( mutex );
QMutexLocker locker( mutex );
return QgsOgrUtils::deleteStyleById( hDS, styleId, errCause );
// check if layer_styles table already exist
OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
if ( !hLayer )
{
errCause = QObject::tr( "Connection to database failed: %1" ).arg( dsUri.uri() );
deleted = false;
}
else
{
if ( OGR_L_DeleteFeature( hLayer, styleId.toInt() ) != OGRERR_NONE )
{
errCause = QObject::tr( "Error executing the delete query." );
deleted = false;
}
else
{
deleted = true;
}
}
return deleted;
}
static
bool LoadDataSourceLayerStylesAndLayer( const QString &uri,
QgsOgrLayerUniquePtr &layerStyles,
QgsOgrLayerUniquePtr &userLayer,
QString &errCause )
{
bool isSubLayer;
int layerIndex;
QString layerName;
QString subsetString;
OGRwkbGeometryType ogrGeometryType;
QStringList openOptions;
QString filePath = QgsOgrProviderUtils::analyzeURI( uri,
isSubLayer,
layerIndex,
layerName,
subsetString,
ogrGeometryType,
openOptions );
layerStyles =
QgsOgrProviderUtils::getLayer( filePath, "layer_styles", errCause );
userLayer = nullptr;
if ( !layerStyles )
{
errCause = QObject::tr( "Cannot find layer_styles layer" );
return false;
}
if ( !layerName.isEmpty() )
{
userLayer = QgsOgrProviderUtils::getLayer( filePath, layerName, errCause );
}
else
{
userLayer = QgsOgrProviderUtils::getLayer( filePath, layerIndex, errCause );
}
if ( !userLayer )
{
layerStyles.reset();
return false;
}
return true;
}
QString QgsOgrProviderMetadata::loadStyle( const QString &uri, QString &errCause )
{
QString name;
@ -436,53 +693,230 @@ QString QgsOgrProviderMetadata::loadStyle( const QString &uri, QString &errCause
QString QgsOgrProviderMetadata::loadStoredStyle( const QString &uri, QString &styleName, QString &errCause )
{
QString filePath;
QgsOgrLayerUniquePtr userLayer = LoadDataSourceAndLayer( uri, false, filePath, errCause );
if ( !userLayer )
QgsOgrLayerUniquePtr layerStyles;
QgsOgrLayerUniquePtr userLayer;
if ( !LoadDataSourceLayerStylesAndLayer( uri, layerStyles, userLayer, errCause ) )
{
return QString();
}
QRecursiveMutex *mutex = nullptr;
OGRLayerH hUserLayer = userLayer->getHandleAndMutex( mutex );
GDALDatasetH hDS = userLayer->getDatasetHandleAndMutex( mutex );
QMutexLocker lock( mutex );
QString layerName = QString( OGR_L_GetName( hUserLayer ) );
QString geomColumn = QString( OGR_L_GetGeometryColumn( hUserLayer ) );
QRecursiveMutex *mutex1 = nullptr;
QRecursiveMutex *mutex2 = nullptr;
return QgsOgrUtils::loadStoredStyle( hDS, layerName, geomColumn, styleName, errCause );
OGRLayerH hLayer = layerStyles->getHandleAndMutex( mutex1 );
OGRLayerH hUserLayer = userLayer->getHandleAndMutex( mutex2 );
QMutexLocker lock1( mutex1 );
QMutexLocker lock2( mutex2 );
QString selectQmlQuery = QStringLiteral( "f_table_schema=''"
" AND f_table_name=%1"
" AND f_geometry_column=%2"
" ORDER BY CASE WHEN useAsDefault THEN 1 ELSE 2 END"
",update_time DESC LIMIT 1" )
.arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetName( hUserLayer ) ) ) )
.arg( QgsOgrProviderUtils::quotedValue( QString( OGR_L_GetGeometryColumn( hUserLayer ) ) ) );
OGR_L_SetAttributeFilter( hLayer, selectQmlQuery.toUtf8().constData() );
OGR_L_ResetReading( hLayer );
OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
QString styleQML;
qlonglong moreRecentTimestamp = 0;
while ( true )
{
gdal::ogr_feature_unique_ptr hFeat( OGR_L_GetNextFeature( hLayer ) );
if ( !hFeat )
break;
if ( OGR_F_GetFieldAsInteger( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "useAsDefault" ) ) )
{
styleQML = QString::fromUtf8(
OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ) ) );
styleName = QString::fromUtf8(
OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ) ) );
break;
}
int year, month, day, hour, minute, second, TZ;
OGR_F_GetFieldAsDateTime( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "update_time" ),
&year, &month, &day, &hour, &minute, &second, &TZ );
qlonglong ts = second + minute * 60 + hour * 3600 + day * 24 * 3600 +
static_cast<qlonglong>( month ) * 31 * 24 * 3600 + static_cast<qlonglong>( year ) * 12 * 31 * 24 * 3600;
if ( ts > moreRecentTimestamp )
{
moreRecentTimestamp = ts;
styleQML = QString::fromUtf8(
OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ) ) );
styleName = QString::fromUtf8(
OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ) ) );
}
}
OGR_L_ResetReading( hLayer );
return styleQML;
}
int QgsOgrProviderMetadata::listStyles(
const QString &uri, QStringList &ids, QStringList &names,
QStringList &descriptions, QString &errCause )
{
QString filePath;
QgsOgrLayerUniquePtr userLayer = LoadDataSourceAndLayer( uri, false, filePath, errCause );
bool isSubLayer;
int layerIndex;
QString layerName;
QString subsetString;
OGRwkbGeometryType ogrGeometryType;
QStringList openOptions;
QString filePath = QgsOgrProviderUtils::analyzeURI( uri,
isSubLayer,
layerIndex,
layerName,
subsetString,
ogrGeometryType,
openOptions );
QgsOgrLayerUniquePtr userLayer;
if ( !layerName.isEmpty() )
{
userLayer = QgsOgrProviderUtils::getLayer( filePath, layerName, errCause );
}
else
{
userLayer = QgsOgrProviderUtils::getLayer( filePath, layerIndex, errCause );
}
if ( !userLayer )
{
return -1;
}
QRecursiveMutex *mutex = nullptr;
OGRLayerH hUserLayer = userLayer->getHandleAndMutex( mutex );
GDALDatasetH hDS = userLayer->getDatasetHandleAndMutex( mutex );
Q_UNUSED( hDS );
QMutexLocker locker( mutex );
QString layerName = QString( OGR_L_GetName( hUserLayer ) );
QString geomColumn = QString( OGR_L_GetGeometryColumn( hUserLayer ) );
QgsOgrLayerUniquePtr layerStyles =
QgsOgrProviderUtils::getLayer( filePath, "layer_styles", errCause );
if ( !layerStyles )
{
QgsMessageLog::logMessage( QObject::tr( "No styles available on DB" ) );
errCause = QObject::tr( "No styles available on DB" );
return 0;
}
return QgsOgrUtils::listStyles( hDS, layerName, geomColumn, ids, names, descriptions, errCause );
QRecursiveMutex *mutex1 = nullptr;
QRecursiveMutex *mutex2 = nullptr;
OGRLayerH hLayer = layerStyles->getHandleAndMutex( mutex1 );
QMutexLocker lock1( mutex1 );
OGRLayerH hUserLayer = userLayer->getHandleAndMutex( mutex2 );
QMutexLocker lock2( mutex2 );
if ( OGR_L_GetFeatureCount( hLayer, TRUE ) == 0 )
{
QgsMessageLog::logMessage( QObject::tr( "No styles available on DB" ) );
errCause = QObject::tr( "No styles available on DB" );
return 0;
}
OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
OGR_L_ResetReading( hLayer );
QList<qlonglong> listTimestamp;
QMap<int, QString> mapIdToStyleName;
QMap<int, QString> mapIdToDescription;
QMap<qlonglong, QList<int> > mapTimestampToId;
int numberOfRelatedStyles = 0;
while ( true )
{
gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
if ( !hFeature )
break;
QString tableName( QString::fromUtf8(
OGR_F_GetFieldAsString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "f_table_name" ) ) ) );
QString geometryColumn( QString::fromUtf8(
OGR_F_GetFieldAsString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "f_geometry_column" ) ) ) );
QString styleName( QString::fromUtf8(
OGR_F_GetFieldAsString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ) ) ) );
QString description( QString::fromUtf8(
OGR_F_GetFieldAsString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "description" ) ) ) );
int fid = static_cast<int>( OGR_F_GetFID( hFeature.get() ) );
if ( tableName == QString::fromUtf8( OGR_L_GetName( hUserLayer ) ) &&
geometryColumn == QString::fromUtf8( OGR_L_GetGeometryColumn( hUserLayer ) ) )
{
// Append first all related styles
QString id( QString::number( fid ) );
ids.append( id );
names.append( styleName );
descriptions.append( description );
++ numberOfRelatedStyles;
}
else
{
int year, month, day, hour, minute, second, TZ;
OGR_F_GetFieldAsDateTime( hFeature.get(), OGR_FD_GetFieldIndex( hLayerDefn, "update_time" ),
&year, &month, &day, &hour, &minute, &second, &TZ );
const qlonglong ts = second + minute * 60 + hour * 3600 + day * 24 * 3600 +
static_cast<qlonglong>( month ) * 31 * 24 * 3600 + static_cast<qlonglong>( year ) * 12 * 31 * 24 * 3600;
listTimestamp.append( ts );
mapIdToStyleName[fid] = styleName;
mapIdToDescription[fid] = description;
mapTimestampToId[ts].append( fid );
}
}
std::sort( listTimestamp.begin(), listTimestamp.end() );
// Sort from most recent to least recent
for ( int i = listTimestamp.size() - 1; i >= 0; i-- )
{
const QList<int> &listId = mapTimestampToId[listTimestamp[i]];
for ( int j = 0; j < listId.size(); j++ )
{
int fid = listId[j];
QString id( QString::number( fid ) );
ids.append( id );
names.append( mapIdToStyleName[fid] );
descriptions.append( mapIdToDescription[fid] );
}
}
return numberOfRelatedStyles;
}
QString QgsOgrProviderMetadata::getStyleById( const QString &uri, const QString &styleId, QString &errCause )
{
QString filePath;
QgsOgrLayerUniquePtr userLayer = LoadDataSourceAndLayer( uri, false, filePath, errCause );
if ( !userLayer )
QgsOgrLayerUniquePtr layerStyles;
QgsOgrLayerUniquePtr userLayer;
if ( !LoadDataSourceLayerStylesAndLayer( uri, layerStyles, userLayer, errCause ) )
{
return QString();
}
QRecursiveMutex *mutex = nullptr;
GDALDatasetH hDS = userLayer->getDatasetHandleAndMutex( mutex );
QMutexLocker locker( mutex );
QRecursiveMutex *mutex1 = nullptr;
return QgsOgrUtils::getStyleById( hDS, styleId, errCause );
OGRLayerH hLayer = layerStyles->getHandleAndMutex( mutex1 );
QMutexLocker lock1( mutex1 );
bool ok;
int id = styleId.toInt( &ok );
if ( !ok )
{
errCause = QObject::tr( "Invalid style identifier" );
return QString();
}
gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetFeature( hLayer, id ) );
if ( !hFeature )
{
errCause = QObject::tr( "No style corresponding to style identifier" );
return QString();
}
OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
QString styleQML( QString::fromUtf8(
OGR_F_GetFieldAsString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ) ) ) );
OGR_L_ResetReading( hLayer );
return styleQML;
}
bool QgsOgrProviderMetadata::saveLayerMetadata( const QString &uri, const QgsLayerMetadata &metadata, QString &errorMessage )

View File

@ -134,8 +134,3 @@ QString QgsDataProvider::sublayerSeparator()
{
return SUBLAYER_SEPARATOR;
}
Qgis::ProviderStyleStorageCapabilities QgsDataProvider::styleStorageCapabilities() const
{
return Qgis::ProviderStyleStorageCapabilities();
}

View File

@ -633,13 +633,6 @@ class CORE_EXPORT QgsDataProvider : public QObject
*/
static QString sublayerSeparator();
/**
* Returns the style storage capabilities.
*
* \since QGIS 3.34
*/
virtual Qgis::ProviderStyleStorageCapabilities styleStorageCapabilities() const;
signals:
/**

View File

@ -3690,21 +3690,6 @@ class CORE_EXPORT Qgis
Q_DECLARE_FLAGS( DatabaseProviderConnectionCapabilities2, DatabaseProviderConnectionCapability2 )
Q_FLAG( DatabaseProviderConnectionCapabilities2 )
/**
* The StorageCapability enum represents the style storage operations supported by the provider.
*
* \since QGIS 3.34
*/
enum class ProviderStyleStorageCapability
{
SaveToDatabase = 1 << 1,
LoadFromDatabase = 1 << 2,
DeleteFromDatabase = 1 << 3
};
Q_ENUM( ProviderStyleStorageCapability );
Q_DECLARE_FLAGS( ProviderStyleStorageCapabilities, ProviderStyleStorageCapability )
Q_FLAG( ProviderStyleStorageCapabilities )
/**
* User profile selection policy.
*

View File

@ -1365,7 +1365,7 @@ QString QgsMapLayer::loadNamedStyle( const QString &uri, bool &resultFlag, QgsMa
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
return loadNamedStyle( uri, resultFlag, false, categories );
return loadNamedProperty( uri, PropertyType::Style, resultFlag, categories );
}
QString QgsMapLayer::loadNamedProperty( const QString &uri, QgsMapLayer::PropertyType type, bool &resultFlag, StyleCategories categories )
@ -2350,90 +2350,6 @@ void QgsMapLayer::removeCustomProperty( const QString &key )
}
}
int QgsMapLayer::listStylesInDatabase( QStringList &ids, QStringList &names, QStringList &descriptions, QString &msgError )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
return QgsProviderRegistry::instance()->listStyles( mProviderKey, mDataSource, ids, names, descriptions, msgError );
}
QString QgsMapLayer::getStyleFromDatabase( const QString &styleId, QString &msgError )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
return QgsProviderRegistry::instance()->getStyleById( mProviderKey, mDataSource, styleId, msgError );
}
bool QgsMapLayer::deleteStyleFromDatabase( const QString &styleId, QString &msgError )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
return QgsProviderRegistry::instance()->deleteStyleById( mProviderKey, mDataSource, styleId, msgError );
}
void QgsMapLayer::saveStyleToDatabase( const QString &name, const QString &description,
bool useAsDefault, const QString &uiFileContent, QString &msgError, QgsMapLayer::StyleCategories categories )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
QString sldStyle, qmlStyle;
QDomDocument qmlDocument, sldDocument;
QgsReadWriteContext context;
exportNamedStyle( qmlDocument, msgError, context, categories );
if ( !msgError.isNull() )
{
return;
}
qmlStyle = qmlDocument.toString();
this->exportSldStyle( sldDocument, msgError );
if ( !msgError.isNull() )
{
return;
}
sldStyle = sldDocument.toString();
QgsProviderRegistry::instance()->saveStyle( mProviderKey,
mDataSource, qmlStyle, sldStyle, name,
description, uiFileContent, useAsDefault, msgError );
}
QString QgsMapLayer::loadNamedStyle( const QString &theURI, bool &resultFlag, bool loadFromLocalDB, QgsMapLayer::StyleCategories categories )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
QString returnMessage;
QString qml, errorMsg;
QString styleName;
if ( !loadFromLocalDB && dataProvider() && dataProvider()->styleStorageCapabilities().testFlag( Qgis::ProviderStyleStorageCapability::LoadFromDatabase ) )
{
qml = QgsProviderRegistry::instance()->loadStoredStyle( mProviderKey, mDataSource, styleName, errorMsg );
}
// Style was successfully loaded from provider storage
if ( !qml.isEmpty() )
{
QDomDocument myDocument( QStringLiteral( "qgis" ) );
myDocument.setContent( qml );
resultFlag = importNamedStyle( myDocument, errorMsg );
returnMessage = QObject::tr( "Loaded from Provider" );
}
else
{
returnMessage = loadNamedProperty( theURI, PropertyType::Style, resultFlag, categories );
}
if ( ! styleName.isEmpty() )
{
styleManager()->renameStyle( styleManager()->currentStyle(), styleName );
}
if ( resultFlag )
emit styleLoaded( categories );
return returnMessage;
}
QgsError QgsMapLayer::error() const
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS

View File

@ -722,63 +722,6 @@ class CORE_EXPORT QgsMapLayer : public QObject
*/
const QgsObjectCustomProperties &customProperties() const;
/**
* Lists all the style in db split into related to the layer and not related to
* \param ids the list in which will be stored the style db ids
* \param names the list in which will be stored the style names
* \param descriptions the list in which will be stored the style descriptions
* \param msgError will be set to a descriptive error message if any occurs
* \returns the number of styles related to current layer (-1 on not implemented)
* \note Since QGIS 3.2 Styles related to the layer are ordered with the default style first then by update time for Postgres, MySQL and Spatialite.
*/
virtual int listStylesInDatabase( QStringList &ids SIP_OUT, QStringList &names SIP_OUT,
QStringList &descriptions SIP_OUT, QString &msgError SIP_OUT );
/**
* Returns the named style corresponding to style id provided
*/
virtual QString getStyleFromDatabase( const QString &styleId, QString &msgError SIP_OUT );
/**
* Deletes a style from the database
* \param styleId the provider's layer_styles table id of the style to delete
* \param msgError will be set to a descriptive error message if any occurs
* \returns TRUE in case of success
* \since QGIS 3.0
*/
virtual bool deleteStyleFromDatabase( const QString &styleId, QString &msgError SIP_OUT );
/**
* Saves named and sld style of the layer to the style table in the db.
* \param name Style name
* \param description A description of the style
* \param useAsDefault Set to TRUE if style should be used as the default style for the layer
* \param uiFileContent
* \param msgError will be set to a descriptive error message if any occurs
* \param categories the style categories to be saved.
*
*
* \note Prior to QGIS 3.24, this method would show a message box warning when a
* style with the same \a styleName already existed to confirm replacing the style with the user.
* Since 3.24, calling this method will ALWAYS overwrite any existing style with the same name.
* Use QgsProviderRegistry::styleExists() to test in advance if a style already exists and handle this appropriately
* in your client code.
*/
virtual void saveStyleToDatabase( const QString &name, const QString &description,
bool useAsDefault, const QString &uiFileContent,
QString &msgError SIP_OUT,
QgsMapLayer::StyleCategories categories = QgsMapLayer::AllStyleCategories );
/**
* Loads a named style from file/local db/datasource db
* \param theURI the URI of the style or the URI of the layer
* \param resultFlag will be set to TRUE if a named style is correctly loaded
* \param loadFromLocalDb if TRUE forces to load from local db instead of datasource one
* \param categories the style categories to be loaded.
*/
virtual QString loadNamedStyle( const QString &theURI, bool &resultFlag SIP_OUT, bool loadFromLocalDb,
QgsMapLayer::StyleCategories categories = QgsMapLayer::AllStyleCategories );
#ifndef SIP_RUN
/**

View File

@ -2666,428 +2666,3 @@ gdal::relationship_unique_ptr QgsOgrUtils::convertRelationship( const QgsWeakRel
return relationH;
}
#endif
int QgsOgrUtils::listStyles( GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, QStringList &ids, QStringList &names, QStringList &descriptions, QString &errCause )
{
OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
if ( !hLayer )
{
QgsMessageLog::logMessage( QObject::tr( "No styles available on DB" ) );
errCause = QObject::tr( "No styles available on DB" );
return 0;
}
if ( OGR_L_GetFeatureCount( hLayer, TRUE ) == 0 )
{
QgsMessageLog::logMessage( QObject::tr( "No styles available on DB" ) );
errCause = QObject::tr( "No styles available on DB" );
return 0;
}
OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
OGR_L_ResetReading( hLayer );
QList<qlonglong> listTimestamp;
QMap<int, QString> mapIdToStyleName;
QMap<int, QString> mapIdToDescription;
QMap<qlonglong, QList<int> > mapTimestampToId;
int numberOfRelatedStyles = 0;
while ( true )
{
gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
if ( !hFeature )
break;
QString tableName( QString::fromUtf8(
OGR_F_GetFieldAsString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "f_table_name" ) ) ) );
QString geometryColumn( QString::fromUtf8(
OGR_F_GetFieldAsString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "f_geometry_column" ) ) ) );
QString styleName( QString::fromUtf8(
OGR_F_GetFieldAsString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ) ) ) );
QString description( QString::fromUtf8(
OGR_F_GetFieldAsString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "description" ) ) ) );
int fid = static_cast<int>( OGR_F_GetFID( hFeature.get() ) );
if ( tableName == layerName &&
geometryColumn == geomColumn )
{
// Append first all related styles
QString id( QString::number( fid ) );
ids.append( id );
names.append( styleName );
descriptions.append( description );
++ numberOfRelatedStyles;
}
else
{
int year, month, day, hour, minute, second, TZ;
OGR_F_GetFieldAsDateTime( hFeature.get(), OGR_FD_GetFieldIndex( hLayerDefn, "update_time" ),
&year, &month, &day, &hour, &minute, &second, &TZ );
const qlonglong ts = second + minute * 60 + hour * 3600 + day * 24 * 3600 +
static_cast<qlonglong>( month ) * 31 * 24 * 3600 + static_cast<qlonglong>( year ) * 12 * 31 * 24 * 3600;
listTimestamp.append( ts );
mapIdToStyleName[fid] = styleName;
mapIdToDescription[fid] = description;
mapTimestampToId[ts].append( fid );
}
}
std::sort( listTimestamp.begin(), listTimestamp.end() );
// Sort from most recent to least recent
for ( int i = listTimestamp.size() - 1; i >= 0; i-- )
{
const QList<int> &listId = mapTimestampToId[listTimestamp[i]];
for ( int j = 0; j < listId.size(); j++ )
{
int fid = listId[j];
QString id( QString::number( fid ) );
ids.append( id );
names.append( mapIdToStyleName[fid] );
descriptions.append( mapIdToDescription[fid] );
}
}
return numberOfRelatedStyles;
}
bool QgsOgrUtils::styleExists( GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, const QString &styleId, QString &errorCause )
{
errorCause.clear();
// check if layer_styles table exists
OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
if ( !hLayer )
return false;
const QString realStyleId = styleId.isEmpty() ? layerName : styleId;
const QString checkQuery = QStringLiteral( "f_table_schema=''"
" AND f_table_name=%1"
" AND f_geometry_column=%2"
" AND styleName=%3" )
.arg( QgsOgrProviderUtils::quotedValue( layerName ),
QgsOgrProviderUtils::quotedValue( geomColumn ),
QgsOgrProviderUtils::quotedValue( realStyleId ) );
OGR_L_SetAttributeFilter( hLayer, checkQuery.toUtf8().constData() );
OGR_L_ResetReading( hLayer );
gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
OGR_L_ResetReading( hLayer );
if ( hFeature )
return true;
return false;
}
QString QgsOgrUtils::getStyleById( GDALDatasetH hDS, const QString &styleId, QString &errCause )
{
OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
if ( !hLayer )
{
QgsMessageLog::logMessage( QObject::tr( "No styles available on DB" ) );
errCause = QObject::tr( "No styles available on DB" );
return QString();
}
bool ok;
int id = styleId.toInt( &ok );
if ( !ok )
{
errCause = QObject::tr( "Invalid style identifier" );
return QString();
}
gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetFeature( hLayer, id ) );
if ( !hFeature )
{
errCause = QObject::tr( "No style corresponding to style identifier" );
return QString();
}
OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
QString styleQML( QString::fromUtf8(
OGR_F_GetFieldAsString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ) ) ) );
OGR_L_ResetReading( hLayer );
return styleQML;
}
bool QgsOgrUtils::deleteStyleById( GDALDatasetH hDS, const QString &styleId, QString &errCause )
{
bool deleted;
OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
// check if layer_styles table already exist
if ( !hLayer )
{
errCause = QObject::tr( "Connection to database failed" );
deleted = false;
}
else
{
if ( OGR_L_DeleteFeature( hLayer, styleId.toInt() ) != OGRERR_NONE )
{
errCause = QObject::tr( "Error executing the delete query." );
deleted = false;
}
else
{
deleted = true;
}
}
return deleted;
}
QString QgsOgrUtils::loadStoredStyle( GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, QString &styleName, QString &errCause )
{
OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
if ( !hLayer )
{
QgsMessageLog::logMessage( QObject::tr( "No styles available on DB" ) );
errCause = QObject::tr( "No styles available on DB" );
return QString();
}
QString selectQmlQuery = QStringLiteral( "f_table_schema=''"
" AND f_table_name=%1"
" AND f_geometry_column=%2"
" ORDER BY CASE WHEN useAsDefault THEN 1 ELSE 2 END"
",update_time DESC LIMIT 1" )
.arg( QgsOgrProviderUtils::quotedValue( layerName ) )
.arg( QgsOgrProviderUtils::quotedValue( geomColumn ) );
OGR_L_SetAttributeFilter( hLayer, selectQmlQuery.toUtf8().constData() );
OGR_L_ResetReading( hLayer );
OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
QString styleQML;
qlonglong moreRecentTimestamp = 0;
while ( true )
{
gdal::ogr_feature_unique_ptr hFeat( OGR_L_GetNextFeature( hLayer ) );
if ( !hFeat )
break;
if ( OGR_F_GetFieldAsInteger( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "useAsDefault" ) ) )
{
styleQML = QString::fromUtf8(
OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ) ) );
styleName = QString::fromUtf8(
OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ) ) );
break;
}
int year, month, day, hour, minute, second, TZ;
OGR_F_GetFieldAsDateTime( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "update_time" ),
&year, &month, &day, &hour, &minute, &second, &TZ );
qlonglong ts = second + minute * 60 + hour * 3600 + day * 24 * 3600 +
static_cast<qlonglong>( month ) * 31 * 24 * 3600 + static_cast<qlonglong>( year ) * 12 * 31 * 24 * 3600;
if ( ts > moreRecentTimestamp )
{
moreRecentTimestamp = ts;
styleQML = QString::fromUtf8(
OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ) ) );
styleName = QString::fromUtf8(
OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ) ) );
}
}
OGR_L_ResetReading( hLayer );
return styleQML;
}
bool QgsOgrUtils::saveStyle(
GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, const QString &qmlStyle, const QString &sldStyle,
const QString &styleName, const QString &styleDescription,
const QString &uiFileContent, bool useAsDefault, QString &errCause
)
{
// check if layer_styles table already exist
OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
if ( !hLayer )
{
// if not create it
// Note: we use the same schema as in the SpatiaLite and postgres providers
//for cross interoperability
char **options = nullptr;
// TODO: might need change if other drivers than GPKG / SQLite
options = CSLSetNameValue( options, "FID", "id" );
hLayer = GDALDatasetCreateLayer( hDS, "layer_styles", nullptr, wkbNone, options );
QgsOgrProviderUtils::invalidateCachedDatasets( QString::fromUtf8( GDALGetDescription( hDS ) ) );
CSLDestroy( options );
if ( !hLayer )
{
errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database." );
return false;
}
bool ok = true;
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_table_catalog", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 256 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_table_schema", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 256 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_table_name", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 256 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_geometry_column", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 256 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "styleName", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 30 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "styleQML", OFTString ) );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "styleSLD", OFTString ) );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "useAsDefault", OFTInteger ) );
OGR_Fld_SetSubType( fld.get(), OFSTBoolean );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "description", OFTString ) );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "owner", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 30 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "ui", OFTString ) );
OGR_Fld_SetWidth( fld.get(), 30 );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
{
gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "update_time", OFTDateTime ) );
OGR_Fld_SetDefault( fld.get(), "CURRENT_TIMESTAMP" );
ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
}
if ( !ok )
{
errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database." );
return false;
}
}
QString realStyleName =
styleName.isEmpty() ? layerName : styleName;
OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
if ( useAsDefault )
{
QString oldDefaultQuery = QStringLiteral( "useAsDefault = 1 AND f_table_schema=''"
" AND f_table_name=%1"
" AND f_geometry_column=%2" )
.arg( QgsOgrProviderUtils::quotedValue( layerName ) )
.arg( QgsOgrProviderUtils::quotedValue( geomColumn ) );
OGR_L_SetAttributeFilter( hLayer, oldDefaultQuery.toUtf8().constData() );
gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
if ( hFeature )
{
OGR_F_SetFieldInteger( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "useAsDefault" ),
0 );
bool ok = OGR_L_SetFeature( hLayer, hFeature.get() ) == 0;
if ( !ok )
{
QgsDebugError( QStringLiteral( "Could not unset previous useAsDefault style" ) );
}
}
}
QString checkQuery = QStringLiteral( "f_table_schema=''"
" AND f_table_name=%1"
" AND f_geometry_column=%2"
" AND styleName=%3" )
.arg( QgsOgrProviderUtils::quotedValue( layerName ) )
.arg( QgsOgrProviderUtils::quotedValue( geomColumn ) )
.arg( QgsOgrProviderUtils::quotedValue( realStyleName ) );
OGR_L_SetAttributeFilter( hLayer, checkQuery.toUtf8().constData() );
OGR_L_ResetReading( hLayer );
gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
OGR_L_ResetReading( hLayer );
bool bNew = true;
if ( hFeature )
{
bNew = false;
}
else
{
hFeature.reset( OGR_F_Create( hLayerDefn ) );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "f_table_catalog" ),
"" );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "f_table_schema" ),
"" );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "f_table_name" ),
layerName.toUtf8().constData() );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "f_geometry_column" ),
geomColumn.toUtf8().constData() );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ),
realStyleName.toUtf8().constData() );
if ( !uiFileContent.isEmpty() )
{
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "ui" ),
uiFileContent.toUtf8().constData() );
}
}
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ),
qmlStyle.toUtf8().constData() );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "styleSLD" ),
sldStyle.toUtf8().constData() );
OGR_F_SetFieldInteger( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "useAsDefault" ),
useAsDefault ? 1 : 0 );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "description" ),
( styleDescription.isEmpty() ? QDateTime::currentDateTime().toString() : styleDescription ).toUtf8().constData() );
OGR_F_SetFieldString( hFeature.get(),
OGR_FD_GetFieldIndex( hLayerDefn, "owner" ),
"" );
bool bFeatureOK;
if ( bNew )
bFeatureOK = OGR_L_CreateFeature( hLayer, hFeature.get() ) == OGRERR_NONE;
else
bFeatureOK = OGR_L_SetFeature( hLayer, hFeature.get() ) == OGRERR_NONE;
if ( !bFeatureOK )
{
QgsMessageLog::logMessage( QObject::tr( "Error updating style" ) );
errCause = QObject::tr( "Error looking for style. The query was logged" );
return false;
}
return true;
}

View File

@ -484,53 +484,6 @@ class CORE_EXPORT QgsOgrUtils
#endif
#endif
/**
* Helper function for listing styles in ogr/gdal database datasources.
*
* \since QGIS 3.34
*/
static int listStyles( GDALDatasetH hDS, const QString &layerName,
const QString &geomColumn, QStringList &ids, QStringList &names,
QStringList &descriptions, QString &errCause );
/**
* Helper function for checking whether a style exists in ogr/gdal database datasources.
*
* \since QGIS 3.34
*/
static bool styleExists( GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, const QString &styleId, QString &errorCause );
/**
* Helper function for getting a style by ID from ogr/gdal database datasources.
*
* \since QGIS 3.34
*/
static QString getStyleById( GDALDatasetH hDS, const QString &styleId, QString &errCause );
/**
* Helper function for saving a style to ogr/gdal database datasources.
*
* \since QGIS 3.34
*/
static bool saveStyle( GDALDatasetH hDS, const QString &layerName,
const QString &geomColumn, const QString &qmlStyle, const QString &sldStyle,
const QString &styleName, const QString &styleDescription,
const QString &uiFileContent, bool useAsDefault, QString &errCause
);
/**
* Helper function for deleting a style by id from ogr/gdal database datasources.
*
* \since QGIS 3.34
*/
static bool deleteStyleById( GDALDatasetH hDS, const QString &styleId, QString &errCause );
/**
* Helper function for loading a stored styles in ogr/gdal database datasources.
*
* \since QGIS 3.34
*/
static QString loadStoredStyle( GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, QString &styleName, QString &errCause );
};
#endif // QGSOGRUTILS_H

View File

@ -850,6 +850,20 @@ QStringList QgsVectorDataProvider::errors() const
return mErrors;
}
bool QgsVectorDataProvider::isSaveAndLoadStyleToDatabaseSupported() const
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
return false;
}
bool QgsVectorDataProvider::isDeleteStyleFromDatabaseSupported() const
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
return false;
}
QgsFeatureRenderer *QgsVectorDataProvider::createRenderer( const QVariantMap & ) const
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS

View File

@ -532,6 +532,18 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat
*/
QStringList errors() const;
/**
* It returns FALSE by default.
* Must be implemented by providers that support saving and loading styles to db returning TRUE
*/
virtual bool isSaveAndLoadStyleToDatabaseSupported() const;
/**
* It returns FALSE by default.
* Must be implemented by providers that support delete styles from db returning TRUE
*/
virtual bool isDeleteStyleFromDatabaseSupported() const;
/**
* Creates a new vector layer feature renderer, using provider backend specific information.
*

View File

@ -1957,7 +1957,7 @@ QString QgsVectorLayer::loadDefaultStyle( bool &resultFlag )
if ( resultFlag )
{
// Try to load all stored styles from DB
if ( mLoadAllStoredStyle && mDataProvider && mDataProvider->styleStorageCapabilities().testFlag( Qgis::ProviderStyleStorageCapability::LoadFromDatabase ) )
if ( mLoadAllStoredStyle && mDataProvider && mDataProvider->isSaveAndLoadStyleToDatabaseSupported() )
{
QStringList ids, names, descriptions;
QString errorMessage;
@ -5793,6 +5793,61 @@ void QgsVectorLayer::setWeakRelations( const QList<QgsWeakRelation> &relations )
mWeakRelations = relations;
}
int QgsVectorLayer::listStylesInDatabase( QStringList &ids, QStringList &names, QStringList &descriptions, QString &msgError )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
return QgsProviderRegistry::instance()->listStyles( mProviderKey, mDataSource, ids, names, descriptions, msgError );
}
QString QgsVectorLayer::getStyleFromDatabase( const QString &styleId, QString &msgError )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
return QgsProviderRegistry::instance()->getStyleById( mProviderKey, mDataSource, styleId, msgError );
}
bool QgsVectorLayer::deleteStyleFromDatabase( const QString &styleId, QString &msgError )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
return QgsProviderRegistry::instance()->deleteStyleById( mProviderKey, mDataSource, styleId, msgError );
}
void QgsVectorLayer::saveStyleToDatabase( const QString &name, const QString &description,
bool useAsDefault, const QString &uiFileContent, QString &msgError, QgsMapLayer::StyleCategories categories )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
QString sldStyle, qmlStyle;
QDomDocument qmlDocument, sldDocument;
QgsReadWriteContext context;
exportNamedStyle( qmlDocument, msgError, context, categories );
if ( !msgError.isNull() )
{
return;
}
qmlStyle = qmlDocument.toString();
this->exportSldStyle( sldDocument, msgError );
if ( !msgError.isNull() )
{
return;
}
sldStyle = sldDocument.toString();
QgsProviderRegistry::instance()->saveStyle( mProviderKey,
mDataSource, qmlStyle, sldStyle, name,
description, uiFileContent, useAsDefault, msgError );
}
QString QgsVectorLayer::loadNamedStyle( const QString &theURI, bool &resultFlag, QgsMapLayer::StyleCategories categories )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
return loadNamedStyle( theURI, resultFlag, false, categories );
}
bool QgsVectorLayer::loadAuxiliaryLayer( const QgsAuxiliaryStorage &storage, const QString &key )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
@ -5863,6 +5918,43 @@ QgsAuxiliaryLayer *QgsVectorLayer::auxiliaryLayer()
return mAuxiliaryLayer.get();
}
QString QgsVectorLayer::loadNamedStyle( const QString &theURI, bool &resultFlag, bool loadFromLocalDB, QgsMapLayer::StyleCategories categories )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
QgsDataSourceUri dsUri( theURI );
QString returnMessage;
QString qml, errorMsg;
QString styleName;
if ( !loadFromLocalDB && mDataProvider && mDataProvider->isSaveAndLoadStyleToDatabaseSupported() )
{
qml = QgsProviderRegistry::instance()->loadStoredStyle( mProviderKey, mDataSource, styleName, errorMsg );
}
// Style was successfully loaded from provider storage
if ( !qml.isEmpty() )
{
QDomDocument myDocument( QStringLiteral( "qgis" ) );
myDocument.setContent( qml );
resultFlag = importNamedStyle( myDocument, errorMsg );
returnMessage = QObject::tr( "Loaded from Provider" );
}
else
{
returnMessage = QgsMapLayer::loadNamedStyle( theURI, resultFlag, categories );
}
if ( ! styleName.isEmpty() )
{
styleManager()->renameStyle( styleManager()->currentStyle(), styleName );
}
if ( resultFlag )
emit styleLoaded( categories );
return returnMessage;
}
QSet<QgsMapLayerDependency> QgsVectorLayer::dependencies() const
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS

View File

@ -1023,6 +1023,70 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
*/
void resolveReferences( QgsProject *project ) FINAL;
/**
* Saves named and sld style of the layer to the style table in the db.
* \param name Style name
* \param description A description of the style
* \param useAsDefault Set to TRUE if style should be used as the default style for the layer
* \param uiFileContent
* \param msgError will be set to a descriptive error message if any occurs
* \param categories the style categories to be saved.
*
*
* \note Prior to QGIS 3.24, this method would show a message box warning when a
* style with the same \a styleName already existed to confirm replacing the style with the user.
* Since 3.24, calling this method will ALWAYS overwrite any existing style with the same name.
* Use QgsProviderRegistry::styleExists() to test in advance if a style already exists and handle this appropriately
* in your client code.
*/
virtual void saveStyleToDatabase( const QString &name, const QString &description,
bool useAsDefault, const QString &uiFileContent,
QString &msgError SIP_OUT,
QgsMapLayer::StyleCategories categories = QgsMapLayer::AllStyleCategories );
/**
* Lists all the style in db split into related to the layer and not related to
* \param ids the list in which will be stored the style db ids
* \param names the list in which will be stored the style names
* \param descriptions the list in which will be stored the style descriptions
* \param msgError will be set to a descriptive error message if any occurs
* \returns the number of styles related to current layer (-1 on not implemented)
* \note Since QGIS 3.2 Styles related to the layer are ordered with the default style first then by update time for Postgres, MySQL and Spatialite.
*/
virtual int listStylesInDatabase( QStringList &ids SIP_OUT, QStringList &names SIP_OUT,
QStringList &descriptions SIP_OUT, QString &msgError SIP_OUT );
/**
* Returns the named style corresponding to style id provided
*/
virtual QString getStyleFromDatabase( const QString &styleId, QString &msgError SIP_OUT );
/**
* Deletes a style from the database
* \param styleId the provider's layer_styles table id of the style to delete
* \param msgError will be set to a descriptive error message if any occurs
* \returns TRUE in case of success
* \since QGIS 3.0
*/
virtual bool deleteStyleFromDatabase( const QString &styleId, QString &msgError SIP_OUT );
/**
* Loads a named style from file/local db/datasource db
* \param theURI the URI of the style or the URI of the layer
* \param resultFlag will be set to TRUE if a named style is correctly loaded
* \param loadFromLocalDb if TRUE forces to load from local db instead of datasource one
* \param categories the style categories to be loaded.
*/
virtual QString loadNamedStyle( const QString &theURI, bool &resultFlag SIP_OUT, bool loadFromLocalDb,
QgsMapLayer::StyleCategories categories = QgsMapLayer::AllStyleCategories );
/**
* Calls loadNamedStyle( theURI, resultFlag, FALSE );
* Retained for backward compatibility
*/
QString loadNamedStyle( const QString &theURI, bool &resultFlag SIP_OUT,
QgsMapLayer::StyleCategories categories = QgsMapLayer::AllStyleCategories ) FINAL;
/**
* Loads the auxiliary layer for this vector layer. If there's no
* corresponding table in the database, then nothing happens and FALSE is

View File

@ -39,6 +39,7 @@ set(QGIS_GUI_SRCS
vector/qgssourcefieldsproperties.cpp
vector/qgsvectorlayerlegendwidget.cpp
vector/qgsvectorlayerproperties.cpp
vector/qgsvectorlayersavestyledialog.cpp
vector/qgswmsdimensiondialog.cpp
symbology/qgs25drendererwidget.cpp
@ -621,7 +622,6 @@ set(QGIS_GUI_SRCS
qgsmaplayerconfigwidgetfactory.cpp
qgsmaplayerloadstyledialog.cpp
qgsmaplayerrefreshsettingswidget.cpp
qgsmaplayersavestyledialog.cpp
qgsmaplayerstylecategoriesmodel.cpp
qgsmaplayerstyleguiutils.cpp
qgsmaplayerstylemanagerwidget.cpp
@ -897,7 +897,6 @@ set(QGIS_GUI_HDRS
qgsmaplayercombobox.h
qgsmaplayerconfigwidget.h
qgsmaplayerconfigwidgetfactory.h
qgsmaplayersavestyledialog.h
qgsmaplayerloadstyledialog.h
qgsmaplayerrefreshsettingswidget.h
qgsmaplayerstylecategoriesmodel.h
@ -1432,6 +1431,7 @@ set(QGIS_GUI_HDRS
vector/qgssourcefieldsproperties.h
vector/qgsvectorlayerlegendwidget.h
vector/qgsvectorlayerproperties.h
vector/qgsvectorlayersavestyledialog.h
vector/qgswmsdimensiondialog.h
symbology/characterwidget.h

View File

@ -24,6 +24,7 @@
class QgsMapLayer;
class QgsMapCanvas;
class QgsMeshLayer;
class QgsRendererMeshPropertiesWidget;
class QgsMeshLayer3DRendererWidget;
class QgsMeshStaticDatasetWidget;

View File

@ -14,8 +14,6 @@
***************************************************************************/
#include "qgslayerpropertiesdialog.h"
#include "qgsmaplayerloadstyledialog.h"
#include "qgsmaplayersavestyledialog.h"
#include "qgsmaplayerconfigwidget.h"
#include "qgsmaplayerconfigwidgetfactory.h"
#include "qgsmaplayerstylemanager.h"
@ -23,9 +21,7 @@
#include "qgssettings.h"
#include "qgsmaplayer.h"
#include "qgsmetadatawidget.h"
#include "qgsproviderregistry.h"
#include "qgsfileutils.h"
#include "qgssldexportcontext.h"
#include "qstackedwidget.h"
#include "qgsmapcanvas.h"
@ -39,6 +35,7 @@ QgsLayerPropertiesDialog::QgsLayerPropertiesDialog( QgsMapLayer *layer, QgsMapCa
, mCanvas( canvas )
, mLayer( layer )
{
}
void QgsLayerPropertiesDialog::setMetadataWidget( QgsMetadataWidget *widget, QWidget *page )
@ -254,6 +251,29 @@ void QgsLayerPropertiesDialog::saveStyleAsDefault()
}
}
void QgsLayerPropertiesDialog::loadDefaultStyle()
{
if ( !mLayer )
return;
bool defaultLoadedFlag = false;
const QString message = mLayer->loadDefaultStyle( defaultLoadedFlag );
// reset if the default style was loaded OK only
if ( defaultLoadedFlag )
{
syncToLayer();
}
else
{
// otherwise let the user know what went wrong
QMessageBox::information( this,
tr( "Default Style" ),
message
);
refocusDialog();
}
}
void QgsLayerPropertiesDialog::initialize()
{
restoreOptionsBaseUi( generateDialogTitle() );
@ -283,303 +303,6 @@ void QgsLayerPropertiesDialog::addPropertiesPageFactory( const QgsMapLayerConfig
page->syncToLayer( mLayer );
}
void QgsLayerPropertiesDialog::loadDefaultStyle()
{
QString msg;
bool defaultLoadedFlag = false;
const QgsDataProvider *provider = mLayer->dataProvider();
if ( !provider )
return;
if ( provider->styleStorageCapabilities().testFlag( Qgis::ProviderStyleStorageCapability::LoadFromDatabase ) )
{
QMessageBox askToUser;
askToUser.setText( tr( "Load default style from: " ) );
askToUser.setIcon( QMessageBox::Question );
askToUser.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
askToUser.addButton( tr( "Local Database" ), QMessageBox::NoRole );
askToUser.addButton( tr( "Datasource Database" ), QMessageBox::YesRole );
switch ( askToUser.exec() )
{
case 0:
return;
case 2:
msg = mLayer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag, false );
if ( !defaultLoadedFlag )
{
//something went wrong - let them know why
QMessageBox::information( this, tr( "Default Style" ), msg );
}
if ( msg.compare( tr( "Loaded from Provider" ) ) )
{
QMessageBox::information( this, tr( "Default Style" ),
tr( "No default style was found for this layer." ) );
}
else
{
syncToLayer();
apply();
}
return;
default:
break;
}
}
QString myMessage = mLayer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag, true );
// QString myMessage = layer->loadDefaultStyle( defaultLoadedFlag );
//reset if the default style was loaded OK only
if ( defaultLoadedFlag )
{
// all worked OK so no need to inform user
syncToLayer();
apply();
}
else
{
//something went wrong - let them know why
QMessageBox::information( this, tr( "Default Style" ), myMessage );
}
}
void QgsLayerPropertiesDialog::saveDefaultStyle()
{
QString errorMsg;
const QgsDataProvider *provider = mLayer->dataProvider();
if ( !provider )
return;
if ( provider->styleStorageCapabilities().testFlag( Qgis::ProviderStyleStorageCapability::SaveToDatabase ) )
{
QMessageBox askToUser;
askToUser.setText( tr( "Save default style to: " ) );
askToUser.setIcon( QMessageBox::Question );
askToUser.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
askToUser.addButton( tr( "Local Database" ), QMessageBox::NoRole );
askToUser.addButton( tr( "Datasource Database" ), QMessageBox::YesRole );
switch ( askToUser.exec() )
{
case 0:
return;
case 2:
{
apply();
QString errorMessage;
if ( QgsProviderRegistry::instance()->styleExists( mLayer->providerType(), mLayer->source(), QString(), errorMessage ) )
{
if ( QMessageBox::question( nullptr, QObject::tr( "Save style in database" ),
QObject::tr( "A matching style already exists in the database for this layer. Do you want to overwrite it?" ),
QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No )
{
return;
}
}
else if ( !errorMessage.isEmpty() )
{
QMessageBox::warning( nullptr, QObject::tr( "Save style in database" ),
errorMessage );
return;
}
mLayer->saveStyleToDatabase( QString(), QString(), true, QString(), errorMsg );
if ( errorMsg.isNull() )
{
return;
}
break;
}
default:
break;
}
}
QgsLayerPropertiesDialog::saveStyleAsDefault();
}
void QgsLayerPropertiesDialog::saveStyleAs()
{
if ( !mLayer->dataProvider() )
return;
QgsMapLayerSaveStyleDialog dlg( mLayer );
if ( dlg.exec() )
{
apply();
bool defaultLoadedFlag = false;
QString errorMessage;
StyleType type = dlg.currentStyleType();
switch ( type )
{
case QML:
case SLD:
{
QString filePath = dlg.outputFilePath();
if ( type == QML )
errorMessage = mLayer->saveNamedStyle( filePath, defaultLoadedFlag, dlg.styleCategories() );
else
{
const QgsSldExportContext sldContext { dlg.sldExportOptions(), Qgis::SldExportVendorExtension::NoVendorExtension, filePath };
errorMessage = mLayer->saveSldStyleV2( defaultLoadedFlag, sldContext );
}
//reset if the default style was loaded OK only
if ( defaultLoadedFlag )
{
syncToLayer();
}
else
{
//let the user know what went wrong
QMessageBox::information( this, tr( "Save Style" ), errorMessage );
}
break;
}
case DatasourceDatabase:
{
QString infoWindowTitle = QObject::tr( "Save style to DB (%1)" ).arg( mLayer->providerType() );
QgsMapLayerSaveStyleDialog::SaveToDbSettings dbSettings = dlg.saveToDbSettings();
if ( QgsProviderRegistry::instance()->styleExists( mLayer->providerType(), mLayer->source(), dbSettings.name, errorMessage ) )
{
if ( QMessageBox::question( nullptr, QObject::tr( "Save style in database" ),
QObject::tr( "A matching style already exists in the database for this layer. Do you want to overwrite it?" ),
QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No )
{
return;
}
}
else if ( !errorMessage.isEmpty() )
{
QMessageBox::warning( this, infoWindowTitle, errorMessage );
return;
}
mLayer->saveStyleToDatabase( dbSettings.name, dbSettings.description, dbSettings.isDefault, dbSettings.uiFileContent, errorMessage, dlg.styleCategories() );
if ( !errorMessage.isNull() )
{
QMessageBox::warning( this, infoWindowTitle, errorMessage );
}
else
{
QMessageBox::information( this, infoWindowTitle, tr( "Style saved" ) );
}
break;
}
case UserDatabase:
{
QString infoWindowTitle = tr( "Save default style to local database" );
errorMessage = mLayer->saveDefaultStyle( defaultLoadedFlag, dlg.styleCategories() );
if ( !defaultLoadedFlag )
{
QMessageBox::warning( this, infoWindowTitle, errorMessage );
}
else
{
QMessageBox::information( this, infoWindowTitle, tr( "Style saved" ) );
}
break;
}
}
}
}
void QgsLayerPropertiesDialog::loadStyle()
{
QString errorMsg;
QStringList ids, names, descriptions;
//get the list of styles in the db
int sectionLimit = mLayer->listStylesInDatabase( ids, names, descriptions, errorMsg );
QgsMapLayerLoadStyleDialog dlg( mLayer, this );
dlg.initializeLists( ids, names, descriptions, sectionLimit );
if ( dlg.exec() )
{
mOldStyle = mLayer->styleManager()->style( mLayer->styleManager()->currentStyle() );
QgsMapLayer::StyleCategories categories = dlg.styleCategories();
StyleType type = dlg.currentStyleType();
bool defaultLoadedFlag = false;
switch ( type )
{
case QML:
case SLD:
{
QString filePath = dlg.filePath();
if ( type == SLD )
{
errorMsg = mLayer->loadSldStyle( filePath, defaultLoadedFlag );
}
else
{
errorMsg = mLayer->loadNamedStyle( filePath, defaultLoadedFlag, true, categories );
}
//reset if the default style was loaded OK only
if ( defaultLoadedFlag )
{
syncToLayer();
apply();
}
else
{
//let the user know what went wrong
QMessageBox::warning( this, tr( "Load Style" ), errorMsg );
}
break;
}
case DatasourceDatabase:
{
QString selectedStyleId = dlg.selectedStyleId();
QString qmlStyle = mLayer->getStyleFromDatabase( selectedStyleId, errorMsg );
if ( !errorMsg.isNull() )
{
QMessageBox::warning( this, tr( "Load Styles from Database" ), errorMsg );
return;
}
QDomDocument myDocument( QStringLiteral( "qgis" ) );
myDocument.setContent( qmlStyle );
if ( mLayer->importNamedStyle( myDocument, errorMsg, categories ) )
{
syncToLayer();
apply();
}
else
{
QMessageBox::warning( this, tr( "Load Styles from Database" ),
tr( "The retrieved style is not a valid named style. Error message: %1" )
.arg( errorMsg ) );
}
break;
}
case UserDatabase:
{
errorMsg = mLayer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag, true, categories );
//reset if the default style was loaded OK only
if ( defaultLoadedFlag )
{
syncToLayer();
apply();
}
else
{
QMessageBox::warning( this, tr( "Load Default Style" ), errorMsg );
}
break;
}
}
activateWindow(); // set focus back to properties dialog
}
}
void QgsLayerPropertiesDialog::storeCurrentStyleForUndo()
{
if ( !mLayer )

View File

@ -42,21 +42,6 @@ class GUI_EXPORT QgsLayerPropertiesDialog : public QgsOptionsDialogBase SIP_ABST
public:
#ifndef SIP_RUN
/**
* Style storage type.
*/
enum StyleType
{
QML,
SLD,
DatasourceDatabase,
UserDatabase,
};
Q_ENUM( StyleType )
#endif
/**
* Constructor for QgsLayerPropertiesDialog.
*
@ -81,27 +66,6 @@ class GUI_EXPORT QgsLayerPropertiesDialog : public QgsOptionsDialogBase SIP_ABST
*/
virtual void addPropertiesPageFactory( const QgsMapLayerConfigWidgetFactory *factory );
/**
* Saves the default style when appropriate button is pressed
*
* \since QGIS 3.30
*/
void saveDefaultStyle();
/**
* Triggers a dialog to load a saved style
*
* \since QGIS 3.30
*/
void loadStyle();
/**
* Saves a style when appriate button is pressed
*
* \since QGIS 3.30
*/
void saveStyleAs();
public slots:
/**

View File

@ -19,7 +19,7 @@
#include "qgsmaplayerloadstyledialog.h"
#include "qgslogger.h"
#include "qgssettings.h"
#include "qgslayerpropertiesdialog.h"
#include "qgsvectorlayerproperties.h"
#include "qgsmaplayerstylecategoriesmodel.h"
#include "qgshelp.h"
#include "qgsapplication.h"
@ -43,29 +43,50 @@ QgsMapLayerLoadStyleDialog::QgsMapLayerLoadStyleDialog( QgsMapLayer *layer, QWid
QgsSettings settings;
QString providerName = mLayer->providerType();
if ( providerName == QLatin1String( "ogr" ) )
{
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayer );
providerName = vl->dataProvider()->storageType();
if ( providerName == QLatin1String( "GPKG" ) )
providerName = QStringLiteral( "GeoPackage" );
}
const QString myLastUsedDir = settings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
// load style type combobox
connect( mStyleTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [ = ]( int )
{
const QgsLayerPropertiesDialog::StyleType type = currentStyleType();
mFileLabel->setVisible( type != QgsLayerPropertiesDialog::StyleType::DatasourceDatabase && type != QgsLayerPropertiesDialog::StyleType::UserDatabase );
mFileWidget->setVisible( type != QgsLayerPropertiesDialog::StyleType::DatasourceDatabase && type != QgsLayerPropertiesDialog::StyleType::UserDatabase );
mFromDbWidget->setVisible( type == QgsLayerPropertiesDialog::StyleType::DatasourceDatabase );
mDeleteButton->setVisible( type == QgsLayerPropertiesDialog::StyleType::DatasourceDatabase && mLayer->dataProvider()->styleStorageCapabilities().testFlag( Qgis::ProviderStyleStorageCapability::DeleteFromDatabase ) );
const QgsVectorLayerProperties::StyleType type = currentStyleType();
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayer );
mFileLabel->setVisible( !vl || ( type != QgsVectorLayerProperties::StyleType::DB && type != QgsVectorLayerProperties::StyleType::Local ) );
mFileWidget->setVisible( !vl || ( type != QgsVectorLayerProperties::StyleType::DB && type != QgsVectorLayerProperties::StyleType::Local ) );
if ( vl )
{
mFromDbWidget->setVisible( type == QgsVectorLayerProperties::StyleType::DB );
mDeleteButton->setVisible( type == QgsVectorLayerProperties::StyleType::DB && vl->dataProvider()->isDeleteStyleFromDatabaseSupported() );
}
else
{
mFromDbWidget->setVisible( false );
mDeleteButton->setVisible( false );
}
mStyleCategoriesListView->setEnabled( currentStyleType() != QgsLayerPropertiesDialog::StyleType::SLD );
mStyleCategoriesListView->setEnabled( !vl || currentStyleType() != QgsVectorLayerProperties::StyleType::SLD );
updateLoadButtonState();
} );
mStyleTypeComboBox->addItem( tr( "From file" ), QgsLayerPropertiesDialog::QML ); // QML is used as entry, but works for SLD too, see currentStyleType()
mStyleTypeComboBox->addItem( tr( "Default from local database" ), QgsLayerPropertiesDialog::UserDatabase );
mStyleTypeComboBox->addItem( tr( "From File" ), QgsVectorLayerProperties::QML ); // QML is used as entry, but works for SLD too, see currentStyleType()
mStyleTypeComboBox->addItem( tr( "Default from local database" ), QgsVectorLayerProperties::Local );
if ( mLayer->dataProvider()->styleStorageCapabilities().testFlag( Qgis::ProviderStyleStorageCapability::LoadFromDatabase ) )
if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayer ) )
{
mStyleTypeComboBox->addItem( tr( "From datasource database" ), QgsLayerPropertiesDialog::StyleType::DatasourceDatabase );
if ( settings.value( QStringLiteral( "style/lastLoadStyleTypeSelection" ) ) == QgsLayerPropertiesDialog::StyleType::DatasourceDatabase )
if ( vl->dataProvider()->isSaveAndLoadStyleToDatabaseSupported() )
{
mStyleTypeComboBox->setCurrentIndex( mStyleTypeComboBox->findData( QgsLayerPropertiesDialog::StyleType::DatasourceDatabase ) );
mStyleTypeComboBox->addItem( tr( "From Database (%1)" ).arg( providerName ), QgsVectorLayerProperties::StyleType::DB );
if ( settings.value( QStringLiteral( "style/lastLoadStyleTypeSelection" ) ) == QgsVectorLayerProperties::StyleType::DB )
{
mStyleTypeComboBox->setCurrentIndex( mStyleTypeComboBox->findData( QgsVectorLayerProperties::StyleType::DB ) );
}
}
}
@ -103,7 +124,8 @@ QgsMapLayerLoadStyleDialog::QgsMapLayerLoadStyleDialog( QgsMapLayer *layer, QWid
mFileWidget->setDefaultRoot( myLastUsedDir );
connect( mFileWidget, &QgsFileWidget::fileChanged, this, [ = ]( const QString & path )
{
mStyleCategoriesListView->setEnabled( currentStyleType() != QgsLayerPropertiesDialog::SLD );
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayer );
mStyleCategoriesListView->setEnabled( !vl || currentStyleType() != QgsVectorLayerProperties::SLD );
QgsSettings settings;
const QFileInfo tmplFileInfo( path );
settings.setValue( QStringLiteral( "style/lastStyleDir" ), tmplFileInfo.absolutePath() );
@ -145,14 +167,14 @@ QgsMapLayer::StyleCategories QgsMapLayerLoadStyleDialog::styleCategories() const
return mModel->categories();
}
QgsLayerPropertiesDialog::StyleType QgsMapLayerLoadStyleDialog::currentStyleType() const
QgsVectorLayerProperties::StyleType QgsMapLayerLoadStyleDialog::currentStyleType() const
{
QgsLayerPropertiesDialog::StyleType type = mStyleTypeComboBox->currentData().value<QgsLayerPropertiesDialog::StyleType>();
if ( type == QgsLayerPropertiesDialog::QML )
QgsVectorLayerProperties::StyleType type = mStyleTypeComboBox->currentData().value<QgsVectorLayerProperties::StyleType>();
if ( type == QgsVectorLayerProperties::QML )
{
const QFileInfo fi( mFileWidget->filePath() );
if ( fi.exists() && fi.suffix().compare( QStringLiteral( "sld" ), Qt::CaseInsensitive ) == 0 )
type = QgsLayerPropertiesDialog::SLD;
type = QgsVectorLayerProperties::SLD;
}
return type;
}
@ -276,6 +298,10 @@ void QgsMapLayerLoadStyleDialog::accept()
void QgsMapLayerLoadStyleDialog::deleteStyleFromDB()
{
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( mLayer );
if ( !vl )
return;
QString msgError;
const QString opInfo = QObject::tr( "Delete style %1 from %2" ).arg( mSelectedStyleName, mLayer->providerType() );
@ -284,7 +310,7 @@ void QgsMapLayerLoadStyleDialog::deleteStyleFromDB()
QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
return;
mLayer->deleteStyleFromDatabase( mSelectedStyleId, msgError );
vl->deleteStyleFromDatabase( mSelectedStyleId, msgError );
if ( !msgError.isNull() )
{
QgsDebugError( opInfo + " failed." );
@ -302,7 +328,7 @@ void QgsMapLayerLoadStyleDialog::deleteStyleFromDB()
QString errorMsg;
QStringList ids, names, descriptions;
//get the list of styles in the db
const int sectionLimit = mLayer->listStylesInDatabase( ids, names, descriptions, errorMsg );
const int sectionLimit = vl->listStylesInDatabase( ids, names, descriptions, errorMsg );
if ( !errorMsg.isNull() )
{
QMessageBox::warning( this, tr( "Error occurred while retrieving styles from database" ), errorMsg );
@ -316,12 +342,19 @@ void QgsMapLayerLoadStyleDialog::deleteStyleFromDB()
void QgsMapLayerLoadStyleDialog::updateLoadButtonState()
{
const QgsLayerPropertiesDialog::StyleType type = currentStyleType();
mLoadButton->setEnabled( ( type == QgsLayerPropertiesDialog::DatasourceDatabase
&& ( mRelatedTable->selectionModel()->hasSelection() || mOthersTable->selectionModel()->hasSelection()
) ) ||
( type != QgsLayerPropertiesDialog::DatasourceDatabase && !mFileWidget->filePath().isEmpty() ) ||
type == QgsLayerPropertiesDialog::UserDatabase );
const QgsVectorLayerProperties::StyleType type = currentStyleType();
if ( mLayer->type() == Qgis::LayerType::Vector )
{
mLoadButton->setEnabled( ( type == QgsVectorLayerProperties::DB
&& ( mRelatedTable->selectionModel()->hasSelection() || mOthersTable->selectionModel()->hasSelection()
) ) ||
( type != QgsVectorLayerProperties::DB && !mFileWidget->filePath().isEmpty() ) ||
type == QgsVectorLayerProperties::Local );
}
else
{
mLoadButton->setEnabled( !mFileWidget->filePath().isEmpty() );
}
}
void QgsMapLayerLoadStyleDialog::showHelp()

View File

@ -54,9 +54,9 @@ class GUI_EXPORT QgsMapLayerLoadStyleDialog : public QDialog, private Ui::QgsVec
QgsMapLayer::StyleCategories styleCategories() const;
/**
* Returns the selected style type.
* Returns the selected vector style type, for vector layers only.
*/
QgsLayerPropertiesDialog::StyleType currentStyleType() const;
QgsVectorLayerProperties::StyleType currentStyleType() const;
/**
* Returns the file extension for the selected layer style source file.

View File

@ -30,8 +30,6 @@ QgsMapLayerStyleCategoriesModel::QgsMapLayerStyleCategoriesModel( Qgis::LayerTyp
break;
case Qgis::LayerType::Raster:
mCategoryList << QgsMapLayer::StyleCategory::Symbology << QgsMapLayer::StyleCategory::AllStyleCategories;
break;
case Qgis::LayerType::Annotation:
case Qgis::LayerType::Plugin:
case Qgis::LayerType::Mesh:
@ -43,11 +41,7 @@ QgsMapLayerStyleCategoriesModel::QgsMapLayerStyleCategoriesModel( Qgis::LayerTyp
}
// move All categories to top
int idxAllStyleCategories = mCategoryList.indexOf( QgsMapLayer::AllStyleCategories );
if ( idxAllStyleCategories > 0 )
{
mCategoryList.move( idxAllStyleCategories, 0 );
}
mCategoryList.move( mCategoryList.indexOf( QgsMapLayer::AllStyleCategories ), 0 );
}
void QgsMapLayerStyleCategoriesModel::setCategories( QgsMapLayer::StyleCategories categories )
@ -75,7 +69,7 @@ void QgsMapLayerStyleCategoriesModel::setShowAllCategories( bool showAll )
int QgsMapLayerStyleCategoriesModel::rowCount( const QModelIndex & ) const
{
int count = mCategoryList.count();
if ( count > 0 && !mShowAllCategories )
if ( !mShowAllCategories )
count--;
return count;
}

View File

@ -527,7 +527,7 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv
setMetadataWidget( mMetadataWidget, mOptsPage_Metadata );
QMenu *menuStyle = new QMenu( this );
menuStyle->addAction( tr( "Load Style…" ), this, &QgsRasterLayerProperties::loadStyle );
menuStyle->addAction( tr( "Load Style…" ), this, &QgsRasterLayerProperties::loadStyleFromFile );
menuStyle->addAction( tr( "Save Style…" ), this, &QgsRasterLayerProperties::saveStyleAs );
menuStyle->addSeparator();
menuStyle->addAction( tr( "Save as Default" ), this, &QgsRasterLayerProperties::saveStyleAsDefault );
@ -1600,6 +1600,66 @@ void QgsRasterLayerProperties::saveDefaultStyle()
saveStyleAsDefault();
}
void QgsRasterLayerProperties::loadStyle()
{
loadStyleFromFile();
}
void QgsRasterLayerProperties::saveStyleAs()
{
QgsSettings settings;
QString lastUsedDir = settings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
QString selectedFilter;
QString outputFileName = QFileDialog::getSaveFileName(
this,
tr( "Save layer properties as style file" ),
lastUsedDir,
tr( "QGIS Layer Style File" ) + " (*.qml)" + ";;" + tr( "Styled Layer Descriptor" ) + " (*.sld)",
&selectedFilter );
if ( outputFileName.isEmpty() )
return;
StyleType type;
// use selectedFilter to set style type
if ( selectedFilter.contains( QStringLiteral( ".qml" ), Qt::CaseInsensitive ) )
{
outputFileName = QgsFileUtils::ensureFileNameHasExtension( outputFileName, QStringList() << QStringLiteral( "qml" ) );
type = StyleType::QML;
}
else
{
outputFileName = QgsFileUtils::ensureFileNameHasExtension( outputFileName, QStringList() << QStringLiteral( "sld" ) );
type = StyleType::SLD;
}
apply(); // make sure the style to save is up-to-date
// then export style
bool defaultLoadedFlag = false;
QString message;
switch ( type )
{
case QML:
{
message = mRasterLayer->saveNamedStyle( outputFileName, defaultLoadedFlag );
break;
}
case SLD:
{
message = mRasterLayer->saveSldStyle( outputFileName, defaultLoadedFlag );
break;
}
}
if ( defaultLoadedFlag )
{
settings.setValue( QStringLiteral( "style/lastStyleDir" ), QFileInfo( outputFileName ).absolutePath() );
sync();
}
else
QMessageBox::information( this, tr( "Save Style" ), message );
}
void QgsRasterLayerProperties::restoreWindowModality()
{
hide();

View File

@ -94,6 +94,20 @@ class GUI_EXPORT QgsRasterLayerProperties : public QgsLayerPropertiesDialog, pri
*/
Q_DECL_DEPRECATED void saveDefaultStyle() SIP_DEPRECATED;
/**
* Loads a saved style when appropriate button is pressed
*
* \deprecated use loadStyleFromFile() instead.
*/
Q_DECL_DEPRECATED void loadStyle() SIP_DEPRECATED;
/**
* Saves a style when appriate button is pressed
*
* \since QGIS 3.30
*/
void saveStyleAs();
protected slots:
void optionsStackedWidget_CurrentChanged( int index ) FINAL;
void apply() FINAL;

View File

@ -48,11 +48,12 @@
#include "qgsrendererpropertiesdialog.h"
#include "qgsstyle.h"
#include "qgsauxiliarystorage.h"
#include "qgsmaplayersavestyledialog.h"
#include "qgsmaplayerserverproperties.h"
#include "qgsnewauxiliarylayerdialog.h"
#include "qgsnewauxiliaryfielddialog.h"
#include "qgslabelinggui.h"
#include "qgsvectorlayersavestyledialog.h"
#include "qgsmaplayerloadstyledialog.h"
#include "qgsmessagebar.h"
#include "qgssymbolwidgetcontext.h"
#include "qgsexpressioncontextutils.h"
@ -1038,9 +1039,217 @@ void QgsVectorLayerProperties::mCrsSelector_crsChanged( const QgsCoordinateRefer
mMetadataWidget->crsChanged();
}
void QgsVectorLayerProperties::loadDefaultStyle()
{
QString msg;
bool defaultLoadedFlag = false;
const QgsVectorDataProvider *provider = mLayer->dataProvider();
if ( !provider )
return;
if ( provider->isSaveAndLoadStyleToDatabaseSupported() )
{
QMessageBox askToUser;
askToUser.setText( tr( "Load default style from: " ) );
askToUser.setIcon( QMessageBox::Question );
askToUser.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
askToUser.addButton( tr( "Local Database" ), QMessageBox::NoRole );
askToUser.addButton( tr( "Datasource Database" ), QMessageBox::YesRole );
switch ( askToUser.exec() )
{
case 0:
return;
case 2:
msg = mLayer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag );
if ( !defaultLoadedFlag )
{
//something went wrong - let them know why
QMessageBox::information( this, tr( "Default Style" ), msg );
}
if ( msg.compare( tr( "Loaded from Provider" ) ) )
{
QMessageBox::information( this, tr( "Default Style" ),
tr( "No default style was found for this layer." ) );
}
else
{
syncToLayer();
apply();
}
return;
default:
break;
}
}
QString myMessage = mLayer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag, true );
// QString myMessage = layer->loadDefaultStyle( defaultLoadedFlag );
//reset if the default style was loaded OK only
if ( defaultLoadedFlag )
{
// all worked OK so no need to inform user
syncToLayer();
apply();
}
else
{
//something went wrong - let them know why
QMessageBox::information( this, tr( "Default Style" ), myMessage );
}
}
void QgsVectorLayerProperties::saveDefaultStyle()
{
QString errorMsg;
const QgsVectorDataProvider *provider = mLayer->dataProvider();
if ( !provider )
return;
if ( provider->isSaveAndLoadStyleToDatabaseSupported() )
{
QMessageBox askToUser;
askToUser.setText( tr( "Save default style to: " ) );
askToUser.setIcon( QMessageBox::Question );
askToUser.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
askToUser.addButton( tr( "Local Database" ), QMessageBox::NoRole );
askToUser.addButton( tr( "Datasource Database" ), QMessageBox::YesRole );
switch ( askToUser.exec() )
{
case 0:
return;
case 2:
{
apply();
QString errorMessage;
if ( QgsProviderRegistry::instance()->styleExists( mLayer->providerType(), mLayer->source(), QString(), errorMessage ) )
{
if ( QMessageBox::question( nullptr, QObject::tr( "Save style in database" ),
QObject::tr( "A matching style already exists in the database for this layer. Do you want to overwrite it?" ),
QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No )
{
return;
}
}
else if ( !errorMessage.isEmpty() )
{
QMessageBox::warning( nullptr, QObject::tr( "Save style in database" ),
errorMessage );
return;
}
mLayer->saveStyleToDatabase( QString(), QString(), true, QString(), errorMsg );
if ( errorMsg.isNull() )
{
return;
}
break;
}
default:
break;
}
}
QgsLayerPropertiesDialog::saveStyleAsDefault();
}
void QgsVectorLayerProperties::saveStyleAs()
{
if ( !mLayer->dataProvider() )
return;
QgsVectorLayerSaveStyleDialog dlg( mLayer );
QgsSettings settings;
if ( dlg.exec() )
{
apply();
bool defaultLoadedFlag = false;
QString errorMessage;
StyleType type = dlg.currentStyleType();
switch ( type )
{
case QML:
case SLD:
{
QString filePath = dlg.outputFilePath();
if ( type == QML )
errorMessage = mLayer->saveNamedStyle( filePath, defaultLoadedFlag, dlg.styleCategories() );
else
{
const QgsSldExportContext sldContext { dlg.sldExportOptions(), Qgis::SldExportVendorExtension::NoVendorExtension, filePath };
errorMessage = mLayer->saveSldStyleV2( defaultLoadedFlag, sldContext );
}
//reset if the default style was loaded OK only
if ( defaultLoadedFlag )
{
syncToLayer();
}
else
{
//let the user know what went wrong
QMessageBox::information( this, tr( "Save Style" ), errorMessage );
}
break;
}
case DB:
{
QString infoWindowTitle = QObject::tr( "Save style to DB (%1)" ).arg( mLayer->providerType() );
QgsVectorLayerSaveStyleDialog::SaveToDbSettings dbSettings = dlg.saveToDbSettings();
if ( QgsProviderRegistry::instance()->styleExists( mLayer->providerType(), mLayer->source(), dbSettings.name, errorMessage ) )
{
if ( QMessageBox::question( nullptr, QObject::tr( "Save style in database" ),
QObject::tr( "A matching style already exists in the database for this layer. Do you want to overwrite it?" ),
QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No )
{
return;
}
}
else if ( !errorMessage.isEmpty() )
{
mMessageBar->pushMessage( infoWindowTitle, errorMessage, Qgis::MessageLevel::Warning );
return;
}
mLayer->saveStyleToDatabase( dbSettings.name, dbSettings.description, dbSettings.isDefault, dbSettings.uiFileContent, errorMessage, dlg.styleCategories() );
if ( !errorMessage.isNull() )
{
mMessageBar->pushMessage( infoWindowTitle, errorMessage, Qgis::MessageLevel::Warning );
}
else
{
mMessageBar->pushMessage( infoWindowTitle, tr( "Style saved" ), Qgis::MessageLevel::Success );
}
break;
}
case Local:
{
QString infoWindowTitle = tr( "Save default style to local database" );
errorMessage = mLayer->saveDefaultStyle( defaultLoadedFlag, dlg.styleCategories() );
if ( !defaultLoadedFlag )
{
mMessageBar->pushMessage( infoWindowTitle, errorMessage, Qgis::MessageLevel::Warning );
}
else
{
mMessageBar->pushMessage( infoWindowTitle, tr( "Style saved" ), Qgis::MessageLevel::Success );
}
break;
}
}
}
}
void QgsVectorLayerProperties::saveMultipleStylesAs()
{
QgsMapLayerSaveStyleDialog dlg( mLayer );
QgsVectorLayerSaveStyleDialog dlg( mLayer );
dlg.setSaveOnlyCurrentStyle( false );
QgsSettings settings;
@ -1110,13 +1319,13 @@ void QgsVectorLayerProperties::saveMultipleStylesAs()
break;
}
case DatasourceDatabase:
case DB:
{
QString infoWindowTitle = QObject::tr( "Save style '%1' to DB (%2)" )
.arg( styleName, mLayer->providerType() );
QString msgError;
QgsMapLayerSaveStyleDialog::SaveToDbSettings dbSettings = dlg.saveToDbSettings();
QgsVectorLayerSaveStyleDialog::SaveToDbSettings dbSettings = dlg.saveToDbSettings();
// If a name is defined, we add _1 etc. else we use the style name
QString name { dbSettings.name };
@ -1149,7 +1358,7 @@ void QgsVectorLayerProperties::saveMultipleStylesAs()
}
else if ( !errorMessage.isEmpty() )
{
QMessageBox::warning( this, infoWindowTitle, errorMessage );
mMessageBar->pushMessage( infoWindowTitle, errorMessage, Qgis::MessageLevel::Warning );
return;
}
@ -1157,15 +1366,16 @@ void QgsVectorLayerProperties::saveMultipleStylesAs()
if ( !msgError.isNull() )
{
QMessageBox::warning( this, infoWindowTitle, msgError );
mMessageBar->pushMessage( infoWindowTitle, msgError, Qgis::MessageLevel::Warning );
}
else
{
QMessageBox::information( this, infoWindowTitle, tr( "Style '%1' saved" ).arg( styleName ) );
mMessageBar->pushMessage( infoWindowTitle, tr( "Style '%1' saved" ).arg( styleName ),
Qgis::MessageLevel::Success );
}
break;
}
case UserDatabase:
case Local:
break;
}
styleIndex ++;
@ -1205,6 +1415,98 @@ void QgsVectorLayerProperties::aboutToShowStyleMenu()
QgsMapLayerStyleGuiUtils::instance()->addStyleManagerActions( m, mLayer );
}
void QgsVectorLayerProperties::loadStyle()
{
QgsSettings settings; // where we keep last used filter in persistent state
QString errorMsg;
QStringList ids, names, descriptions;
//get the list of styles in the db
int sectionLimit = mLayer->listStylesInDatabase( ids, names, descriptions, errorMsg );
QgsMapLayerLoadStyleDialog dlg( mLayer, this );
dlg.initializeLists( ids, names, descriptions, sectionLimit );
if ( dlg.exec() )
{
mOldStyle = mLayer->styleManager()->style( mLayer->styleManager()->currentStyle() );
QgsMapLayer::StyleCategories categories = dlg.styleCategories();
StyleType type = dlg.currentStyleType();
bool defaultLoadedFlag = false;
switch ( type )
{
case QML:
case SLD:
{
QString filePath = dlg.filePath();
if ( type == SLD )
{
errorMsg = mLayer->loadSldStyle( filePath, defaultLoadedFlag );
}
else
{
errorMsg = mLayer->loadNamedStyle( filePath, defaultLoadedFlag, true, categories );
}
//reset if the default style was loaded OK only
if ( defaultLoadedFlag )
{
syncToLayer();
apply();
}
else
{
//let the user know what went wrong
QMessageBox::warning( this, tr( "Load Style" ), errorMsg );
}
break;
}
case DB:
{
QString selectedStyleId = dlg.selectedStyleId();
QString qmlStyle = mLayer->getStyleFromDatabase( selectedStyleId, errorMsg );
if ( !errorMsg.isNull() )
{
QMessageBox::warning( this, tr( "Load Styles from Database" ), errorMsg );
return;
}
QDomDocument myDocument( QStringLiteral( "qgis" ) );
myDocument.setContent( qmlStyle );
if ( mLayer->importNamedStyle( myDocument, errorMsg, categories ) )
{
syncToLayer();
apply();
}
else
{
QMessageBox::warning( this, tr( "Load Styles from Database" ),
tr( "The retrieved style is not a valid named style. Error message: %1" )
.arg( errorMsg ) );
}
break;
}
case Local:
{
errorMsg = mLayer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag, true, categories );
//reset if the default style was loaded OK only
if ( defaultLoadedFlag )
{
syncToLayer();
apply();
}
else
{
QMessageBox::warning( this, tr( "Load Default Style" ), errorMsg );
}
break;
}
}
activateWindow(); // set focus back to properties dialog
}
}
void QgsVectorLayerProperties::mButtonAddJoin_clicked()
{
if ( !mLayer )

View File

@ -59,11 +59,49 @@ class GUI_EXPORT QgsVectorLayerProperties : public QgsLayerPropertiesDialog, pri
Q_OBJECT
public:
#ifndef SIP_RUN
enum StyleType
{
QML,
SLD,
DB,
Local,
};
Q_ENUM( StyleType )
#endif
QgsVectorLayerProperties( QgsMapCanvas *canvas, QgsMessageBar *messageBar, QgsVectorLayer *lyr = nullptr, QWidget *parent = nullptr, Qt::WindowFlags fl = QgsGuiUtils::ModalDialogFlags );
bool eventFilter( QObject *obj, QEvent *ev ) override;
/**
* Loads the default style when appropriate button is pressed
*
* \since QGIS 3.30
*/
void loadDefaultStyle();
/**
* Saves the default style when appropriate button is pressed
*
* \since QGIS 3.30
*/
void saveDefaultStyle();
/**
* Loads a saved style when appropriate button is pressed
*
* \since QGIS 3.30
*/
void loadStyle();
/**
* Saves a style when appriate button is pressed
*
* \since QGIS 3.30
*/
void saveStyleAs();
protected slots:
void optionsStackedWidget_CurrentChanged( int index ) final;
void syncToLayer() FINAL;

View File

@ -1,5 +1,5 @@
/***************************************************************************
qgsmaplayersavestyledialog.h
qgsvectorlayersavestyledialog.h
--------------------------------------
Date : September 2018
Copyright : (C) 2018 by Denis Rouzaud
@ -15,17 +15,16 @@
#include <QListWidgetItem>
#include <QMessageBox>
#include <QPushButton>
#include "qgsmaplayersavestyledialog.h"
#include "qgsvectorlayersavestyledialog.h"
#include "qgsvectorlayer.h"
#include "qgssettings.h"
#include "qgshelp.h"
#include "qgsgui.h"
#include "qgsmaplayerstylecategoriesmodel.h"
#include "qgsmaplayerstylemanager.h"
#include "qgsvectorlayer.h"
QgsMapLayerSaveStyleDialog::QgsMapLayerSaveStyleDialog( QgsMapLayer *layer, QWidget *parent )
QgsVectorLayerSaveStyleDialog::QgsVectorLayerSaveStyleDialog( QgsVectorLayer *layer, QWidget *parent )
: QDialog( parent )
, mLayer( layer )
{
@ -39,28 +38,28 @@ QgsMapLayerSaveStyleDialog::QgsMapLayerSaveStyleDialog( QgsMapLayer *layer, QWid
// save style type combobox
connect( mStyleTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [ = ]( int )
{
const QgsLayerPropertiesDialog::StyleType type = currentStyleType();
mFileLabel->setVisible( type != QgsLayerPropertiesDialog::DatasourceDatabase && type != QgsLayerPropertiesDialog::UserDatabase );
mFileWidget->setVisible( type != QgsLayerPropertiesDialog::DatasourceDatabase && type != QgsLayerPropertiesDialog::UserDatabase );
mSaveToDbWidget->setVisible( type == QgsLayerPropertiesDialog::DatasourceDatabase );
mSaveToSldWidget->setVisible( type == QgsLayerPropertiesDialog::SLD && layer->type() == Qgis::LayerType::Vector && static_cast<QgsVectorLayer *>( layer )->geometryType() == Qgis::GeometryType::Polygon );
mStyleCategoriesListView->setEnabled( type != QgsLayerPropertiesDialog::SLD );
mFileWidget->setFilter( type == QgsLayerPropertiesDialog::QML ? tr( "QGIS Layer Style File (*.qml)" ) : tr( "SLD File (*.sld)" ) );
const QgsVectorLayerProperties::StyleType type = currentStyleType();
mFileLabel->setVisible( type != QgsVectorLayerProperties::DB && type != QgsVectorLayerProperties::Local );
mFileWidget->setVisible( type != QgsVectorLayerProperties::DB && type != QgsVectorLayerProperties::Local );
mSaveToDbWidget->setVisible( type == QgsVectorLayerProperties::DB );
mSaveToSldWidget->setVisible( type == QgsVectorLayerProperties::SLD && layer->geometryType() == Qgis::GeometryType::Polygon );
mStyleCategoriesListView->setEnabled( type != QgsVectorLayerProperties::SLD );
mFileWidget->setFilter( type == QgsVectorLayerProperties::QML ? tr( "QGIS Layer Style File (*.qml)" ) : tr( "SLD File (*.sld)" ) );
updateSaveButtonState();
} );
// Save to DB setup
connect( mDbStyleNameEdit, &QLineEdit::textChanged, this, &QgsMapLayerSaveStyleDialog::updateSaveButtonState );
connect( mDbStyleNameEdit, &QLineEdit::textChanged, this, &QgsVectorLayerSaveStyleDialog::updateSaveButtonState );
mDbStyleDescriptionEdit->setTabChangesFocus( true );
setTabOrder( mDbStyleNameEdit, mDbStyleDescriptionEdit );
setTabOrder( mDbStyleDescriptionEdit, mDbStyleUseAsDefault );
mDbStyleUIFileWidget->setDefaultRoot( myLastUsedDir );
mDbStyleUIFileWidget->setFilter( tr( "Qt Designer UI file (*.ui)" ) );
connect( mDbStyleUIFileWidget, &QgsFileWidget::fileChanged, this, &QgsMapLayerSaveStyleDialog::readUiFileContent );
connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsMapLayerSaveStyleDialog::showHelp );
connect( mDbStyleUIFileWidget, &QgsFileWidget::fileChanged, this, &QgsVectorLayerSaveStyleDialog::readUiFileContent );
connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsVectorLayerSaveStyleDialog::showHelp );
// save to file setup
connect( mFileWidget, &QgsFileWidget::fileChanged, this, &QgsMapLayerSaveStyleDialog::updateSaveButtonState );
connect( mFileWidget, &QgsFileWidget::fileChanged, this, &QgsVectorLayerSaveStyleDialog::updateSaveButtonState );
mFileWidget->setStorageMode( QgsFileWidget::SaveFile );
mFileWidget->setDefaultRoot( myLastUsedDir );
connect( mFileWidget, &QgsFileWidget::fileChanged, this, [ = ]( const QString & path )
@ -82,32 +81,40 @@ QgsMapLayerSaveStyleDialog::QgsMapLayerSaveStyleDialog( QgsMapLayer *layer, QWid
}
void QgsMapLayerSaveStyleDialog::populateStyleComboBox()
void QgsVectorLayerSaveStyleDialog::populateStyleComboBox()
{
mStyleTypeComboBox->clear();
mStyleTypeComboBox->addItem( tr( "As QGIS QML style file" ), QgsLayerPropertiesDialog::QML );
mStyleTypeComboBox->addItem( tr( "As SLD style file" ), QgsLayerPropertiesDialog::SLD );
QString providerName = mLayer->providerType();
if ( providerName == QLatin1String( "ogr" ) )
{
providerName = mLayer->dataProvider()->storageType();
if ( providerName == QLatin1String( "GPKG" ) )
providerName = QStringLiteral( "GeoPackage" );
}
if ( mLayer->dataProvider()->styleStorageCapabilities().testFlag( Qgis::ProviderStyleStorageCapability::SaveToDatabase ) )
mStyleTypeComboBox->addItem( tr( "In datasource database" ), QgsLayerPropertiesDialog::DatasourceDatabase );
mStyleTypeComboBox->clear();
mStyleTypeComboBox->addItem( tr( "As QGIS QML Style File" ), QgsVectorLayerProperties::QML );
mStyleTypeComboBox->addItem( tr( "As SLD Style File" ), QgsVectorLayerProperties::SLD );
if ( mLayer->dataProvider()->isSaveAndLoadStyleToDatabaseSupported() )
mStyleTypeComboBox->addItem( tr( "In Database (%1)" ).arg( providerName ), QgsVectorLayerProperties::DB );
if ( mSaveOnlyCurrentStyle )
mStyleTypeComboBox->addItem( tr( "As default in local user database" ), QgsLayerPropertiesDialog::UserDatabase );
mStyleTypeComboBox->addItem( tr( "As Default In Local Database" ), QgsVectorLayerProperties::Local );
}
void QgsMapLayerSaveStyleDialog::accept()
void QgsVectorLayerSaveStyleDialog::accept()
{
QgsSettings().setFlagValue( QStringLiteral( "style/lastStyleCategories" ), styleCategories() );
QDialog::accept();
}
void QgsMapLayerSaveStyleDialog::updateSaveButtonState()
void QgsVectorLayerSaveStyleDialog::updateSaveButtonState()
{
const QgsLayerPropertiesDialog::StyleType type = currentStyleType();
const QgsVectorLayerProperties::StyleType type = currentStyleType();
bool enabled { false };
switch ( type )
{
case QgsLayerPropertiesDialog::DatasourceDatabase:
case QgsVectorLayerProperties::DB:
if ( saveOnlyCurrentStyle( ) )
{
enabled = ! mDbStyleNameEdit->text().isEmpty();
@ -117,18 +124,18 @@ void QgsMapLayerSaveStyleDialog::updateSaveButtonState()
enabled = true;
}
break;
case QgsLayerPropertiesDialog::QML:
case QgsLayerPropertiesDialog::SLD:
case QgsVectorLayerProperties::QML:
case QgsVectorLayerProperties::SLD:
enabled = ! mFileWidget->filePath().isEmpty();
break;
case QgsLayerPropertiesDialog::UserDatabase:
case QgsVectorLayerProperties::Local:
enabled = true;
break;
}
buttonBox->button( QDialogButtonBox::Ok )->setEnabled( enabled );
}
QgsMapLayerSaveStyleDialog::SaveToDbSettings QgsMapLayerSaveStyleDialog::saveToDbSettings() const
QgsVectorLayerSaveStyleDialog::SaveToDbSettings QgsVectorLayerSaveStyleDialog::saveToDbSettings() const
{
SaveToDbSettings settings;
settings.name = mDbStyleNameEdit->text();
@ -138,22 +145,22 @@ QgsMapLayerSaveStyleDialog::SaveToDbSettings QgsMapLayerSaveStyleDialog::saveToD
return settings;
}
QString QgsMapLayerSaveStyleDialog::outputFilePath() const
QString QgsVectorLayerSaveStyleDialog::outputFilePath() const
{
return mFileWidget->filePath();
}
QgsMapLayer::StyleCategories QgsMapLayerSaveStyleDialog::styleCategories() const
QgsMapLayer::StyleCategories QgsVectorLayerSaveStyleDialog::styleCategories() const
{
return mModel->categories();
}
QgsLayerPropertiesDialog::StyleType QgsMapLayerSaveStyleDialog::currentStyleType() const
QgsVectorLayerProperties::StyleType QgsVectorLayerSaveStyleDialog::currentStyleType() const
{
return mStyleTypeComboBox->currentData().value<QgsLayerPropertiesDialog::StyleType>();
return mStyleTypeComboBox->currentData().value<QgsVectorLayerProperties::StyleType>();
}
void QgsMapLayerSaveStyleDialog::readUiFileContent( const QString &filePath )
void QgsVectorLayerSaveStyleDialog::readUiFileContent( const QString &filePath )
{
QgsSettings myQSettings; // where we keep last used filter in persistent state
mUiFileContent = QString();
@ -184,7 +191,7 @@ void QgsMapLayerSaveStyleDialog::readUiFileContent( const QString &filePath )
}
}
void QgsMapLayerSaveStyleDialog::setupMultipleStyles()
void QgsVectorLayerSaveStyleDialog::setupMultipleStyles()
{
// Show/hide part of the UI according to multiple style support
if ( ! mSaveOnlyCurrentStyle )
@ -222,12 +229,12 @@ void QgsMapLayerSaveStyleDialog::setupMultipleStyles()
populateStyleComboBox();
}
bool QgsMapLayerSaveStyleDialog::saveOnlyCurrentStyle() const
bool QgsVectorLayerSaveStyleDialog::saveOnlyCurrentStyle() const
{
return mSaveOnlyCurrentStyle;
}
void QgsMapLayerSaveStyleDialog::setSaveOnlyCurrentStyle( bool saveOnlyCurrentStyle )
void QgsVectorLayerSaveStyleDialog::setSaveOnlyCurrentStyle( bool saveOnlyCurrentStyle )
{
if ( mSaveOnlyCurrentStyle != saveOnlyCurrentStyle )
{
@ -236,23 +243,23 @@ void QgsMapLayerSaveStyleDialog::setSaveOnlyCurrentStyle( bool saveOnlyCurrentSt
}
}
const QListWidget *QgsMapLayerSaveStyleDialog::stylesWidget()
const QListWidget *QgsVectorLayerSaveStyleDialog::stylesWidget()
{
return mStylesWidget;
}
Qgis::SldExportOptions QgsMapLayerSaveStyleDialog::sldExportOptions() const
Qgis::SldExportOptions QgsVectorLayerSaveStyleDialog::sldExportOptions() const
{
Qgis::SldExportOptions options;
if ( mStyleTypeComboBox->currentData( ) == QgsLayerPropertiesDialog::SLD )
if ( mStyleTypeComboBox->currentData( ) == QgsVectorLayerProperties::SLD )
{
options.setFlag( Qgis::SldExportOption::Png );
}
return options;
}
void QgsMapLayerSaveStyleDialog::showHelp()
void QgsVectorLayerSaveStyleDialog::showHelp()
{
QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#save-and-share-layer-properties" ) );
}

View File

@ -1,5 +1,5 @@
/***************************************************************************
qgsmaplayersavestyledialog.h
qgsvectorlayersavestyledialog.h
--------------------------------------
Date : September 2018
Copyright : (C) 2018 by Denis Rouzaud
@ -13,31 +13,29 @@
* *
***************************************************************************/
#ifndef QGSMAPLAYERSAVESTYLEDIALOG_H
#define QGSMAPLAYERSAVESTYLEDIALOG_H
#ifndef QGSVECTORLAYERSAVESTYLEDIALOG_H
#define QGSVECTORLAYERSAVESTYLEDIALOG_H
// We don't want to expose this in the public API
#define SIP_NO_FILE
#include <QDialog>
#include "ui_qgsmaplayersavestyledialog.h"
#include "qgsmaplayer.h"
#include "qgslayerpropertiesdialog.h"
#include "ui_qgsvectorlayersavestyledialog.h"
#include "qgsvectorlayerproperties.h"
#include "qgis_gui.h"
class QgsVectorLayer;
class QgsMapLayerStyleCategoriesModel;
/**
* \ingroup gui
* \class QgsMapLayerSaveStyleDialog
* \class QgsVectorLayerSaveStyleDialog
*
* \brief The QgsMapLayerSaveStyleDialog class provides the UI to save the current style
* \brief The QgsVectorLayerSaveStyleDialog class provides the UI to save the current style
* or multiple styles into different storage containers (QML, SLD and DB).
* The user can select what categories must be saved.
*
* \since QGIS 3.34
*/
class GUI_EXPORT QgsMapLayerSaveStyleDialog : public QDialog, private Ui::QgsMapLayerSaveStyleDialog
class GUI_EXPORT QgsVectorLayerSaveStyleDialog : public QDialog, private Ui::QgsVectorLayerSaveStyleDialog
{
Q_OBJECT
@ -52,48 +50,17 @@ class GUI_EXPORT QgsMapLayerSaveStyleDialog : public QDialog, private Ui::QgsMap
bool isDefault;
};
/**
* Constructor
*/
explicit QgsMapLayerSaveStyleDialog( QgsMapLayer *layer, QWidget *parent = nullptr );
explicit QgsVectorLayerSaveStyleDialog( QgsVectorLayer *layer, QWidget *parent = nullptr );
/**
* Returns the database settings for saving the style in the DB.
*/
SaveToDbSettings saveToDbSettings() const;
/**
* Returns the selected file output path.
*/
QString outputFilePath() const;
/**
* Returns the available style categories.
*/
QgsMapLayer::StyleCategories styleCategories() const;
/**
* Returns the selected style storage type.
*/
QgsLayerPropertiesDialog::StyleType currentStyleType() const;
QgsVectorLayerProperties::StyleType currentStyleType() const;
/**
* Returns whether the user only allowed to save the current style.
*
* \see setSaveOnlyCurrentStyle()
*/
bool saveOnlyCurrentStyle() const;
/**
* Sets whether the user only allowed to save the current style.
*
* \see saveOnlyCurrentStyle()
*/
void setSaveOnlyCurrentStyle( bool saveCurrentStyle );
/**
* Returns the styles list widget.
*/
const QListWidget *stylesWidget( );
/**
@ -113,10 +80,10 @@ class GUI_EXPORT QgsMapLayerSaveStyleDialog : public QDialog, private Ui::QgsMap
private:
void setupMultipleStyles();
void populateStyleComboBox();
QgsMapLayer *mLayer = nullptr;
QgsVectorLayer *mLayer = nullptr;
QgsMapLayerStyleCategoriesModel *mModel;
QString mUiFileContent;
bool mSaveOnlyCurrentStyle = true;
};
#endif // QGSMAPLAYERSAVESTYLEDIALOG_H
#endif // QGSVECTORLAYERSAVESTYLE_H

View File

@ -1129,17 +1129,6 @@ bool QgsMssqlProvider::isValid() const
return mValid;
}
Qgis::ProviderStyleStorageCapabilities QgsMssqlProvider::styleStorageCapabilities() const
{
Qgis::ProviderStyleStorageCapabilities storageCapabilities;
if ( isValid() )
{
storageCapabilities |= Qgis::ProviderStyleStorageCapability::SaveToDatabase;
storageCapabilities |= Qgis::ProviderStyleStorageCapability::LoadFromDatabase;
}
return storageCapabilities;
}
bool QgsMssqlProvider::addFeatures( QgsFeatureList &flist, Flags flags )
{
for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end(); ++it )

View File

@ -115,7 +115,7 @@ class QgsMssqlProvider final: public QgsVectorDataProvider
bool isValid() const override;
Qgis::ProviderStyleStorageCapabilities styleStorageCapabilities() const override;
bool isSaveAndLoadStyleToDatabaseSupported() const override { return true; }
bool addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flags flags = QgsFeatureSink::Flags() ) override;

View File

@ -337,14 +337,6 @@ bool QgsOracleProvider::execLoggedStatic( QSqlQuery &qry, const QString &sql, co
return res;
}
Qgis::ProviderStyleStorageCapabilities QgsOracleProvider::styleStorageCapabilities() const
{
Qgis::ProviderStyleStorageCapabilities storageCapabilities;
storageCapabilities |= Qgis::ProviderStyleStorageCapability::SaveToDatabase;
storageCapabilities |= Qgis::ProviderStyleStorageCapability::LoadFromDatabase;
return storageCapabilities;
}
void QgsOracleProvider::setTransaction( QgsTransaction *transaction )
{
// static_cast since layers cannot be added to a transaction of a non-matching provider

View File

@ -185,7 +185,7 @@ class QgsOracleProvider final: public QgsVectorDataProvider
static bool execLoggedStatic( QSqlQuery &qry, const QString &sql, const QVariantList &args, const QString &uri, const QString &originatorClass = QString(), const QString &queryOrigin = QString() );
Qgis::ProviderStyleStorageCapabilities styleStorageCapabilities() const override;
bool isSaveAndLoadStyleToDatabaseSupported() const override { return true; }
void setTransaction( QgsTransaction *transaction ) override;
QgsTransaction *transaction() const override;

View File

@ -2269,18 +2269,6 @@ bool QgsPostgresProvider::isValid() const
return mValid;
}
Qgis::ProviderStyleStorageCapabilities QgsPostgresProvider::styleStorageCapabilities() const
{
Qgis::ProviderStyleStorageCapabilities storageCapabilities;
if ( isValid() )
{
storageCapabilities |= Qgis::ProviderStyleStorageCapability::SaveToDatabase;
storageCapabilities |= Qgis::ProviderStyleStorageCapability::LoadFromDatabase;
storageCapabilities |= Qgis::ProviderStyleStorageCapability::DeleteFromDatabase;
}
return storageCapabilities;
}
QString QgsPostgresProvider::defaultValueClause( int fieldId ) const
{
QString defVal = mDefaultValues.value( fieldId, QString() );

View File

@ -154,7 +154,8 @@ class QgsPostgresProvider final: public QgsVectorDataProvider
QgsFeedback *feedback = nullptr ) const override;
void enumValues( int index, QStringList &enumList ) const override;
bool isValid() const override;
Qgis::ProviderStyleStorageCapabilities styleStorageCapabilities() const override;
bool isSaveAndLoadStyleToDatabaseSupported() const override { return true; }
bool isDeleteStyleFromDatabaseSupported() const override { return true; }
QgsAttributeList attributeIndexes() const override;
QgsAttributeList pkAttributeIndexes() const override { return mPrimaryKeyAttrs; }
QString defaultValueClause( int fieldId ) const override;

View File

@ -3781,15 +3781,9 @@ bool QgsSpatiaLiteProvider::isValid() const
return mValid;
}
Qgis::ProviderStyleStorageCapabilities QgsSpatiaLiteProvider::styleStorageCapabilities() const
bool QgsSpatiaLiteProvider::isSaveAndLoadStyleToDatabaseSupported() const
{
Qgis::ProviderStyleStorageCapabilities storageCapabilities;
if ( isValid() )
{
storageCapabilities |= Qgis::ProviderStyleStorageCapability::SaveToDatabase;
storageCapabilities |= Qgis::ProviderStyleStorageCapability::LoadFromDatabase;
}
return storageCapabilities;
return mValid;
}
QString QgsSpatiaLiteProvider::name() const

View File

@ -114,7 +114,7 @@ class QgsSpatiaLiteProvider final: public QgsVectorDataProvider
QgsFeedback *feedback = nullptr ) const override;
bool isValid() const override;
Qgis::ProviderStyleStorageCapabilities styleStorageCapabilities() const override;
bool isSaveAndLoadStyleToDatabaseSupported() const override;
bool addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flags flags = QgsFeatureSink::Flags() ) override;
bool deleteFeatures( const QgsFeatureIds &id ) override;
bool truncate() override;

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsMapLayerSaveStyleDialog</class>
<widget class="QDialog" name="QgsMapLayerSaveStyleDialog">
<class>QgsVectorLayerSaveStyleDialog</class>
<widget class="QDialog" name="QgsVectorLayerSaveStyleDialog">
<property name="geometry">
<rect>
<x>0</x>
@ -200,7 +200,7 @@
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QgsMapLayerSaveStyleDialog</receiver>
<receiver>QgsVectorLayerSaveStyleDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
@ -216,7 +216,7 @@
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QgsMapLayerSaveStyleDialog</receiver>
<receiver>QgsVectorLayerSaveStyleDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">

View File

@ -15,7 +15,6 @@ import qgis # NOQA
from qgis.PyQt.QtCore import QDate, QDateTime, QDir, QTime, QVariant
from qgis.core import (
NULL,
Qgis,
QgsCoordinateReferenceSystem,
QgsDataProvider,
QgsDataSourceUri,
@ -404,8 +403,8 @@ class TestPyQgsMssqlProvider(QgisTestCase, ProviderTestCase):
vl = self.getSource()
self.assertTrue(vl.isValid())
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.LoadFromDatabase, Qgis.ProviderStyleStorageCapability.LoadFromDatabase)
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.SaveToDatabase, Qgis.ProviderStyleStorageCapability.SaveToDatabase)
self.assertTrue(
vl.dataProvider().isSaveAndLoadStyleToDatabaseSupported())
# table layer_styles does not exist

View File

@ -667,8 +667,7 @@ class TestPyQgsOGRProviderGpkg(QgisTestCase):
# First test with invalid URI
vl = QgsVectorLayer('/idont/exist.gpkg', 'test', 'ogr')
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.LoadFromDatabase, 0)
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.SaveToDatabase, 0)
self.assertFalse(vl.dataProvider().isSaveAndLoadStyleToDatabaseSupported())
res, err = QgsProviderRegistry.instance().styleExists('ogr', '/idont/exist.gpkg', '')
self.assertFalse(res)
@ -717,8 +716,7 @@ class TestPyQgsOGRProviderGpkg(QgisTestCase):
vl2 = QgsVectorLayer(f'{tmpfile}|layername=test2', 'test2', 'ogr')
self.assertTrue(vl2.isValid())
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.LoadFromDatabase, Qgis.ProviderStyleStorageCapability.LoadFromDatabase)
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.SaveToDatabase, Qgis.ProviderStyleStorageCapability.SaveToDatabase)
self.assertTrue(vl.dataProvider().isSaveAndLoadStyleToDatabaseSupported())
# style tables don't exist yet
res, err = QgsProviderRegistry.instance().styleExists('ogr', vl.source(), '')
@ -1157,8 +1155,7 @@ class TestPyQgsOGRProviderGpkg(QgisTestCase):
if count > 0:
# We should have just 1 but for obscure reasons
# uniqueFields() (sometimes?) leaves one behind
# Even more obscure reasons a third FD remains open
self.assertIn(count, (1, 2, 3))
self.assertIn(count, (1, 2))
for i in range(70):
got = [feat for feat in vl.getFeatures()]
@ -1169,7 +1166,7 @@ class TestPyQgsOGRProviderGpkg(QgisTestCase):
# one shared by the feature iterators
count = count_opened_filedescriptors(tmpfile)
if count > 0:
self.assertIn(count, (2, 3))
self.assertEqual(count, 2)
# Re-open an already opened layers. We should get a new handle
layername = 'layer%d' % 0

View File

@ -1941,9 +1941,9 @@ class TestPyQgsPostgresProvider(QgisTestCase, ProviderTestCase):
vl = self.getEditableLayer()
self.assertTrue(vl.isValid())
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.LoadFromDatabase, Qgis.ProviderStyleStorageCapability.LoadFromDatabase)
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.SaveToDatabase, Qgis.ProviderStyleStorageCapability.SaveToDatabase)
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.DeleteFromDatabase, Qgis.ProviderStyleStorageCapability.DeleteFromDatabase)
self.assertTrue(
vl.dataProvider().isSaveAndLoadStyleToDatabaseSupported())
self.assertTrue(vl.dataProvider().isDeleteStyleFromDatabaseSupported())
# table layer_styles does not exist
@ -2125,9 +2125,9 @@ class TestPyQgsPostgresProvider(QgisTestCase, ProviderTestCase):
vl = self.getEditableLayer()
self.assertTrue(vl.isValid())
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.LoadFromDatabase, Qgis.ProviderStyleStorageCapability.LoadFromDatabase)
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.SaveToDatabase, Qgis.ProviderStyleStorageCapability.SaveToDatabase)
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.DeleteFromDatabase, Qgis.ProviderStyleStorageCapability.DeleteFromDatabase)
self.assertTrue(
vl.dataProvider().isSaveAndLoadStyleToDatabaseSupported())
self.assertTrue(vl.dataProvider().isDeleteStyleFromDatabaseSupported())
mFilePath = QDir.toNativeSeparators(
f"{unitTestDataPath()}/symbol_layer/fontSymbol.qml")

View File

@ -1096,8 +1096,7 @@ class TestQgsSpatialiteProvider(QgisTestCase, ProviderTestCase):
# First test with invalid URI
vl = QgsVectorLayer('/idont/exist.sqlite', 'test', 'spatialite')
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.LoadFromDatabase, 0)
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.SaveToDatabase, 0)
self.assertFalse(vl.dataProvider().isSaveAndLoadStyleToDatabaseSupported())
res, err = QgsProviderRegistry.instance().styleExists('spatialite', '/idont/exist.sqlite', '')
self.assertFalse(res)
@ -1151,8 +1150,7 @@ class TestQgsSpatialiteProvider(QgisTestCase, ProviderTestCase):
vl = QgsVectorLayer(testPath, 'test', 'spatialite')
self.assertTrue(vl.isValid())
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.LoadFromDatabase, Qgis.ProviderStyleStorageCapability.LoadFromDatabase)
self.assertEqual(int(vl.dataProvider().styleStorageCapabilities()) & Qgis.ProviderStyleStorageCapability.SaveToDatabase, Qgis.ProviderStyleStorageCapability.SaveToDatabase)
self.assertTrue(vl.dataProvider().isSaveAndLoadStyleToDatabaseSupported())
# style tables don't exist yet
res, err = QgsProviderRegistry.instance().styleExists('spatialite', vl.source(), '')