load and save metadata to a QMD file

This commit is contained in:
Etienne Trimaille 2018-01-02 18:58:05 +03:00 committed by Nyall Dawson
parent a343570eab
commit 3432bf8f3b
12 changed files with 844 additions and 69 deletions

View File

@ -55,6 +55,12 @@ This is the base class for all map layer types (vector, raster).
PluginLayer
};
enum PropertyType
{
Style,
Metadata,
};
QgsMapLayer( QgsMapLayer::LayerType type = VectorLayer, const QString &name = QString(), const QString &source = QString() );
%Docstring
Constructor for QgsMapLayer
@ -80,6 +86,15 @@ is still unique.
QgsMapLayer::LayerType type() const;
%Docstring
Returns the type of the layer.
%End
static QString extensionPropertyType( PropertyType type );
%Docstring
Returns the extension of a Property.
:return: The extension
.. versionadded:: 3.0
%End
QString id() const;
@ -545,6 +560,118 @@ Sets layer's spatial reference system
%Docstring
A convenience function to capitalize and format a layer ``name``.
.. versionadded:: 3.0
%End
virtual QString metadataUri() const;
%Docstring
Retrieve the metadata URI for this layer
(either as a .qmd file on disk or as a
record in the users style table in their personal qgis.db)
:return: a QString with the metadata file name
.. versionadded:: 3.0
%End
void exportNamedMetadata( QDomDocument &doc, QString &errorMsg ) const;
%Docstring
Export the current metadata of this layer as named metadata in a QDomDocument
:param doc: the target QDomDocument
:param errorMsg: this QString will be initialized on error
.. versionadded:: 3.0
%End
virtual QString saveDefaultMetadata( bool &resultFlag /Out/ );
%Docstring
Save the current metadata of this layer as the default metadata
(either as a .qmd file on disk or as a
record in the users style table in their personal qgis.db)
:param resultFlag: a reference to a flag that will be set to false if
we did not manage to save the default metadata.
:return: a QString with any status messages
.. versionadded:: 3.0
%End
QString saveNamedMetadata( const QString &uri, bool &resultFlag );
%Docstring
Save the current metadata of this layer as a named metadata
(either as a .qmd file on disk or as a
record in the users style table in their personal qgis.db)
:param uri: the file name or other URI for the
metadata file. First an attempt will be made to see if this
is a file and save to that, if that fails the qgis.db metadata
table will be used to create a metadata entry who's
key matches the URI.
:param resultFlag: a reference to a flag that will be set to false if
we did not manage to save the default metadata.
:return: a QString with any status messages
.. versionadded:: 3.0
%End
virtual QString loadNamedMetadata( const QString &uri, bool &resultFlag /Out/ );
%Docstring
Retrieve a named metadata for this layer if one
exists (either as a .qmd file on disk or as a
record in the users style table in their personal qgis.db)
:param uri: - the file name or other URI for the
metadata file. First an attempt will be made to see if this
is a file and load that, if that fails the qgis.db metadata
table will be consulted to see if there is a metadata who's
key matches the URI.
:param resultFlag: a reference to a flag that will be set to false if
we did not manage to load the default metadata.
:return: a QString with any status messages
.. versionadded:: 3.0
%End
QString loadDefaultMetadata( bool &resultFlag );
%Docstring
Retrieve the default metadata for this layer if one
exists (either as a .qmd file on disk or as a
record in the users metadata table in their personal qgis.db)
:param resultFlag: a reference to a flag that will be set to false if
we did not manage to load the default metadata.
:return: a QString with any status messages
.. versionadded:: 3.0
%End
bool loadNamedMetadataFromDatabase( const QString &db, const QString &uri, QString &qmd );
%Docstring
Retrieve a named metadata for this layer from a sqlite database.
:param db: path to sqlite database
:param uri: uri for table
:param qmd: will be set to QMD xml metadata content from database
:return: true if style was successfully loaded
.. versionadded:: 3.0
%End
bool importNamedMetadata( QDomDocument &document, QString &errorMessage );
%Docstring
Import the metadata of this layer from a QDomDocument
:param document: source QDomDocument
:param errorMessage: this QString will be initialized on error
:return: true on success
.. versionadded:: 3.0
%End

View File

@ -46,6 +46,11 @@ If the CRS is updated.
void acceptMetadata();
%Docstring
Saves the metadata to the layer.
%End
virtual void setMetadata( const QgsLayerMetadata &metadata );
%Docstring
Sets the layer's ``metadata`` store.
%End
static QMap<QString, QString> parseLanguages();

View File

@ -4321,6 +4321,7 @@ bool QgisApp::addVectorLayers( const QStringList &layerQStringList, const QStrin
{
bool ok;
l->loadDefaultStyle( ok );
l->loadDefaultMetadata( ok );
}
activateDeactivateLayerRelatedActions( activeLayer() );
@ -4747,6 +4748,7 @@ void QgisApp::askUserForOGRSublayers( QgsVectorLayer *layer )
{
bool ok;
l->loadDefaultStyle( ok );
l->loadDefaultMetadata( ok );
if ( addToGroup )
group->addLayer( l );
}
@ -4839,6 +4841,7 @@ void QgisApp::addDatabaseLayers( QStringList const &layerPathList, QString const
{
bool ok;
l->loadDefaultStyle( ok );
l->loadDefaultMetadata( ok );
}
// draw the map
@ -10249,6 +10252,7 @@ QgsVectorLayer *QgisApp::addVectorLayer( const QString &vectorLayerPath, const Q
QgsProject::instance()->addMapLayers( myList );
bool ok;
layer->loadDefaultStyle( ok );
layer->loadDefaultMetadata( ok );
}
}
else

View File

@ -104,16 +104,26 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv
connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsRasterLayerProperties::showHelp );
QPushButton *b = new QPushButton( tr( "Style" ) );
QMenu *m = new QMenu( this );
m->addAction( tr( "Load Style..." ), this, SLOT( loadStyle_clicked() ) );
m->addAction( tr( "Save Style..." ), this, SLOT( saveStyleAs_clicked() ) );
m->addSeparator();
m->addAction( tr( "Save as Default" ), this, SLOT( saveDefaultStyle_clicked() ) );
m->addAction( tr( "Restore Default" ), this, SLOT( loadDefaultStyle_clicked() ) );
b->setMenu( m );
connect( m, &QMenu::aboutToShow, this, &QgsRasterLayerProperties::aboutToShowStyleMenu );
buttonBox->addButton( b, QDialogButtonBox::ResetRole );
mBtnStyle = new QPushButton( tr( "Style" ) );
QMenu *menuStyle = new QMenu( this );
menuStyle->addAction( tr( "Load Style..." ), this, SLOT( loadStyle_clicked() ) );
menuStyle->addAction( tr( "Save Style..." ), this, SLOT( saveStyleAs_clicked() ) );
menuStyle->addSeparator();
menuStyle->addAction( tr( "Save as Default" ), this, SLOT( saveDefaultStyle_clicked() ) );
menuStyle->addAction( tr( "Restore Default" ), this, SLOT( loadDefaultStyle_clicked() ) );
mBtnStyle->setMenu( menuStyle );
connect( menuStyle, &QMenu::aboutToShow, this, &QgsRasterLayerProperties::aboutToShowStyleMenu );
buttonBox->addButton( mBtnStyle, QDialogButtonBox::ResetRole );
mBtnMetadata = new QPushButton( tr( "Metadata" ), this );
QMenu *menuMetadata = new QMenu( this );
mActionLoadMetadata = menuMetadata->addAction( tr( "Load Metadata" ), this, SLOT( loadMetadata() ) );
mActionSaveMetadataAs = menuMetadata->addAction( tr( "Save Metadata" ), this, SLOT( saveMetadataAs() ) );
menuMetadata->addSeparator();
menuMetadata->addAction( tr( "Save as Default" ), this, SLOT( saveDefaultMetadata() ) );
menuMetadata->addAction( tr( "Restore Default" ), this, SLOT( loadDefaultMetadata() ) );
mBtnMetadata->setMenu( menuMetadata );
buttonBox->addButton( mBtnMetadata, QDialogButtonBox::ResetRole );
connect( lyr->styleManager(), &QgsMapLayerStyleManager::currentStyleChanged, this, &QgsRasterLayerProperties::syncToLayer );
@ -436,6 +446,7 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv
QString title = QString( tr( "Layer Properties - %1" ) ).arg( lyr->name() );
restoreOptionsBaseUi( title );
optionsStackedWidget_CurrentChanged( mOptionsStackedWidget->currentIndex() );
} // QgsRasterLayerProperties ctor
@ -1488,6 +1499,10 @@ void QgsRasterLayerProperties::optionsStackedWidget_CurrentChanged( int index )
{
QgsOptionsDialogBase::optionsStackedWidget_CurrentChanged( index );
bool isMetadataPanel = ( index == mOptStackedWidget->indexOf( mOptsPage_Metadata ) );
mBtnStyle->setVisible( ! isMetadataPanel );
mBtnMetadata->setVisible( isMetadataPanel );
if ( !mHistogramWidget )
return;
@ -1856,6 +1871,102 @@ void QgsRasterLayerProperties::saveStyleAs_clicked()
QMessageBox::information( this, tr( "Saved Style" ), message );
}
//
//
// Next four methods for saving and restoring QMD metadata
//
//
void QgsRasterLayerProperties::loadMetadata()
{
QgsSettings myQSettings; // where we keep last used filter in persistent state
QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load layer metadata from metadata file" ), myLastUsedDir,
tr( "QGIS Layer Metadata File" ) + " (*.qmd)" );
if ( myFileName.isNull() )
{
return;
}
QString myMessage;
bool defaultLoadedFlag = false;
myMessage = mRasterLayer->loadNamedMetadata( myFileName, defaultLoadedFlag );
//reset if the default style was loaded OK only
if ( defaultLoadedFlag )
{
mMetadataWidget->setMetadata( mRasterLayer->metadata() );
}
else
{
//let the user know what went wrong
QMessageBox::warning( this, tr( "Load Metadata" ), myMessage );
}
QFileInfo myFI( myFileName );
QString myPath = myFI.path();
myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), myPath );
activateWindow(); // set focus back to properties dialog
}
void QgsRasterLayerProperties::saveMetadataAs()
{
QgsSettings myQSettings; // where we keep last used filter in persistent state
QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
QString myOutputFileName = QFileDialog::getSaveFileName( this, tr( "Save layer metadata as QMD" ),
myLastUsedDir, tr( "QMD File" ) + " (*.qmd)" );
if ( myOutputFileName.isNull() ) //dialog canceled
{
return;
}
mMetadataWidget->acceptMetadata();
//ensure the user never omitted the extension from the file name
if ( !myOutputFileName.endsWith( QgsMapLayer::extensionPropertyType( QgsMapLayer::Metadata ), Qt::CaseInsensitive ) )
{
myOutputFileName += QgsMapLayer::extensionPropertyType( QgsMapLayer::Metadata );
}
bool defaultLoadedFlag = false;
QString message = mRasterLayer->saveNamedMetadata( myOutputFileName, defaultLoadedFlag );
if ( defaultLoadedFlag )
myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), QFileInfo( myOutputFileName ).absolutePath() );
else
QMessageBox::information( this, tr( "Saved Metadata" ), message );
}
void QgsRasterLayerProperties::saveDefaultMetadata()
{
mMetadataWidget->acceptMetadata();
bool defaultSavedFlag = false;
QString errorMsg = mRasterLayer->saveDefaultMetadata( defaultSavedFlag );
if ( !defaultSavedFlag )
{
QMessageBox::warning( this, tr( "Default Metadata" ), errorMsg );
}
}
void QgsRasterLayerProperties::loadDefaultMetadata()
{
bool defaultLoadedFlag = false;
QString myMessage = mRasterLayer->loadNamedMetadata( mRasterLayer->metadataUri(), defaultLoadedFlag );
//reset if the default metadata was loaded OK only
if ( defaultLoadedFlag )
{
mMetadataWidget->setMetadata( mRasterLayer->metadata() );
}
else
{
QMessageBox::information( this, tr( "Default Metadata" ), myMessage );
}
}
void QgsRasterLayerProperties::toggleBuildPyramidsButton()
{
if ( lbxPyramidResolutions->selectedItems().empty() )

View File

@ -103,6 +103,16 @@ class APP_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private
void loadStyle_clicked();
//! Save a style when appriate button is pressed.
void saveStyleAs_clicked();
//! Load a saved metadata file.
void loadMetadata();
//! Save a metadata.
void saveMetadataAs();
//! Save the default metadata.
void saveDefaultMetadata();
//! Load the default metadata.
void loadDefaultMetadata();
//! Help button
void showHelp();
@ -131,6 +141,11 @@ class APP_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private
void refreshLegend( const QString &layerID, bool expandItem );
private:
QPushButton *mBtnStyle = nullptr;
QPushButton *mBtnMetadata = nullptr;
QAction *mActionLoadMetadata = nullptr;
QAction *mActionSaveMetadataAs = nullptr;
//! \brief A constant that signals property not used
const QString TRSTRING_NOT_SET;

View File

@ -111,16 +111,26 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
// and connecting QDialogButtonBox's accepted/rejected signals to dialog's accept/reject slots
initOptionsBase( false );
QPushButton *b = new QPushButton( tr( "Style" ) );
QMenu *m = new QMenu( this );
mActionLoadStyle = m->addAction( tr( "Load Style" ), this, SLOT( loadStyle_clicked() ) );
mActionSaveStyleAs = m->addAction( tr( "Save Style" ), this, SLOT( saveStyleAs_clicked() ) );
m->addSeparator();
m->addAction( tr( "Save as Default" ), this, SLOT( saveDefaultStyle_clicked() ) );
m->addAction( tr( "Restore Default" ), this, SLOT( loadDefaultStyle_clicked() ) );
b->setMenu( m );
connect( m, &QMenu::aboutToShow, this, &QgsVectorLayerProperties::aboutToShowStyleMenu );
buttonBox->addButton( b, QDialogButtonBox::ResetRole );
mBtnStyle = new QPushButton( tr( "Style" ), this );
QMenu *menuStyle = new QMenu( this );
mActionLoadStyle = menuStyle->addAction( tr( "Load Style" ), this, SLOT( loadStyle_clicked() ) );
mActionSaveStyleAs = menuStyle->addAction( tr( "Save Style" ), this, SLOT( saveStyleAs_clicked() ) );
menuStyle->addSeparator();
menuStyle->addAction( tr( "Save as Default" ), this, SLOT( saveDefaultStyle_clicked() ) );
menuStyle->addAction( tr( "Restore Default" ), this, SLOT( loadDefaultStyle_clicked() ) );
mBtnStyle->setMenu( menuStyle );
connect( menuStyle, &QMenu::aboutToShow, this, &QgsVectorLayerProperties::aboutToShowStyleMenu );
buttonBox->addButton( mBtnStyle, QDialogButtonBox::ResetRole );
mBtnMetadata = new QPushButton( tr( "Metadata" ), this );
QMenu *menuMetadata = new QMenu( this );
mActionLoadMetadata = menuMetadata->addAction( tr( "Load Metadata" ), this, SLOT( loadMetadata() ) );
mActionSaveMetadataAs = menuMetadata->addAction( tr( "Save Metadata" ), this, SLOT( saveMetadataAs() ) );
menuMetadata->addSeparator();
menuMetadata->addAction( tr( "Save as Default" ), this, SLOT( saveDefaultMetadata() ) );
menuMetadata->addAction( tr( "Restore Default" ), this, SLOT( loadDefaultMetadata() ) );
mBtnMetadata->setMenu( menuMetadata );
buttonBox->addButton( mBtnMetadata, QDialogButtonBox::ResetRole );
connect( lyr->styleManager(), &QgsMapLayerStyleManager::currentStyleChanged, this, &QgsVectorLayerProperties::syncToLayer );
@ -227,6 +237,15 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
delete mOptsPage_3DView; // removes both the "3d view" list item and its page
#endif
// Metadata tab, before the syncToLayer
QVBoxLayout *metadataLayout = new QVBoxLayout( metadataFrame );
metadataLayout->setMargin( 0 );
mMetadataWidget = new QgsMetadataWidget( this, mLayer );
mMetadataWidget->layout()->setContentsMargins( -1, 0, -1, 0 );
mMetadataWidget->setMapCanvas( QgisApp::instance()->mapCanvas() );
metadataLayout->addWidget( mMetadataWidget );
metadataFrame->setLayout( metadataLayout );
syncToLayer();
if ( mLayer->dataProvider() )//enable spatial index button group if supported by provider
@ -282,15 +301,6 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
diagLayout->addWidget( diagramPropertiesDialog );
mDiagramFrame->setLayout( diagLayout );
// Metadata tab
QVBoxLayout *metadataLayout = new QVBoxLayout( metadataFrame );
metadataLayout->setMargin( 0 );
mMetadataWidget = new QgsMetadataWidget( this, mLayer );
mMetadataWidget->layout()->setContentsMargins( -1, 0, -1, 0 );
mMetadataWidget->setMapCanvas( QgisApp::instance()->mapCanvas() );
metadataLayout->addWidget( mMetadataWidget );
metadataFrame->setLayout( metadataLayout );
// Legend tab
mLegendConfigEmbeddedWidget->setLayer( mLayer );
@ -401,6 +411,8 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
connect( mAuxiliaryStorageFieldsAddBtn, &QPushButton::clicked, this, &QgsVectorLayerProperties::onAuxiliaryLayerAddField );
updateAuxiliaryStoragePage();
optionsStackedWidget_CurrentChanged( mOptStackedWidget->currentIndex() );
}
void QgsVectorLayerProperties::toggleEditing()
@ -546,6 +558,8 @@ void QgsVectorLayerProperties::syncToLayer()
mVector3DWidget->setLayer( mLayer );
#endif
mMetadataWidget->setMetadata( mLayer->metadata() );
} // syncToLayer()
void QgsVectorLayerProperties::apply()
@ -982,6 +996,108 @@ void QgsVectorLayerProperties::saveStyleAs_clicked()
saveStyleAs( QML );
}
void QgsVectorLayerProperties::loadMetadata()
{
QgsSettings myQSettings; // where we keep last used filter in persistent state
QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load layer metadata from metadata file" ), myLastUsedDir,
tr( "QGIS Layer Metadata File" ) + " (*.qmd)" );
if ( myFileName.isNull() )
{
return;
}
QString myMessage;
bool defaultLoadedFlag = false;
myMessage = mLayer->loadNamedMetadata( myFileName, defaultLoadedFlag );
//reset if the default style was loaded OK only
if ( defaultLoadedFlag )
{
mMetadataWidget->setMetadata( mLayer->metadata() );
}
else
{
//let the user know what went wrong
QMessageBox::warning( this, tr( "Load Metadata" ), myMessage );
}
QFileInfo myFI( myFileName );
QString myPath = myFI.path();
myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), myPath );
activateWindow(); // set focus back to properties dialog
}
void QgsVectorLayerProperties::saveMetadataAs()
{
QgsSettings myQSettings; // where we keep last used filter in persistent state
QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
QString myOutputFileName = QFileDialog::getSaveFileName( this, tr( "Save layer metadata as QMD" ),
myLastUsedDir, tr( "QMD File" ) + " (*.qmd)" );
if ( myOutputFileName.isNull() ) //dialog canceled
{
return;
}
mMetadataWidget->acceptMetadata();
//ensure the user never omitted the extension from the file name
if ( !myOutputFileName.endsWith( QgsMapLayer::extensionPropertyType( QgsMapLayer::Metadata ), Qt::CaseInsensitive ) )
{
myOutputFileName += QgsMapLayer::extensionPropertyType( QgsMapLayer::Metadata );
}
QString myMessage;
bool defaultLoadedFlag = false;
myMessage = mLayer->saveNamedMetadata( myOutputFileName, defaultLoadedFlag );
//reset if the default style was loaded OK only
if ( defaultLoadedFlag )
{
syncToLayer();
}
else
{
//let the user know what went wrong
QMessageBox::information( this, tr( "Saved Metadata" ), myMessage );
}
QFileInfo myFI( myOutputFileName );
QString myPath = myFI.path();
// Persist last used dir
myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), myPath );
}
void QgsVectorLayerProperties::saveDefaultMetadata()
{
mMetadataWidget->acceptMetadata();
bool defaultSavedFlag = false;
QString errorMsg = mLayer->saveDefaultMetadata( defaultSavedFlag );
if ( !defaultSavedFlag )
{
QMessageBox::warning( this, tr( "Default Metadata" ), errorMsg );
}
}
void QgsVectorLayerProperties::loadDefaultMetadata()
{
bool defaultLoadedFlag = false;
QString myMessage = mLayer->loadNamedMetadata( mLayer->metadataUri(), defaultLoadedFlag );
//reset if the default metadata was loaded OK only
if ( defaultLoadedFlag )
{
mMetadataWidget->setMetadata( mLayer->metadata() );
}
else
{
QMessageBox::information( this, tr( "Default Metadata" ), myMessage );
}
}
void QgsVectorLayerProperties::saveStyleAsMenuTriggered( QAction *action )
{
QMenu *menu = qobject_cast<QMenu *>( sender() );
@ -1045,7 +1161,7 @@ void QgsVectorLayerProperties::saveStyleAs( StyleType styleType )
else
{
format = tr( "QGIS Layer Style File" ) + " (*.qml)";
extension = QStringLiteral( ".qml" );
extension = QgsMapLayer::extensionPropertyType( QgsMapLayer::Style );
}
QString myOutputFileName = QFileDialog::getSaveFileName( this, tr( "Save layer properties as style file" ),
@ -1480,6 +1596,10 @@ void QgsVectorLayerProperties::optionsStackedWidget_CurrentChanged( int index )
{
QgsOptionsDialogBase::optionsStackedWidget_CurrentChanged( index );
bool isMetadataPanel = ( index == mOptStackedWidget->indexOf( mOptsPage_Metadata ) );
mBtnStyle->setVisible( ! isMetadataPanel );
mBtnMetadata->setVisible( isMetadataPanel );
if ( index != mOptStackedWidget->indexOf( mOptsPage_Information ) || mMetadataFilled )
return;

View File

@ -118,6 +118,10 @@ class APP_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private
void saveDefaultStyle_clicked();
void loadStyle_clicked();
void saveStyleAs_clicked();
void loadMetadata();
void saveMetadataAs();
void saveDefaultMetadata();
void loadDefaultMetadata();
void optionsStackedWidget_CurrentChanged( int index ) override;
void pbnUpdateExtents_clicked();
@ -167,6 +171,12 @@ class APP_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private
private:
enum PropertyType
{
Style = 0,
Metadata,
};
void saveStyleAs( StyleType styleType );
//! When provider supports, it will list all the styles relative the layer in a dialog
@ -182,6 +192,11 @@ class APP_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private
QString mOriginalSubsetSQL;
QPushButton *mBtnStyle = nullptr;
QPushButton *mBtnMetadata = nullptr;
QAction *mActionLoadMetadata = nullptr;
QAction *mActionSaveMetadataAs = nullptr;
QMenu *mSaveAsMenu = nullptr;
QMenu *mLoadStyleMenu = nullptr;

View File

@ -54,6 +54,19 @@
#include "qgssettings.h" // TODO: get rid of it [MD]
#include "qgsstringutils.h"
QString QgsMapLayer::extensionPropertyType( QgsMapLayer::PropertyType type )
{
switch ( type )
{
case Metadata:
return QStringLiteral( ".qmd" );
case Style:
return QStringLiteral( ".qml" );
}
return QStringLiteral();
}
QgsMapLayer::QgsMapLayer( QgsMapLayer::LayerType type,
const QString &lyrname,
const QString &source )
@ -1009,7 +1022,7 @@ QString QgsMapLayer::formatLayerName( const QString &name )
return layerName;
}
QString QgsMapLayer::styleURI() const
QString QgsMapLayer::baseURI( PropertyType type ) const
{
QString myURI = publicSource();
@ -1051,7 +1064,7 @@ QString QgsMapLayer::styleURI() const
myURI.chop( 4 );
myFileInfo.setFile( myURI );
// get the file name for our .qml style file
key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + QgsMapLayer::extensionPropertyType( type );
}
else
{
@ -1061,12 +1074,42 @@ QString QgsMapLayer::styleURI() const
return key;
}
QString QgsMapLayer::metadataUri() const
{
return baseURI( PropertyType::Metadata );
}
QString QgsMapLayer::saveDefaultMetadata( bool &resultFlag )
{
return saveNamedMetadata( metadataUri(), resultFlag );
}
QString QgsMapLayer::loadDefaultMetadata( bool &resultFlag )
{
return loadNamedMetadata( metadataUri(), resultFlag );
}
QString QgsMapLayer::styleURI() const
{
return baseURI( PropertyType::Style );
}
QString QgsMapLayer::loadDefaultStyle( bool &resultFlag )
{
return loadNamedStyle( styleURI(), resultFlag );
}
bool QgsMapLayer::loadNamedMetadataFromDatabase( const QString &db, const QString &uri, QString &qmd )
{
return loadNamedPropertyFromDatabase( db, uri, qmd, PropertyType::Metadata );
}
bool QgsMapLayer::loadNamedStyleFromDatabase( const QString &db, const QString &uri, QString &qml )
{
return loadNamedPropertyFromDatabase( db, uri, qml, PropertyType::Style );
}
bool QgsMapLayer::loadNamedPropertyFromDatabase( const QString &db, const QString &uri, QString &xml, QgsMapLayer::PropertyType type )
{
QgsDebugMsgLevel( QString( "db = %1 uri = %2" ).arg( db, uri ), 4 );
@ -1078,7 +1121,7 @@ bool QgsMapLayer::loadNamedStyleFromDatabase( const QString &db, const QString &
int myResult;
QgsDebugMsgLevel( QString( "Trying to load style for \"%1\" from \"%2\"" ).arg( uri, db ), 4 );
QgsDebugMsgLevel( QString( "Trying to load style or metadata for \"%1\" from \"%2\"" ).arg( uri, db ), 4 );
if ( db.isEmpty() || !QFile( db ).exists() )
return false;
@ -1089,7 +1132,18 @@ bool QgsMapLayer::loadNamedStyleFromDatabase( const QString &db, const QString &
return false;
}
QString mySql = QStringLiteral( "select qml from tbl_styles where style=?" );
QString mySql;
switch ( type )
{
case Metadata:
mySql = QStringLiteral( "select qmd from tbl_metadata where metadata=?" );
break;
case Style:
mySql = QStringLiteral( "select qml from tbl_styles where style=?" );
break;
}
statement = database.prepare( mySql, myResult );
if ( myResult == SQLITE_OK )
{
@ -1098,7 +1152,7 @@ bool QgsMapLayer::loadNamedStyleFromDatabase( const QString &db, const QString &
if ( sqlite3_bind_text( statement.get(), 1, param.data(), param.length(), SQLITE_STATIC ) == SQLITE_OK &&
sqlite3_step( statement.get() ) == SQLITE_ROW )
{
qml = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( statement.get(), 0 ) ) );
xml = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( statement.get(), 0 ) ) );
resultFlag = true;
}
}
@ -1107,9 +1161,15 @@ bool QgsMapLayer::loadNamedStyleFromDatabase( const QString &db, const QString &
QString QgsMapLayer::loadNamedStyle( const QString &uri, bool &resultFlag )
{
return loadNamedProperty( uri, PropertyType::Style, resultFlag );
}
QString QgsMapLayer::loadNamedProperty( const QString &uri, QgsMapLayer::PropertyType type, bool &resultFlag )
{
QgsDebugMsgLevel( QString( "uri = %1 myURI = %2" ).arg( uri, publicSource() ), 4 );
QgsDebugMsg( "loadNamedProperty" );
resultFlag = false;
QDomDocument myDocument( QStringLiteral( "qgis" ) );
@ -1121,6 +1181,7 @@ QString QgsMapLayer::loadNamedStyle( const QString &uri, bool &resultFlag )
QFile myFile( uri );
if ( myFile.open( QFile::ReadOnly ) )
{
QgsDebugMsg( QString( "file found %1" ).arg( uri ) );
// read file
resultFlag = myDocument.setContent( &myFile, &myErrorMessage, &line, &column );
if ( !resultFlag )
@ -1132,20 +1193,47 @@ QString QgsMapLayer::loadNamedStyle( const QString &uri, bool &resultFlag )
QFileInfo project( QgsProject::instance()->fileName() );
QgsDebugMsgLevel( QString( "project fileName: %1" ).arg( project.absoluteFilePath() ), 4 );
QString qml;
if ( loadNamedStyleFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( QStringLiteral( "qgis.qmldb" ) ), uri, qml ) ||
( project.exists() && loadNamedStyleFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, qml ) ) ||
loadNamedStyleFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( QStringLiteral( "resources/qgis.qmldb" ) ), uri, qml ) )
QString xml;
switch ( type )
{
resultFlag = myDocument.setContent( qml, &myErrorMessage, &line, &column );
if ( !resultFlag )
case QgsMapLayer::Style:
{
myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
if ( loadNamedStyleFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( QStringLiteral( "qgis.qmldb" ) ), uri, xml ) ||
( project.exists() && loadNamedStyleFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, xml ) ) ||
loadNamedStyleFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( QStringLiteral( "resources/qgis.qmldb" ) ), uri, xml ) )
{
resultFlag = myDocument.setContent( xml, &myErrorMessage, &line, &column );
if ( !resultFlag )
{
myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
}
}
else
{
myErrorMessage = tr( "Style not found in database" );
resultFlag = false;
}
break;
}
case QgsMapLayer::Metadata:
{
if ( loadNamedMetadataFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( QStringLiteral( "qgis.qmldb" ) ), uri, xml ) ||
( project.exists() && loadNamedMetadataFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, xml ) ) ||
loadNamedMetadataFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( QStringLiteral( "resources/qgis.qmldb" ) ), uri, xml ) )
{
resultFlag = myDocument.setContent( xml, &myErrorMessage, &line, &column );
if ( !resultFlag )
{
myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
}
}
else
{
myErrorMessage = tr( "Metadata not found in database" );
resultFlag = false;
}
break;
}
}
else
{
myErrorMessage = tr( "Style not found in database" );
}
}
@ -1154,13 +1242,33 @@ QString QgsMapLayer::loadNamedStyle( const QString &uri, bool &resultFlag )
return myErrorMessage;
}
resultFlag = importNamedStyle( myDocument, myErrorMessage );
if ( !resultFlag )
myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, myErrorMessage );
switch ( type )
{
case QgsMapLayer::Style:
resultFlag = importNamedStyle( myDocument, myErrorMessage );
if ( !resultFlag )
myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, myErrorMessage );
break;
case QgsMapLayer::Metadata:
resultFlag = importNamedMetadata( myDocument, myErrorMessage );
if ( !resultFlag )
myErrorMessage = tr( "Loading metadata file %1 failed because:\n%2" ).arg( uri, myErrorMessage );
break;
}
return myErrorMessage;
}
bool QgsMapLayer::importNamedMetadata( QDomDocument &document, QString &errorMessage )
{
QDomElement myRoot = document.firstChildElement( QStringLiteral( "qgis" ) );
if ( myRoot.isNull() )
{
errorMessage = tr( "Root <qgis> element could not be found" );
return false;
}
return mMetadata.readMetadataXml( myRoot );
}
bool QgsMapLayer::importNamedStyle( QDomDocument &myDocument, QString &myErrorMessage )
{
@ -1210,6 +1318,25 @@ bool QgsMapLayer::importNamedStyle( QDomDocument &myDocument, QString &myErrorMe
return readSymbology( myRoot, myErrorMessage, QgsReadWriteContext() ); // TODO: support relative paths in QML?
}
void QgsMapLayer::exportNamedMetadata( QDomDocument &doc, QString &errorMsg ) const
{
QDomImplementation DomImplementation;
QDomDocumentType documentType = DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
QDomDocument myDocument( documentType );
QDomElement myRootNode = myDocument.createElement( QStringLiteral( "qgis" ) );
myRootNode.setAttribute( QStringLiteral( "version" ), Qgis::QGIS_VERSION );
myDocument.appendChild( myRootNode );
if ( !mMetadata.writeMetadataXml( myRootNode, myDocument ) )
{
errorMsg = QObject::tr( "Could not save metadata" );
return;
}
doc = myDocument;
}
void QgsMapLayer::exportNamedStyle( QDomDocument &doc, QString &errorMsg ) const
{
QDomImplementation DomImplementation;
@ -1256,13 +1383,19 @@ QString QgsMapLayer::saveDefaultStyle( bool &resultFlag )
return saveNamedStyle( styleURI(), resultFlag );
}
QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
QString QgsMapLayer::saveNamedMetadata( const QString &uri, bool &resultFlag )
{
QString myErrorMessage;
QDomDocument myDocument;
exportNamedStyle( myDocument, myErrorMessage );
return saveNamedProperty( uri, QgsMapLayer::Metadata, resultFlag );
}
// check if the uri is a file or ends with .qml,
QString QgsMapLayer::loadNamedMetadata( const QString &uri, bool &resultFlag )
{
return loadNamedProperty( uri, QgsMapLayer::Metadata, resultFlag );
}
QString QgsMapLayer::saveNamedProperty( const QString &uri, QgsMapLayer::PropertyType type, bool &resultFlag )
{
// check if the uri is a file or ends with .qml/.qmd,
// which indicates that it should become one
// everything else goes to the database
QString filename;
@ -1290,8 +1423,21 @@ QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
filename = uri;
}
QString myErrorMessage;
QDomDocument myDocument;
switch ( type )
{
case Metadata:
exportNamedMetadata( myDocument, myErrorMessage );
break;
case Style:
exportNamedStyle( myDocument, myErrorMessage );
break;
}
QFileInfo myFileInfo( filename );
if ( myFileInfo.exists() || filename.endsWith( QLatin1String( ".qml" ), Qt::CaseInsensitive ) )
if ( myFileInfo.exists() || filename.endsWith( QgsMapLayer::extensionPropertyType( type ), Qt::CaseInsensitive ) )
{
QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
if ( !myDirInfo.isWritable() )
@ -1299,8 +1445,8 @@ QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
return tr( "The directory containing your dataset needs to be writable!" );
}
// now construct the file name for our .qml style file
QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
// now construct the file name for our .qml or .qmd file
QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + QgsMapLayer::extensionPropertyType( type );
QFile myFile( myFileName );
if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
@ -1310,12 +1456,27 @@ QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
myDocument.save( myFileStream, 2 );
myFile.close();
resultFlag = true;
return tr( "Created default style file as %1" ).arg( myFileName );
switch ( type )
{
case Metadata:
return tr( "Created default metadata file as %1" ).arg( myFileName );
case Style:
return tr( "Created default style file as %1" ).arg( myFileName );
}
}
else
{
resultFlag = false;
return tr( "ERROR: Failed to created default style file as %1. Check file permissions and retry." ).arg( myFileName );
switch ( type )
{
case Metadata:
return tr( "ERROR: Failed to created default metadata file as %1. Check file permissions and retry." ).arg( myFileName );
case Style:
return tr( "ERROR: Failed to created default style file as %1. Check file permissions and retry." ).arg( myFileName );
}
}
}
else
@ -1335,18 +1496,45 @@ QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
QByteArray param0 = uri.toUtf8();
QByteArray param1 = qml.toUtf8();
QString mySql = QStringLiteral( "create table if not exists tbl_styles(style varchar primary key,qml varchar)" );
QString mySql;
switch ( type )
{
case Metadata:
mySql = QStringLiteral( "create table if not exists tbl_metadata(metadata varchar primary key,qmd varchar)" );
break;
case Style:
mySql = QStringLiteral( "create table if not exists tbl_styles(style varchar primary key,qml varchar)" );
break;
}
statement = database.prepare( mySql, myResult );
if ( myResult == SQLITE_OK )
{
if ( sqlite3_step( statement.get() ) != SQLITE_DONE )
{
resultFlag = false;
return tr( "The style table could not be created." );
switch ( type )
{
case Metadata:
return tr( "The metadata table could not be created." );
case Style:
return tr( "The style table could not be created." );
}
}
}
mySql = QStringLiteral( "insert into tbl_styles(style,qml) values (?,?)" );
switch ( type )
{
case Metadata:
mySql = QStringLiteral( "insert into tbl_metadata(metadata,qmd) values (?,?)" );
break;
case Style:
mySql = QStringLiteral( "insert into tbl_styles(style,qml) values (?,?)" );
break;
}
statement = database.prepare( mySql, myResult );
if ( myResult == SQLITE_OK )
{
@ -1355,15 +1543,33 @@ QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
sqlite3_step( statement.get() ) == SQLITE_DONE )
{
resultFlag = true;
myErrorMessage = tr( "The style %1 was saved to database" ).arg( uri );
switch ( type )
{
case Metadata:
myErrorMessage = tr( "The metadata %1 was saved to database" ).arg( uri );
break;
case Style:
myErrorMessage = tr( "The style %1 was saved to database" ).arg( uri );
break;
}
}
}
if ( !resultFlag )
{
QString mySql = QStringLiteral( "update tbl_styles set qml=? where style=?" );
statement = database.prepare( mySql, myResult );
QString mySql;
switch ( type )
{
case Metadata:
mySql = QStringLiteral( "update tbl_metadata set qmd=? where metadata=?" );
break;
case Style:
mySql = QStringLiteral( "update tbl_styles set qml=? where style=?" );
break;
}
statement = database.prepare( mySql, myResult );
if ( myResult == SQLITE_OK )
{
if ( sqlite3_bind_text( statement.get(), 2, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
@ -1371,18 +1577,45 @@ QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
sqlite3_step( statement.get() ) == SQLITE_DONE )
{
resultFlag = true;
myErrorMessage = tr( "The style %1 was updated in the database." ).arg( uri );
switch ( type )
{
case Metadata:
myErrorMessage = tr( "The metadata %1 was updated in the database." ).arg( uri );
break;
case Style:
myErrorMessage = tr( "The style %1 was updated in the database." ).arg( uri );
break;
}
}
else
{
resultFlag = false;
myErrorMessage = tr( "The style %1 could not be updated in the database." ).arg( uri );
switch ( type )
{
case Metadata:
myErrorMessage = tr( "The metadata %1 could not be updated in the database." ).arg( uri );
break;
case Style:
myErrorMessage = tr( "The style %1 could not be updated in the database." ).arg( uri );
break;
}
}
}
else
{
resultFlag = false;
myErrorMessage = tr( "The style %1 could not be inserted into database." ).arg( uri );
switch ( type )
{
case Metadata:
myErrorMessage = tr( "The metadata %1 could not be inserted into database." ).arg( uri );
break;
case Style:
myErrorMessage = tr( "The style %1 could not be inserted into database." ).arg( uri );
break;
}
}
}
}
@ -1390,6 +1623,11 @@ QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
return myErrorMessage;
}
QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
{
return saveNamedProperty( uri, QgsMapLayer::Style, resultFlag );
}
void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg ) const
{
QDomDocument myDocument = QDomDocument();

View File

@ -98,6 +98,16 @@ class CORE_EXPORT QgsMapLayer : public QObject
PluginLayer
};
/**
* Maplayer has a style and a metadata property
* \since QGIS 3.0
*/
enum PropertyType
{
Style = 0,
Metadata,
};
/**
* Constructor for QgsMapLayer
* \param type layer type
@ -126,6 +136,13 @@ class CORE_EXPORT QgsMapLayer : public QObject
*/
QgsMapLayer::LayerType type() const;
/**
* Returns the extension of a Property.
* \returns The extension
* \since QGIS 3.0
*/
static QString extensionPropertyType( PropertyType type );
//! Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString id() const;
@ -525,6 +542,96 @@ class CORE_EXPORT QgsMapLayer : public QObject
*/
static QString formatLayerName( const QString &name );
/**
* Retrieve the metadata URI for this layer
* (either as a .qmd file on disk or as a
* record in the users style table in their personal qgis.db)
* \returns a QString with the metadata file name
* \since QGIS 3.0
*/
virtual QString metadataUri() const;
/**
* Export the current metadata of this layer as named metadata in a QDomDocument
* \param doc the target QDomDocument
* \param errorMsg this QString will be initialized on error
* \since QGIS 3.0
*/
void exportNamedMetadata( QDomDocument &doc, QString &errorMsg ) const;
/**
* Save the current metadata of this layer as the default metadata
* (either as a .qmd file on disk or as a
* record in the users style table in their personal qgis.db)
* \param resultFlag a reference to a flag that will be set to false if
* we did not manage to save the default metadata.
* \returns a QString with any status messages
* \since QGIS 3.0
*/
virtual QString saveDefaultMetadata( bool &resultFlag SIP_OUT );
/**
* Save the current metadata of this layer as a named metadata
* (either as a .qmd file on disk or as a
* record in the users style table in their personal qgis.db)
* \param uri the file name or other URI for the
* metadata file. First an attempt will be made to see if this
* is a file and save to that, if that fails the qgis.db metadata
* table will be used to create a metadata entry who's
* key matches the URI.
* \param resultFlag a reference to a flag that will be set to false if
* we did not manage to save the default metadata.
* \returns a QString with any status messages
* \since QGIS 3.0
*/
QString saveNamedMetadata( const QString &uri, bool &resultFlag );
/**
* Retrieve a named metadata for this layer if one
* exists (either as a .qmd file on disk or as a
* record in the users style table in their personal qgis.db)
* \param uri - the file name or other URI for the
* metadata file. First an attempt will be made to see if this
* is a file and load that, if that fails the qgis.db metadata
* table will be consulted to see if there is a metadata who's
* key matches the URI.
* \param resultFlag a reference to a flag that will be set to false if
* we did not manage to load the default metadata.
* \returns a QString with any status messages
* \since QGIS 3.0
*/
virtual QString loadNamedMetadata( const QString &uri, bool &resultFlag SIP_OUT );
/**
* Retrieve the default metadata for this layer if one
* exists (either as a .qmd file on disk or as a
* record in the users metadata table in their personal qgis.db)
* \param resultFlag a reference to a flag that will be set to false if
* we did not manage to load the default metadata.
* \returns a QString with any status messages
* \since QGIS 3.0
*/
QString loadDefaultMetadata( bool &resultFlag );
/**
* Retrieve a named metadata for this layer from a sqlite database.
* \param db path to sqlite database
* \param uri uri for table
* \param qmd will be set to QMD xml metadata content from database
* \returns true if style was successfully loaded
* \since QGIS 3.0
*/
bool loadNamedMetadataFromDatabase( const QString &db, const QString &uri, QString &qmd );
/**
* Import the metadata of this layer from a QDomDocument
* \param document source QDomDocument
* \param errorMessage this QString will be initialized on error
* \returns true on success
* \since QGIS 3.0
*/
bool importNamedMetadata( QDomDocument &document, QString &errorMessage );
/**
* Retrieve the style URI for this layer
* (either as a .qml file on disk or as a
@ -1169,6 +1276,11 @@ class CORE_EXPORT QgsMapLayer : public QObject
private:
virtual QString baseURI( PropertyType type ) const;
QString saveNamedProperty( const QString &uri, QgsMapLayer::PropertyType type, bool &resultFlag );
QString loadNamedProperty( const QString &uri, QgsMapLayer::PropertyType type, bool &resultFlag );
bool loadNamedPropertyFromDatabase( const QString &db, const QString &uri, QString &xml, QgsMapLayer::PropertyType type );
/**
* This method returns true by default but can be overwritten to specify
* that a certain layer is writable.

View File

@ -820,6 +820,12 @@ void QgsMetadataWidget::acceptMetadata()
mLayer->setMetadata( mMetadata );
}
void QgsMetadataWidget::setMetadata( const QgsLayerMetadata &metadata )
{
mMetadata = metadata;
setPropertiesFromLayer();
}
void QgsMetadataWidget::syncFromCategoriesTabToKeywordsTab() const
{
if ( mCategoriesModel->rowCount() > 0 )

View File

@ -66,6 +66,11 @@ class GUI_EXPORT QgsMetadataWidget : public QWidget, private Ui::QgsMetadataWidg
*/
void acceptMetadata();
/**
* Sets the layer's \a metadata store.
*/
virtual void setMetadata( const QgsLayerMetadata &metadata );
/**
* Returns a list of languages available by default in the wizard.
*/

View File

@ -13,6 +13,7 @@ __copyright__ = 'Copyright 2017, The QGIS Project'
__revision__ = '$Format:%H$'
import qgis # NOQA
import tempfile
from qgis.core import (QgsReadWriteContext,
QgsVectorLayer,
@ -88,6 +89,22 @@ class TestQgsMapLayer(unittest.TestCase):
self.assertTrue(layer2.hasAutoRefreshEnabled())
self.assertEqual(layer2.autoRefreshInterval(), 56)
def testReadWriteMetadata(self):
layer = QgsVectorLayer("Point?field=fldtxt:string", "layer", "memory")
m = layer.metadata()
# Only abstract, more tests are done in test_qgslayermetadata.py
m.setAbstract('My abstract')
layer.setMetadata(m)
self.assertTrue(layer.metadata().abstract(), 'My abstract')
destination = tempfile.NamedTemporaryFile(suffix='.qmd').name
message, status = layer.saveNamedMetadata(destination)
self.assertTrue(status, message)
layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer", "memory")
message, status = layer2.loadNamedMetadata(destination)
self.assertTrue(status)
self.assertTrue(layer2.metadata().abstract(), 'My abstract')
if __name__ == '__main__':
unittest.main()