Skip to content

Commit

Permalink
Merge pull request #3789 from nirvn/style_import_export_imp
Browse files Browse the repository at this point in the history
[style manager] imporve import and export experience, save symbols' tags & favorite flag
nyalldawson authored Nov 21, 2016
2 parents eda412d + 2260780 commit ee71077
Showing 6 changed files with 205 additions and 59 deletions.
3 changes: 3 additions & 0 deletions python/core/symbology-ng/qgsstyle.sip
Original file line number Diff line number Diff line change
@@ -208,6 +208,9 @@ class QgsStyle : QObject
//! Changes ramp's name
bool renameColorRamp( const QString& oldName, const QString& newName );

//! Creates a temporary memory database
bool createMemoryDB();

//! Loads a file into the style
bool load( const QString& filename );

114 changes: 105 additions & 9 deletions src/core/symbology-ng/qgsstyle.cpp
Original file line number Diff line number Diff line change
@@ -289,6 +289,42 @@ bool QgsStyle::openDB( const QString& filename )
return true;
}

bool QgsStyle::createMemoryDB()
{
mErrorString.clear();
if ( !openDB( QStringLiteral( ":memory:" ) ) )
{
mErrorString = QStringLiteral( "Unable to create temporary memory database" );
QgsDebugMsg( mErrorString );
return false;
}
char *query = sqlite3_mprintf( "CREATE TABLE symbol("\
"id INTEGER PRIMARY KEY,"\
"name TEXT UNIQUE,"\
"xml TEXT,"\
"favorite INTEGER);"\
"CREATE TABLE colorramp("\
"id INTEGER PRIMARY KEY,"\
"name TEXT UNIQUE,"\
"xml TEXT,"\
"favorite INTEGER);"\
"CREATE TABLE tag("\
"id INTEGER PRIMARY KEY,"\
"name TEXT);"\
"CREATE TABLE tagmap("\
"tag_id INTEGER NOT NULL,"\
"symbol_id INTEGER);"\
"CREATE TABLE ctagmap("\
"tag_id INTEGER NOT NULL,"\
"colorramp_id INTEGER);"\
"CREATE TABLE smartgroup("\
"id INTEGER PRIMARY KEY,"\
"name TEXT,"\
"xml TEXT);" );
runEmptyQuery( query );
return true;
}

bool QgsStyle::load( const QString& filename )
{
mErrorString.clear();
@@ -1391,14 +1427,42 @@ bool QgsStyle::exportXml( const QString& filename )
root.setAttribute( QStringLiteral( "version" ), STYLE_CURRENT_VERSION );
doc.appendChild( root );

// TODO work on the groups and tags
QStringList favoriteSymbols = symbolsOfFavorite( SymbolEntity );
QStringList favoriteColorramps = symbolsOfFavorite( ColorrampEntity );

// save symbols and attach tags
QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( mSymbols, QStringLiteral( "symbols" ), doc );
QDomElement rampsElem = doc.createElement( QStringLiteral( "colorramps" ) );
QDomNodeList symbolsList = symbolsElem.elementsByTagName( QStringLiteral( "symbol" ) );
int nbSymbols = symbolsList.count();
for ( int i = 0; i < nbSymbols; ++i )
{
QDomElement symbol = symbolsList.at( i ).toElement();
QString name = symbol.attribute( QStringLiteral( "name" ) );
QStringList tags = tagsOfSymbol( SymbolEntity, name );
if ( tags.count() > 0 )
{
symbol.setAttribute( QStringLiteral( "tags" ), tags.join( "," ) );
}
if ( favoriteSymbols.contains( name ) )
{
symbol.setAttribute( QStringLiteral( "favorite" ), "1" );
}
}

// save color ramps
QDomElement rampsElem = doc.createElement( QStringLiteral( "colorramps" ) );
for ( QMap<QString, QgsColorRamp*>::const_iterator itr = mColorRamps.constBegin(); itr != mColorRamps.constEnd(); ++itr )
{
QDomElement rampEl = QgsSymbolLayerUtils::saveColorRamp( itr.key(), itr.value(), doc );
QStringList tags = tagsOfSymbol( ColorrampEntity, itr.key() );
if ( tags.count() > 0 )
{
rampEl.setAttribute( QStringLiteral( "tags" ), tags.join( "," ) );
}
if ( favoriteColorramps.contains( itr.key() ) )
{
rampEl.setAttribute( QStringLiteral( "favorite" ), "1" );
}
rampsElem.appendChild( rampEl );
}

@@ -1469,10 +1533,26 @@ bool QgsStyle::importXml( const QString& filename )
{
if ( e.tagName() == QLatin1String( "symbol" ) )
{
QString name = e.attribute( QStringLiteral( "name" ) );
QStringList tags;
if ( e.hasAttribute( QStringLiteral( "tags" ) ) )
{
tags = e.attribute( QStringLiteral( "tags" ) ).split( "," );
}
bool favorite = false;
if ( e.hasAttribute( QStringLiteral( "favorite" ) ) && e.attribute( QStringLiteral( "favorite" ) ) == "1" )
{
favorite = true;
}

QgsSymbol* symbol = QgsSymbolLayerUtils::loadSymbol( e );
if ( symbol )
{
symbols.insert( e.attribute( QStringLiteral( "name" ) ), symbol );
addSymbol( name, symbol );
if ( mCurrentDB )
{
saveSymbol( name, symbol, favorite, tags );
}
}
}
else
@@ -1486,12 +1566,12 @@ bool QgsStyle::importXml( const QString& filename )
{
// for the old version, use the utility function to solve @symbol@layer subsymbols
symbols = QgsSymbolLayerUtils::loadSymbols( symbolsElement );
}

// save the symbols with proper name
for ( QMap<QString, QgsSymbol*>::iterator it = symbols.begin(); it != symbols.end(); ++it )
{
addSymbol( it.key(), it.value() );
// save the symbols with proper name
for ( QMap<QString, QgsSymbol*>::iterator it = symbols.begin(); it != symbols.end(); ++it )
{
addSymbol( it.key(), it.value() );
}
}

// load color ramps
@@ -1501,10 +1581,26 @@ bool QgsStyle::importXml( const QString& filename )
{
if ( e.tagName() == QLatin1String( "colorramp" ) )
{
QString name = e.attribute( QStringLiteral( "name" ) );
QStringList tags;
if ( e.hasAttribute( QStringLiteral( "tags" ) ) )
{
tags = e.attribute( QStringLiteral( "tags" ) ).split( "," );
}
bool favorite = false;
if ( e.hasAttribute( QStringLiteral( "favorite" ) ) && e.attribute( QStringLiteral( "favorite" ) ) == "1" )
{
favorite = true;
}

QgsColorRamp* ramp = QgsSymbolLayerUtils::loadColorRamp( e );
if ( ramp )
{
addColorRamp( e.attribute( QStringLiteral( "name" ) ), ramp );
addColorRamp( name, ramp );
if ( mCurrentDB )
{
saveColorRamp( name, ramp, favorite, tags );
}
}
}
else
14 changes: 13 additions & 1 deletion src/core/symbology-ng/qgsstyle.h
Original file line number Diff line number Diff line change
@@ -273,7 +273,19 @@ class CORE_EXPORT QgsStyle : public QObject
//! Changes ramp's name
bool renameColorRamp( const QString& oldName, const QString& newName );

//! Loads a file into the style
/** Creates a temporary memory database
*
* This function is used if you do not need to associate styles with a permanent on-disk database.
* \return returns the success state of the temporary memory database creation
*/
bool createMemoryDB();

/** Loads a file into the style
*
* This function will populate styles from an on-disk database.
* \param filename location of the database to load styles from
* \return returns the success state of the database being loaded
*/
bool load( const QString& filename );

//! Saves style into a file (will use current filename if empty string is passed)
71 changes: 49 additions & 22 deletions src/gui/symbology-ng/qgsstyleexportimportdialog.cpp
Original file line number Diff line number Diff line change
@@ -50,12 +50,13 @@ QgsStyleExportImportDialog::QgsStyleExportImportDialog( QgsStyle* style, QWidget
connect( pb, SIGNAL( clicked() ), this, SLOT( clearSelection() ) );

QStandardItemModel* model = new QStandardItemModel( listItems );

listItems->setModel( model );
connect( listItems->selectionModel(), SIGNAL( selectionChanged( const QItemSelection&, const QItemSelection& ) ),
this, SLOT( selectionChanged( const QItemSelection&, const QItemSelection& ) ) );

mTempStyle = new QgsStyle();
mTempStyle->createMemoryDB();

// TODO validate
mFileName = QLatin1String( "" );
mProgressDlg = nullptr;
@@ -92,6 +93,7 @@ QgsStyleExportImportDialog::QgsStyleExportImportDialog( QgsStyle* style, QWidget
locationLineEdit->setHidden( true );

mFavorite->setHidden( true );
mIgnoreXMLTags->setHidden( true );

pb = new QPushButton( tr( "Select by group" ) );
buttonBox->addButton( pb, QDialogButtonBox::ActionRole );
@@ -148,6 +150,12 @@ void QgsStyleExportImportDialog::doExportImport()
.arg( mTempStyle->errorString() ) );
return;
}
else
{
QMessageBox::information( this, tr( "Export successful" ),
tr( "The selected symbols were successfully exported to file:\n%1" )
.arg( mFileName ) );
}
}
else // import
{
@@ -187,10 +195,16 @@ bool QgsStyleExportImportDialog::populateStyles( QgsStyle* style )
for ( int i = 0; i < styleNames.count(); ++i )
{
name = styleNames[i];
QStringList tags = style->tagsOfSymbol( QgsStyle::SymbolEntity, name );
QgsSymbol* symbol = style->symbol( name );
QStandardItem* item = new QStandardItem( name );
QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( symbol, listItems->iconSize() );
QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( symbol, listItems->iconSize(), 15 );
item->setIcon( icon );
item->setToolTip( QString( "<b>%1</b><br><i>%2</i>" ).arg( name ).arg( tags.count() > 0 ? tags.join( ", " ) : tr( "Not tagged" ) ) );
// Set font to 10points to show reasonable text
QFont itemFont = item->font();
itemFont.setPointSize( 10 );
item->setFont( itemFont );
model->appendRow( item );
delete symbol;
}
@@ -204,7 +218,7 @@ bool QgsStyleExportImportDialog::populateStyles( QgsStyle* style )
QScopedPointer< QgsColorRamp > ramp( style->colorRamp( name ) );

QStandardItem* item = new QStandardItem( name );
QIcon icon = QgsSymbolLayerUtils::colorRampPreviewIcon( ramp.data(), listItems->iconSize() );
QIcon icon = QgsSymbolLayerUtils::colorRampPreviewIcon( ramp.data(), listItems->iconSize(), 15 );
item->setIcon( icon );
model->appendRow( item );
}
@@ -215,25 +229,44 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
{
QString symbolName;
QgsSymbol* symbol;
QStringList symbolTags;
bool symbolFavorite;
QgsColorRamp *ramp = nullptr;
QModelIndex index;
bool isSymbol = true;
bool prompt = true;
bool overwrite = true;
QStringList tags;

// get the groupid when going for import
if ( mDialogMode == Import )
{
// get the name the user entered
tags = mSymbolTags->text().split( ',' );
}
QStringList importTags = mSymbolTags->text().split( ',' );

QStringList favoriteSymbols = src->symbolsOfFavorite( QgsStyle::SymbolEntity );
QStringList favoriteColorramps = src->symbolsOfFavorite( QgsStyle::ColorrampEntity );

for ( int i = 0; i < selection->size(); ++i )
{
index = selection->at( i );
symbolName = index.model()->data( index, 0 ).toString();
symbol = src->symbol( symbolName );

if ( !mIgnoreXMLTags->isChecked() )
{
symbolTags = src->tagsOfSymbol( !symbol ? QgsStyle::ColorrampEntity : QgsStyle::SymbolEntity, symbolName );
}
else
{
symbolTags.clear();
}

if ( mDialogMode == Import )
{
symbolTags << importTags;
symbolFavorite = mFavorite->isChecked();
}
else
{
symbolFavorite = !symbol ? favoriteColorramps.contains( symbolName ) : favoriteSymbols.contains( symbolName );
}

if ( !symbol )
{
isSymbol = false;
@@ -256,8 +289,7 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
continue;
case QMessageBox::Yes:
dst->addSymbol( symbolName, symbol );
if ( mDialogMode == Import )
dst->saveSymbol( symbolName, symbol, mFavorite->isChecked(), tags );
dst->saveSymbol( symbolName, symbol, symbolFavorite, symbolTags );
continue;
case QMessageBox::YesToAll:
prompt = false;
@@ -273,8 +305,7 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
if ( dst->symbolNames().contains( symbolName ) && overwrite )
{
dst->addSymbol( symbolName, symbol );
if ( mDialogMode == Import )
dst->saveSymbol( symbolName, symbol, mFavorite->isChecked(), tags );
dst->saveSymbol( symbolName, symbol, symbolFavorite, symbolTags );
}
else if ( dst->symbolNames().contains( symbolName ) && !overwrite )
{
@@ -283,8 +314,7 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
else
{
dst->addSymbol( symbolName, symbol );
if ( mDialogMode == Import )
dst->saveSymbol( symbolName, symbol, mFavorite->isChecked(), tags );
dst->saveSymbol( symbolName, symbol, symbolFavorite, symbolTags );
}
}
else
@@ -303,8 +333,7 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
continue;
case QMessageBox::Yes:
dst->addColorRamp( symbolName, ramp );
if ( mDialogMode == Import )
dst->saveColorRamp( symbolName, ramp, mFavorite->isChecked(), tags );
dst->saveColorRamp( symbolName, ramp, symbolFavorite, symbolTags );
continue;
case QMessageBox::YesToAll:
prompt = false;
@@ -320,8 +349,7 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
if ( dst->colorRampNames().contains( symbolName ) && overwrite )
{
dst->addColorRamp( symbolName, ramp );
if ( mDialogMode == Import )
dst->saveColorRamp( symbolName, ramp, mFavorite->isChecked(), tags );
dst->saveColorRamp( symbolName, ramp, symbolFavorite, symbolTags );
}
else if ( dst->colorRampNames().contains( symbolName ) && !overwrite )
{
@@ -330,8 +358,7 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
else
{
dst->addColorRamp( symbolName, ramp );
if ( mDialogMode == Import )
dst->saveColorRamp( symbolName, ramp, mFavorite->isChecked() , tags );
dst->saveColorRamp( symbolName, ramp, symbolFavorite, symbolTags );
}
}
}
38 changes: 30 additions & 8 deletions src/ui/qgsstyleexportimportdialogbase.ui
Original file line number Diff line number Diff line change
@@ -43,10 +43,10 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="tagLabel">
<property name="text">
<string>Tag(s)</string>
<string>Additional tag(s)</string>
</property>
</widget>
</item>
@@ -63,10 +63,23 @@
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QLineEdit" name="mSymbolTags"/>
<item row="3" column="0" colspan="3">
<widget class="QCheckBox" name="mIgnoreXMLTags">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Do not import embedded tags</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QLineEdit" name="mSymbolTags"/>
</item>
<item row="5" column="1" colspan="2">
<widget class="QLabel" name="tagHintLabel">
<property name="text">
<string>Tip: separate multiple tags with commas</string>
@@ -98,12 +111,15 @@
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
<width>77</width>
<height>70</height>
</size>
</property>
<property name="movement">
<enum>QListView::Static</enum>
<property name="textElideMode">
<enum>Qt::ElideNone</enum>
</property>
<property name="flow">
<enum>QListView::LeftToRight</enum>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
@@ -114,6 +130,12 @@
<property name="viewMode">
<enum>QListView::IconMode</enum>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
24 changes: 5 additions & 19 deletions tests/src/core/testqgsstyle.cpp
Original file line number Diff line number Diff line change
@@ -93,15 +93,9 @@ void TestStyle::initTestCase()
QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );

// initialize with a clean style
QFile styleFile( QgsApplication::userStylePath() );
if ( styleFile.exists() )
{
styleFile.remove();
QgsDebugMsg( "removed user style file " + styleFile.fileName() );
}
mStyle = QgsStyle::defaultStyle();
// mStyle->clear();
//initize a temporary memory-based style for tests to avoid clashing with shipped symbols
mStyle = new QgsStyle();
mStyle->createMemoryDB();

// cpt-city ramp, small selection available in <testdir>/cpt-city
QgsCptCityArchive::initArchives();
@@ -114,6 +108,7 @@ void TestStyle::cleanupTestCase()
// don't save
// mStyle->save();
delete mStyle;

QgsCptCityArchive::clearArchives();
QgsApplication::exitQgis();

@@ -186,8 +181,7 @@ void TestStyle::testCreateColorRamps()
void TestStyle::testLoadColorRamps()
{
QStringList colorRamps = mStyle->colorRampNames();
QStringList colorRampsTest = QStringList() << QStringLiteral( "BrBG" ) << QStringLiteral( "Spectral" )
<< QStringLiteral( "test_gradient" ) << QStringLiteral( "test_random" )
QStringList colorRampsTest = QStringList() << QStringLiteral( "test_gradient" ) << QStringLiteral( "test_random" )
<< QStringLiteral( "test_cb1" ) << QStringLiteral( "test_cb2" );

// values for color tests
@@ -235,11 +229,6 @@ void TestStyle::testLoadColorRamps()

void TestStyle::testSaveLoad()
{
// save not needed anymore, because used update=true in addColorRamp()
// mStyle->save();
mStyle->clear();
mStyle->load( QgsApplication::userStylePath() );

// basic test to see that ramp is present
QStringList colorRamps = mStyle->colorRampNames();
QgsDebugMsg( "loaded colorRamps: " + colorRamps.join( " " ) );
@@ -261,8 +250,6 @@ void TestStyle::testSaveLoad()

void TestStyle::testFavorites()
{
mStyle->clear();

// save initial number of favorites to compare against additions / substractions
QStringList favorites;
favorites = mStyle->symbolsOfFavorite( QgsStyle::SymbolEntity );
@@ -290,7 +277,6 @@ void TestStyle::testFavorites()

void TestStyle::testTags()
{
mStyle->clear();
//add some tags
int id = mStyle->addTag( QStringLiteral( "red" ) );
QCOMPARE( id, mStyle->tagId( "red" ) );

0 comments on commit ee71077

Please sign in to comment.