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 PluginLayer
}; };
enum PropertyType
{
Style,
Metadata,
};
QgsMapLayer( QgsMapLayer::LayerType type = VectorLayer, const QString &name = QString(), const QString &source = QString() ); QgsMapLayer( QgsMapLayer::LayerType type = VectorLayer, const QString &name = QString(), const QString &source = QString() );
%Docstring %Docstring
Constructor for QgsMapLayer Constructor for QgsMapLayer
@ -80,6 +86,15 @@ is still unique.
QgsMapLayer::LayerType type() const; QgsMapLayer::LayerType type() const;
%Docstring %Docstring
Returns the type of the layer. 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 %End
QString id() const; QString id() const;
@ -545,6 +560,118 @@ Sets layer's spatial reference system
%Docstring %Docstring
A convenience function to capitalize and format a layer ``name``. 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 .. versionadded:: 3.0
%End %End

View File

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

View File

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

View File

@ -104,16 +104,26 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv
connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsRasterLayerProperties::showHelp ); connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsRasterLayerProperties::showHelp );
QPushButton *b = new QPushButton( tr( "Style" ) ); mBtnStyle = new QPushButton( tr( "Style" ) );
QMenu *m = new QMenu( this ); QMenu *menuStyle = new QMenu( this );
m->addAction( tr( "Load Style..." ), this, SLOT( loadStyle_clicked() ) ); menuStyle->addAction( tr( "Load Style..." ), this, SLOT( loadStyle_clicked() ) );
m->addAction( tr( "Save Style..." ), this, SLOT( saveStyleAs_clicked() ) ); menuStyle->addAction( tr( "Save Style..." ), this, SLOT( saveStyleAs_clicked() ) );
m->addSeparator(); menuStyle->addSeparator();
m->addAction( tr( "Save as Default" ), this, SLOT( saveDefaultStyle_clicked() ) ); menuStyle->addAction( tr( "Save as Default" ), this, SLOT( saveDefaultStyle_clicked() ) );
m->addAction( tr( "Restore Default" ), this, SLOT( loadDefaultStyle_clicked() ) ); menuStyle->addAction( tr( "Restore Default" ), this, SLOT( loadDefaultStyle_clicked() ) );
b->setMenu( m ); mBtnStyle->setMenu( menuStyle );
connect( m, &QMenu::aboutToShow, this, &QgsRasterLayerProperties::aboutToShowStyleMenu ); connect( menuStyle, &QMenu::aboutToShow, this, &QgsRasterLayerProperties::aboutToShowStyleMenu );
buttonBox->addButton( b, QDialogButtonBox::ResetRole ); 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 ); 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() ); QString title = QString( tr( "Layer Properties - %1" ) ).arg( lyr->name() );
restoreOptionsBaseUi( title ); restoreOptionsBaseUi( title );
optionsStackedWidget_CurrentChanged( mOptionsStackedWidget->currentIndex() );
} // QgsRasterLayerProperties ctor } // QgsRasterLayerProperties ctor
@ -1488,6 +1499,10 @@ void QgsRasterLayerProperties::optionsStackedWidget_CurrentChanged( int index )
{ {
QgsOptionsDialogBase::optionsStackedWidget_CurrentChanged( index ); QgsOptionsDialogBase::optionsStackedWidget_CurrentChanged( index );
bool isMetadataPanel = ( index == mOptStackedWidget->indexOf( mOptsPage_Metadata ) );
mBtnStyle->setVisible( ! isMetadataPanel );
mBtnMetadata->setVisible( isMetadataPanel );
if ( !mHistogramWidget ) if ( !mHistogramWidget )
return; return;
@ -1856,6 +1871,102 @@ void QgsRasterLayerProperties::saveStyleAs_clicked()
QMessageBox::information( this, tr( "Saved Style" ), message ); 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() void QgsRasterLayerProperties::toggleBuildPyramidsButton()
{ {
if ( lbxPyramidResolutions->selectedItems().empty() ) if ( lbxPyramidResolutions->selectedItems().empty() )

View File

@ -103,6 +103,16 @@ class APP_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private
void loadStyle_clicked(); void loadStyle_clicked();
//! Save a style when appriate button is pressed. //! Save a style when appriate button is pressed.
void saveStyleAs_clicked(); 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 //! Help button
void showHelp(); void showHelp();
@ -131,6 +141,11 @@ class APP_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private
void refreshLegend( const QString &layerID, bool expandItem ); void refreshLegend( const QString &layerID, bool expandItem );
private: private:
QPushButton *mBtnStyle = nullptr;
QPushButton *mBtnMetadata = nullptr;
QAction *mActionLoadMetadata = nullptr;
QAction *mActionSaveMetadataAs = nullptr;
//! \brief A constant that signals property not used //! \brief A constant that signals property not used
const QString TRSTRING_NOT_SET; 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 // and connecting QDialogButtonBox's accepted/rejected signals to dialog's accept/reject slots
initOptionsBase( false ); initOptionsBase( false );
QPushButton *b = new QPushButton( tr( "Style" ) ); mBtnStyle = new QPushButton( tr( "Style" ), this );
QMenu *m = new QMenu( this ); QMenu *menuStyle = new QMenu( this );
mActionLoadStyle = m->addAction( tr( "Load Style" ), this, SLOT( loadStyle_clicked() ) ); mActionLoadStyle = menuStyle->addAction( tr( "Load Style" ), this, SLOT( loadStyle_clicked() ) );
mActionSaveStyleAs = m->addAction( tr( "Save Style" ), this, SLOT( saveStyleAs_clicked() ) ); mActionSaveStyleAs = menuStyle->addAction( tr( "Save Style" ), this, SLOT( saveStyleAs_clicked() ) );
m->addSeparator(); menuStyle->addSeparator();
m->addAction( tr( "Save as Default" ), this, SLOT( saveDefaultStyle_clicked() ) ); menuStyle->addAction( tr( "Save as Default" ), this, SLOT( saveDefaultStyle_clicked() ) );
m->addAction( tr( "Restore Default" ), this, SLOT( loadDefaultStyle_clicked() ) ); menuStyle->addAction( tr( "Restore Default" ), this, SLOT( loadDefaultStyle_clicked() ) );
b->setMenu( m ); mBtnStyle->setMenu( menuStyle );
connect( m, &QMenu::aboutToShow, this, &QgsVectorLayerProperties::aboutToShowStyleMenu ); connect( menuStyle, &QMenu::aboutToShow, this, &QgsVectorLayerProperties::aboutToShowStyleMenu );
buttonBox->addButton( b, QDialogButtonBox::ResetRole ); 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 ); 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 delete mOptsPage_3DView; // removes both the "3d view" list item and its page
#endif #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(); syncToLayer();
if ( mLayer->dataProvider() )//enable spatial index button group if supported by provider if ( mLayer->dataProvider() )//enable spatial index button group if supported by provider
@ -282,15 +301,6 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
diagLayout->addWidget( diagramPropertiesDialog ); diagLayout->addWidget( diagramPropertiesDialog );
mDiagramFrame->setLayout( diagLayout ); 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 // Legend tab
mLegendConfigEmbeddedWidget->setLayer( mLayer ); mLegendConfigEmbeddedWidget->setLayer( mLayer );
@ -401,6 +411,8 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
connect( mAuxiliaryStorageFieldsAddBtn, &QPushButton::clicked, this, &QgsVectorLayerProperties::onAuxiliaryLayerAddField ); connect( mAuxiliaryStorageFieldsAddBtn, &QPushButton::clicked, this, &QgsVectorLayerProperties::onAuxiliaryLayerAddField );
updateAuxiliaryStoragePage(); updateAuxiliaryStoragePage();
optionsStackedWidget_CurrentChanged( mOptStackedWidget->currentIndex() );
} }
void QgsVectorLayerProperties::toggleEditing() void QgsVectorLayerProperties::toggleEditing()
@ -546,6 +558,8 @@ void QgsVectorLayerProperties::syncToLayer()
mVector3DWidget->setLayer( mLayer ); mVector3DWidget->setLayer( mLayer );
#endif #endif
mMetadataWidget->setMetadata( mLayer->metadata() );
} // syncToLayer() } // syncToLayer()
void QgsVectorLayerProperties::apply() void QgsVectorLayerProperties::apply()
@ -982,6 +996,108 @@ void QgsVectorLayerProperties::saveStyleAs_clicked()
saveStyleAs( QML ); 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 ) void QgsVectorLayerProperties::saveStyleAsMenuTriggered( QAction *action )
{ {
QMenu *menu = qobject_cast<QMenu *>( sender() ); QMenu *menu = qobject_cast<QMenu *>( sender() );
@ -1045,7 +1161,7 @@ void QgsVectorLayerProperties::saveStyleAs( StyleType styleType )
else else
{ {
format = tr( "QGIS Layer Style File" ) + " (*.qml)"; 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" ), 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 ); QgsOptionsDialogBase::optionsStackedWidget_CurrentChanged( index );
bool isMetadataPanel = ( index == mOptStackedWidget->indexOf( mOptsPage_Metadata ) );
mBtnStyle->setVisible( ! isMetadataPanel );
mBtnMetadata->setVisible( isMetadataPanel );
if ( index != mOptStackedWidget->indexOf( mOptsPage_Information ) || mMetadataFilled ) if ( index != mOptStackedWidget->indexOf( mOptsPage_Information ) || mMetadataFilled )
return; return;

View File

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

View File

@ -54,6 +54,19 @@
#include "qgssettings.h" // TODO: get rid of it [MD] #include "qgssettings.h" // TODO: get rid of it [MD]
#include "qgsstringutils.h" #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, QgsMapLayer::QgsMapLayer( QgsMapLayer::LayerType type,
const QString &lyrname, const QString &lyrname,
const QString &source ) const QString &source )
@ -1009,7 +1022,7 @@ QString QgsMapLayer::formatLayerName( const QString &name )
return layerName; return layerName;
} }
QString QgsMapLayer::styleURI() const QString QgsMapLayer::baseURI( PropertyType type ) const
{ {
QString myURI = publicSource(); QString myURI = publicSource();
@ -1051,7 +1064,7 @@ QString QgsMapLayer::styleURI() const
myURI.chop( 4 ); myURI.chop( 4 );
myFileInfo.setFile( myURI ); myFileInfo.setFile( myURI );
// get the file name for our .qml style file // 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 else
{ {
@ -1061,12 +1074,42 @@ QString QgsMapLayer::styleURI() const
return key; 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 ) QString QgsMapLayer::loadDefaultStyle( bool &resultFlag )
{ {
return loadNamedStyle( styleURI(), 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 ) 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 ); QgsDebugMsgLevel( QString( "db = %1 uri = %2" ).arg( db, uri ), 4 );
@ -1078,7 +1121,7 @@ bool QgsMapLayer::loadNamedStyleFromDatabase( const QString &db, const QString &
int myResult; 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() ) if ( db.isEmpty() || !QFile( db ).exists() )
return false; return false;
@ -1089,7 +1132,18 @@ bool QgsMapLayer::loadNamedStyleFromDatabase( const QString &db, const QString &
return false; 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 ); statement = database.prepare( mySql, myResult );
if ( myResult == SQLITE_OK ) 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 && if ( sqlite3_bind_text( statement.get(), 1, param.data(), param.length(), SQLITE_STATIC ) == SQLITE_OK &&
sqlite3_step( statement.get() ) == SQLITE_ROW ) 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; resultFlag = true;
} }
} }
@ -1107,9 +1161,15 @@ bool QgsMapLayer::loadNamedStyleFromDatabase( const QString &db, const QString &
QString QgsMapLayer::loadNamedStyle( const QString &uri, bool &resultFlag ) 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 ); QgsDebugMsgLevel( QString( "uri = %1 myURI = %2" ).arg( uri, publicSource() ), 4 );
QgsDebugMsg( "loadNamedProperty" );
resultFlag = false; resultFlag = false;
QDomDocument myDocument( QStringLiteral( "qgis" ) ); QDomDocument myDocument( QStringLiteral( "qgis" ) );
@ -1121,6 +1181,7 @@ QString QgsMapLayer::loadNamedStyle( const QString &uri, bool &resultFlag )
QFile myFile( uri ); QFile myFile( uri );
if ( myFile.open( QFile::ReadOnly ) ) if ( myFile.open( QFile::ReadOnly ) )
{ {
QgsDebugMsg( QString( "file found %1" ).arg( uri ) );
// read file // read file
resultFlag = myDocument.setContent( &myFile, &myErrorMessage, &line, &column ); resultFlag = myDocument.setContent( &myFile, &myErrorMessage, &line, &column );
if ( !resultFlag ) if ( !resultFlag )
@ -1132,20 +1193,47 @@ QString QgsMapLayer::loadNamedStyle( const QString &uri, bool &resultFlag )
QFileInfo project( QgsProject::instance()->fileName() ); QFileInfo project( QgsProject::instance()->fileName() );
QgsDebugMsgLevel( QString( "project fileName: %1" ).arg( project.absoluteFilePath() ), 4 ); QgsDebugMsgLevel( QString( "project fileName: %1" ).arg( project.absoluteFilePath() ), 4 );
QString qml; QString xml;
if ( loadNamedStyleFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( QStringLiteral( "qgis.qmldb" ) ), uri, qml ) || switch ( type )
( project.exists() && loadNamedStyleFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, qml ) ) ||
loadNamedStyleFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( QStringLiteral( "resources/qgis.qmldb" ) ), uri, qml ) )
{ {
resultFlag = myDocument.setContent( qml, &myErrorMessage, &line, &column ); case QgsMapLayer::Style:
if ( !resultFlag )
{ {
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; return myErrorMessage;
} }
resultFlag = importNamedStyle( myDocument, myErrorMessage ); switch ( type )
if ( !resultFlag ) {
myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, myErrorMessage ); 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; 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 ) 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? 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 void QgsMapLayer::exportNamedStyle( QDomDocument &doc, QString &errorMsg ) const
{ {
QDomImplementation DomImplementation; QDomImplementation DomImplementation;
@ -1256,13 +1383,19 @@ QString QgsMapLayer::saveDefaultStyle( bool &resultFlag )
return saveNamedStyle( styleURI(), resultFlag ); return saveNamedStyle( styleURI(), resultFlag );
} }
QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag ) QString QgsMapLayer::saveNamedMetadata( const QString &uri, bool &resultFlag )
{ {
QString myErrorMessage; return saveNamedProperty( uri, QgsMapLayer::Metadata, resultFlag );
QDomDocument myDocument; }
exportNamedStyle( myDocument, myErrorMessage );
// 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 // which indicates that it should become one
// everything else goes to the database // everything else goes to the database
QString filename; QString filename;
@ -1290,8 +1423,21 @@ QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
filename = uri; filename = uri;
} }
QString myErrorMessage;
QDomDocument myDocument;
switch ( type )
{
case Metadata:
exportNamedMetadata( myDocument, myErrorMessage );
break;
case Style:
exportNamedStyle( myDocument, myErrorMessage );
break;
}
QFileInfo myFileInfo( filename ); 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 QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
if ( !myDirInfo.isWritable() ) 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!" ); return tr( "The directory containing your dataset needs to be writable!" );
} }
// now construct the file name for our .qml style file // now construct the file name for our .qml or .qmd file
QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml"; QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + QgsMapLayer::extensionPropertyType( type );
QFile myFile( myFileName ); QFile myFile( myFileName );
if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) ) if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
@ -1310,12 +1456,27 @@ QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
myDocument.save( myFileStream, 2 ); myDocument.save( myFileStream, 2 );
myFile.close(); myFile.close();
resultFlag = true; 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 else
{ {
resultFlag = false; 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 else
@ -1335,18 +1496,45 @@ QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
QByteArray param0 = uri.toUtf8(); QByteArray param0 = uri.toUtf8();
QByteArray param1 = qml.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 ); statement = database.prepare( mySql, myResult );
if ( myResult == SQLITE_OK ) if ( myResult == SQLITE_OK )
{ {
if ( sqlite3_step( statement.get() ) != SQLITE_DONE ) if ( sqlite3_step( statement.get() ) != SQLITE_DONE )
{ {
resultFlag = false; 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 ); statement = database.prepare( mySql, myResult );
if ( myResult == SQLITE_OK ) if ( myResult == SQLITE_OK )
{ {
@ -1355,15 +1543,33 @@ QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
sqlite3_step( statement.get() ) == SQLITE_DONE ) sqlite3_step( statement.get() ) == SQLITE_DONE )
{ {
resultFlag = true; 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 ) if ( !resultFlag )
{ {
QString mySql = QStringLiteral( "update tbl_styles set qml=? where style=?" ); QString mySql;
statement = database.prepare( mySql, myResult ); 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 ( myResult == SQLITE_OK )
{ {
if ( sqlite3_bind_text( statement.get(), 2, param0.data(), param0.length(), SQLITE_STATIC ) == 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 ) sqlite3_step( statement.get() ) == SQLITE_DONE )
{ {
resultFlag = true; 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 else
{ {
resultFlag = false; 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 else
{ {
resultFlag = false; 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; return myErrorMessage;
} }
QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
{
return saveNamedProperty( uri, QgsMapLayer::Style, resultFlag );
}
void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg ) const void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg ) const
{ {
QDomDocument myDocument = QDomDocument(); QDomDocument myDocument = QDomDocument();

View File

@ -98,6 +98,16 @@ class CORE_EXPORT QgsMapLayer : public QObject
PluginLayer PluginLayer
}; };
/**
* Maplayer has a style and a metadata property
* \since QGIS 3.0
*/
enum PropertyType
{
Style = 0,
Metadata,
};
/** /**
* Constructor for QgsMapLayer * Constructor for QgsMapLayer
* \param type layer type * \param type layer type
@ -126,6 +136,13 @@ class CORE_EXPORT QgsMapLayer : public QObject
*/ */
QgsMapLayer::LayerType type() const; 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. //! Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString id() const; QString id() const;
@ -525,6 +542,96 @@ class CORE_EXPORT QgsMapLayer : public QObject
*/ */
static QString formatLayerName( const QString &name ); 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 * Retrieve the style URI for this layer
* (either as a .qml file on disk or as a * (either as a .qml file on disk or as a
@ -1169,6 +1276,11 @@ class CORE_EXPORT QgsMapLayer : public QObject
private: 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 * This method returns true by default but can be overwritten to specify
* that a certain layer is writable. * that a certain layer is writable.

View File

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

View File

@ -66,6 +66,11 @@ class GUI_EXPORT QgsMetadataWidget : public QWidget, private Ui::QgsMetadataWidg
*/ */
void acceptMetadata(); 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. * 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$' __revision__ = '$Format:%H$'
import qgis # NOQA import qgis # NOQA
import tempfile
from qgis.core import (QgsReadWriteContext, from qgis.core import (QgsReadWriteContext,
QgsVectorLayer, QgsVectorLayer,
@ -88,6 +89,22 @@ class TestQgsMapLayer(unittest.TestCase):
self.assertTrue(layer2.hasAutoRefreshEnabled()) self.assertTrue(layer2.hasAutoRefreshEnabled())
self.assertEqual(layer2.autoRefreshInterval(), 56) 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__': if __name__ == '__main__':
unittest.main() unittest.main()