Skip to content

Commit ee71077

Browse files
authoredNov 21, 2016
Merge pull request #3789 from nirvn/style_import_export_imp
[style manager] imporve import and export experience, save symbols' tags & favorite flag
2 parents eda412d + 2260780 commit ee71077

File tree

6 files changed

+205
-59
lines changed

6 files changed

+205
-59
lines changed
 

‎python/core/symbology-ng/qgsstyle.sip

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ class QgsStyle : QObject
208208
//! Changes ramp's name
209209
bool renameColorRamp( const QString& oldName, const QString& newName );
210210

211+
//! Creates a temporary memory database
212+
bool createMemoryDB();
213+
211214
//! Loads a file into the style
212215
bool load( const QString& filename );
213216

‎src/core/symbology-ng/qgsstyle.cpp

Lines changed: 105 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,42 @@ bool QgsStyle::openDB( const QString& filename )
289289
return true;
290290
}
291291

292+
bool QgsStyle::createMemoryDB()
293+
{
294+
mErrorString.clear();
295+
if ( !openDB( QStringLiteral( ":memory:" ) ) )
296+
{
297+
mErrorString = QStringLiteral( "Unable to create temporary memory database" );
298+
QgsDebugMsg( mErrorString );
299+
return false;
300+
}
301+
char *query = sqlite3_mprintf( "CREATE TABLE symbol("\
302+
"id INTEGER PRIMARY KEY,"\
303+
"name TEXT UNIQUE,"\
304+
"xml TEXT,"\
305+
"favorite INTEGER);"\
306+
"CREATE TABLE colorramp("\
307+
"id INTEGER PRIMARY KEY,"\
308+
"name TEXT UNIQUE,"\
309+
"xml TEXT,"\
310+
"favorite INTEGER);"\
311+
"CREATE TABLE tag("\
312+
"id INTEGER PRIMARY KEY,"\
313+
"name TEXT);"\
314+
"CREATE TABLE tagmap("\
315+
"tag_id INTEGER NOT NULL,"\
316+
"symbol_id INTEGER);"\
317+
"CREATE TABLE ctagmap("\
318+
"tag_id INTEGER NOT NULL,"\
319+
"colorramp_id INTEGER);"\
320+
"CREATE TABLE smartgroup("\
321+
"id INTEGER PRIMARY KEY,"\
322+
"name TEXT,"\
323+
"xml TEXT);" );
324+
runEmptyQuery( query );
325+
return true;
326+
}
327+
292328
bool QgsStyle::load( const QString& filename )
293329
{
294330
mErrorString.clear();
@@ -1391,14 +1427,42 @@ bool QgsStyle::exportXml( const QString& filename )
13911427
root.setAttribute( QStringLiteral( "version" ), STYLE_CURRENT_VERSION );
13921428
doc.appendChild( root );
13931429

1394-
// TODO work on the groups and tags
1430+
QStringList favoriteSymbols = symbolsOfFavorite( SymbolEntity );
1431+
QStringList favoriteColorramps = symbolsOfFavorite( ColorrampEntity );
1432+
1433+
// save symbols and attach tags
13951434
QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( mSymbols, QStringLiteral( "symbols" ), doc );
1396-
QDomElement rampsElem = doc.createElement( QStringLiteral( "colorramps" ) );
1435+
QDomNodeList symbolsList = symbolsElem.elementsByTagName( QStringLiteral( "symbol" ) );
1436+
int nbSymbols = symbolsList.count();
1437+
for ( int i = 0; i < nbSymbols; ++i )
1438+
{
1439+
QDomElement symbol = symbolsList.at( i ).toElement();
1440+
QString name = symbol.attribute( QStringLiteral( "name" ) );
1441+
QStringList tags = tagsOfSymbol( SymbolEntity, name );
1442+
if ( tags.count() > 0 )
1443+
{
1444+
symbol.setAttribute( QStringLiteral( "tags" ), tags.join( "," ) );
1445+
}
1446+
if ( favoriteSymbols.contains( name ) )
1447+
{
1448+
symbol.setAttribute( QStringLiteral( "favorite" ), "1" );
1449+
}
1450+
}
13971451

13981452
// save color ramps
1453+
QDomElement rampsElem = doc.createElement( QStringLiteral( "colorramps" ) );
13991454
for ( QMap<QString, QgsColorRamp*>::const_iterator itr = mColorRamps.constBegin(); itr != mColorRamps.constEnd(); ++itr )
14001455
{
14011456
QDomElement rampEl = QgsSymbolLayerUtils::saveColorRamp( itr.key(), itr.value(), doc );
1457+
QStringList tags = tagsOfSymbol( ColorrampEntity, itr.key() );
1458+
if ( tags.count() > 0 )
1459+
{
1460+
rampEl.setAttribute( QStringLiteral( "tags" ), tags.join( "," ) );
1461+
}
1462+
if ( favoriteColorramps.contains( itr.key() ) )
1463+
{
1464+
rampEl.setAttribute( QStringLiteral( "favorite" ), "1" );
1465+
}
14021466
rampsElem.appendChild( rampEl );
14031467
}
14041468

@@ -1469,10 +1533,26 @@ bool QgsStyle::importXml( const QString& filename )
14691533
{
14701534
if ( e.tagName() == QLatin1String( "symbol" ) )
14711535
{
1536+
QString name = e.attribute( QStringLiteral( "name" ) );
1537+
QStringList tags;
1538+
if ( e.hasAttribute( QStringLiteral( "tags" ) ) )
1539+
{
1540+
tags = e.attribute( QStringLiteral( "tags" ) ).split( "," );
1541+
}
1542+
bool favorite = false;
1543+
if ( e.hasAttribute( QStringLiteral( "favorite" ) ) && e.attribute( QStringLiteral( "favorite" ) ) == "1" )
1544+
{
1545+
favorite = true;
1546+
}
1547+
14721548
QgsSymbol* symbol = QgsSymbolLayerUtils::loadSymbol( e );
14731549
if ( symbol )
14741550
{
1475-
symbols.insert( e.attribute( QStringLiteral( "name" ) ), symbol );
1551+
addSymbol( name, symbol );
1552+
if ( mCurrentDB )
1553+
{
1554+
saveSymbol( name, symbol, favorite, tags );
1555+
}
14761556
}
14771557
}
14781558
else
@@ -1486,12 +1566,12 @@ bool QgsStyle::importXml( const QString& filename )
14861566
{
14871567
// for the old version, use the utility function to solve @symbol@layer subsymbols
14881568
symbols = QgsSymbolLayerUtils::loadSymbols( symbolsElement );
1489-
}
14901569

1491-
// save the symbols with proper name
1492-
for ( QMap<QString, QgsSymbol*>::iterator it = symbols.begin(); it != symbols.end(); ++it )
1493-
{
1494-
addSymbol( it.key(), it.value() );
1570+
// save the symbols with proper name
1571+
for ( QMap<QString, QgsSymbol*>::iterator it = symbols.begin(); it != symbols.end(); ++it )
1572+
{
1573+
addSymbol( it.key(), it.value() );
1574+
}
14951575
}
14961576

14971577
// load color ramps
@@ -1501,10 +1581,26 @@ bool QgsStyle::importXml( const QString& filename )
15011581
{
15021582
if ( e.tagName() == QLatin1String( "colorramp" ) )
15031583
{
1584+
QString name = e.attribute( QStringLiteral( "name" ) );
1585+
QStringList tags;
1586+
if ( e.hasAttribute( QStringLiteral( "tags" ) ) )
1587+
{
1588+
tags = e.attribute( QStringLiteral( "tags" ) ).split( "," );
1589+
}
1590+
bool favorite = false;
1591+
if ( e.hasAttribute( QStringLiteral( "favorite" ) ) && e.attribute( QStringLiteral( "favorite" ) ) == "1" )
1592+
{
1593+
favorite = true;
1594+
}
1595+
15041596
QgsColorRamp* ramp = QgsSymbolLayerUtils::loadColorRamp( e );
15051597
if ( ramp )
15061598
{
1507-
addColorRamp( e.attribute( QStringLiteral( "name" ) ), ramp );
1599+
addColorRamp( name, ramp );
1600+
if ( mCurrentDB )
1601+
{
1602+
saveColorRamp( name, ramp, favorite, tags );
1603+
}
15081604
}
15091605
}
15101606
else

‎src/core/symbology-ng/qgsstyle.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,19 @@ class CORE_EXPORT QgsStyle : public QObject
273273
//! Changes ramp's name
274274
bool renameColorRamp( const QString& oldName, const QString& newName );
275275

276-
//! Loads a file into the style
276+
/** Creates a temporary memory database
277+
*
278+
* This function is used if you do not need to associate styles with a permanent on-disk database.
279+
* \return returns the success state of the temporary memory database creation
280+
*/
281+
bool createMemoryDB();
282+
283+
/** Loads a file into the style
284+
*
285+
* This function will populate styles from an on-disk database.
286+
* \param filename location of the database to load styles from
287+
* \return returns the success state of the database being loaded
288+
*/
277289
bool load( const QString& filename );
278290

279291
//! Saves style into a file (will use current filename if empty string is passed)

‎src/gui/symbology-ng/qgsstyleexportimportdialog.cpp

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,13 @@ QgsStyleExportImportDialog::QgsStyleExportImportDialog( QgsStyle* style, QWidget
5050
connect( pb, SIGNAL( clicked() ), this, SLOT( clearSelection() ) );
5151

5252
QStandardItemModel* model = new QStandardItemModel( listItems );
53-
5453
listItems->setModel( model );
5554
connect( listItems->selectionModel(), SIGNAL( selectionChanged( const QItemSelection&, const QItemSelection& ) ),
5655
this, SLOT( selectionChanged( const QItemSelection&, const QItemSelection& ) ) );
5756

5857
mTempStyle = new QgsStyle();
58+
mTempStyle->createMemoryDB();
59+
5960
// TODO validate
6061
mFileName = QLatin1String( "" );
6162
mProgressDlg = nullptr;
@@ -92,6 +93,7 @@ QgsStyleExportImportDialog::QgsStyleExportImportDialog( QgsStyle* style, QWidget
9293
locationLineEdit->setHidden( true );
9394

9495
mFavorite->setHidden( true );
96+
mIgnoreXMLTags->setHidden( true );
9597

9698
pb = new QPushButton( tr( "Select by group" ) );
9799
buttonBox->addButton( pb, QDialogButtonBox::ActionRole );
@@ -148,6 +150,12 @@ void QgsStyleExportImportDialog::doExportImport()
148150
.arg( mTempStyle->errorString() ) );
149151
return;
150152
}
153+
else
154+
{
155+
QMessageBox::information( this, tr( "Export successful" ),
156+
tr( "The selected symbols were successfully exported to file:\n%1" )
157+
.arg( mFileName ) );
158+
}
151159
}
152160
else // import
153161
{
@@ -187,10 +195,16 @@ bool QgsStyleExportImportDialog::populateStyles( QgsStyle* style )
187195
for ( int i = 0; i < styleNames.count(); ++i )
188196
{
189197
name = styleNames[i];
198+
QStringList tags = style->tagsOfSymbol( QgsStyle::SymbolEntity, name );
190199
QgsSymbol* symbol = style->symbol( name );
191200
QStandardItem* item = new QStandardItem( name );
192-
QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( symbol, listItems->iconSize() );
201+
QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( symbol, listItems->iconSize(), 15 );
193202
item->setIcon( icon );
203+
item->setToolTip( QString( "<b>%1</b><br><i>%2</i>" ).arg( name ).arg( tags.count() > 0 ? tags.join( ", " ) : tr( "Not tagged" ) ) );
204+
// Set font to 10points to show reasonable text
205+
QFont itemFont = item->font();
206+
itemFont.setPointSize( 10 );
207+
item->setFont( itemFont );
194208
model->appendRow( item );
195209
delete symbol;
196210
}
@@ -204,7 +218,7 @@ bool QgsStyleExportImportDialog::populateStyles( QgsStyle* style )
204218
QScopedPointer< QgsColorRamp > ramp( style->colorRamp( name ) );
205219

206220
QStandardItem* item = new QStandardItem( name );
207-
QIcon icon = QgsSymbolLayerUtils::colorRampPreviewIcon( ramp.data(), listItems->iconSize() );
221+
QIcon icon = QgsSymbolLayerUtils::colorRampPreviewIcon( ramp.data(), listItems->iconSize(), 15 );
208222
item->setIcon( icon );
209223
model->appendRow( item );
210224
}
@@ -215,25 +229,44 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
215229
{
216230
QString symbolName;
217231
QgsSymbol* symbol;
232+
QStringList symbolTags;
233+
bool symbolFavorite;
218234
QgsColorRamp *ramp = nullptr;
219235
QModelIndex index;
220236
bool isSymbol = true;
221237
bool prompt = true;
222238
bool overwrite = true;
223-
QStringList tags;
224239

225-
// get the groupid when going for import
226-
if ( mDialogMode == Import )
227-
{
228-
// get the name the user entered
229-
tags = mSymbolTags->text().split( ',' );
230-
}
240+
QStringList importTags = mSymbolTags->text().split( ',' );
241+
242+
QStringList favoriteSymbols = src->symbolsOfFavorite( QgsStyle::SymbolEntity );
243+
QStringList favoriteColorramps = src->symbolsOfFavorite( QgsStyle::ColorrampEntity );
231244

232245
for ( int i = 0; i < selection->size(); ++i )
233246
{
234247
index = selection->at( i );
235248
symbolName = index.model()->data( index, 0 ).toString();
236249
symbol = src->symbol( symbolName );
250+
251+
if ( !mIgnoreXMLTags->isChecked() )
252+
{
253+
symbolTags = src->tagsOfSymbol( !symbol ? QgsStyle::ColorrampEntity : QgsStyle::SymbolEntity, symbolName );
254+
}
255+
else
256+
{
257+
symbolTags.clear();
258+
}
259+
260+
if ( mDialogMode == Import )
261+
{
262+
symbolTags << importTags;
263+
symbolFavorite = mFavorite->isChecked();
264+
}
265+
else
266+
{
267+
symbolFavorite = !symbol ? favoriteColorramps.contains( symbolName ) : favoriteSymbols.contains( symbolName );
268+
}
269+
237270
if ( !symbol )
238271
{
239272
isSymbol = false;
@@ -256,8 +289,7 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
256289
continue;
257290
case QMessageBox::Yes:
258291
dst->addSymbol( symbolName, symbol );
259-
if ( mDialogMode == Import )
260-
dst->saveSymbol( symbolName, symbol, mFavorite->isChecked(), tags );
292+
dst->saveSymbol( symbolName, symbol, symbolFavorite, symbolTags );
261293
continue;
262294
case QMessageBox::YesToAll:
263295
prompt = false;
@@ -273,8 +305,7 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
273305
if ( dst->symbolNames().contains( symbolName ) && overwrite )
274306
{
275307
dst->addSymbol( symbolName, symbol );
276-
if ( mDialogMode == Import )
277-
dst->saveSymbol( symbolName, symbol, mFavorite->isChecked(), tags );
308+
dst->saveSymbol( symbolName, symbol, symbolFavorite, symbolTags );
278309
}
279310
else if ( dst->symbolNames().contains( symbolName ) && !overwrite )
280311
{
@@ -283,8 +314,7 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
283314
else
284315
{
285316
dst->addSymbol( symbolName, symbol );
286-
if ( mDialogMode == Import )
287-
dst->saveSymbol( symbolName, symbol, mFavorite->isChecked(), tags );
317+
dst->saveSymbol( symbolName, symbol, symbolFavorite, symbolTags );
288318
}
289319
}
290320
else
@@ -303,8 +333,7 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
303333
continue;
304334
case QMessageBox::Yes:
305335
dst->addColorRamp( symbolName, ramp );
306-
if ( mDialogMode == Import )
307-
dst->saveColorRamp( symbolName, ramp, mFavorite->isChecked(), tags );
336+
dst->saveColorRamp( symbolName, ramp, symbolFavorite, symbolTags );
308337
continue;
309338
case QMessageBox::YesToAll:
310339
prompt = false;
@@ -320,8 +349,7 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
320349
if ( dst->colorRampNames().contains( symbolName ) && overwrite )
321350
{
322351
dst->addColorRamp( symbolName, ramp );
323-
if ( mDialogMode == Import )
324-
dst->saveColorRamp( symbolName, ramp, mFavorite->isChecked(), tags );
352+
dst->saveColorRamp( symbolName, ramp, symbolFavorite, symbolTags );
325353
}
326354
else if ( dst->colorRampNames().contains( symbolName ) && !overwrite )
327355
{
@@ -330,8 +358,7 @@ void QgsStyleExportImportDialog::moveStyles( QModelIndexList* selection, QgsStyl
330358
else
331359
{
332360
dst->addColorRamp( symbolName, ramp );
333-
if ( mDialogMode == Import )
334-
dst->saveColorRamp( symbolName, ramp, mFavorite->isChecked() , tags );
361+
dst->saveColorRamp( symbolName, ramp, symbolFavorite, symbolTags );
335362
}
336363
}
337364
}

‎src/ui/qgsstyleexportimportdialogbase.ui

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@
4343
</property>
4444
</widget>
4545
</item>
46-
<item row="3" column="0">
46+
<item row="4" column="0">
4747
<widget class="QLabel" name="tagLabel">
4848
<property name="text">
49-
<string>Tag(s)</string>
49+
<string>Additional tag(s)</string>
5050
</property>
5151
</widget>
5252
</item>
@@ -63,10 +63,23 @@
6363
</property>
6464
</widget>
6565
</item>
66-
<item row="3" column="1" colspan="2">
67-
<widget class="QLineEdit" name="mSymbolTags"/>
66+
<item row="3" column="0" colspan="3">
67+
<widget class="QCheckBox" name="mIgnoreXMLTags">
68+
<property name="sizePolicy">
69+
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
70+
<horstretch>0</horstretch>
71+
<verstretch>0</verstretch>
72+
</sizepolicy>
73+
</property>
74+
<property name="text">
75+
<string>Do not import embedded tags</string>
76+
</property>
77+
</widget>
6878
</item>
6979
<item row="4" column="1" colspan="2">
80+
<widget class="QLineEdit" name="mSymbolTags"/>
81+
</item>
82+
<item row="5" column="1" colspan="2">
7083
<widget class="QLabel" name="tagHintLabel">
7184
<property name="text">
7285
<string>Tip: separate multiple tags with commas</string>
@@ -98,12 +111,15 @@
98111
</property>
99112
<property name="iconSize">
100113
<size>
101-
<width>48</width>
102-
<height>48</height>
114+
<width>77</width>
115+
<height>70</height>
103116
</size>
104117
</property>
105-
<property name="movement">
106-
<enum>QListView::Static</enum>
118+
<property name="textElideMode">
119+
<enum>Qt::ElideNone</enum>
120+
</property>
121+
<property name="flow">
122+
<enum>QListView::LeftToRight</enum>
107123
</property>
108124
<property name="resizeMode">
109125
<enum>QListView::Adjust</enum>
@@ -114,6 +130,12 @@
114130
<property name="viewMode">
115131
<enum>QListView::IconMode</enum>
116132
</property>
133+
<property name="uniformItemSizes">
134+
<bool>true</bool>
135+
</property>
136+
<property name="wordWrap">
137+
<bool>true</bool>
138+
</property>
117139
</widget>
118140
</item>
119141
<item row="3" column="0">

‎tests/src/core/testqgsstyle.cpp

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,9 @@ void TestStyle::initTestCase()
9393
QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
9494
QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
9595

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

106100
// cpt-city ramp, small selection available in <testdir>/cpt-city
107101
QgsCptCityArchive::initArchives();
@@ -114,6 +108,7 @@ void TestStyle::cleanupTestCase()
114108
// don't save
115109
// mStyle->save();
116110
delete mStyle;
111+
117112
QgsCptCityArchive::clearArchives();
118113
QgsApplication::exitQgis();
119114

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

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

236230
void TestStyle::testSaveLoad()
237231
{
238-
// save not needed anymore, because used update=true in addColorRamp()
239-
// mStyle->save();
240-
mStyle->clear();
241-
mStyle->load( QgsApplication::userStylePath() );
242-
243232
// basic test to see that ramp is present
244233
QStringList colorRamps = mStyle->colorRampNames();
245234
QgsDebugMsg( "loaded colorRamps: " + colorRamps.join( " " ) );
@@ -261,8 +250,6 @@ void TestStyle::testSaveLoad()
261250

262251
void TestStyle::testFavorites()
263252
{
264-
mStyle->clear();
265-
266253
// save initial number of favorites to compare against additions / substractions
267254
QStringList favorites;
268255
favorites = mStyle->symbolsOfFavorite( QgsStyle::SymbolEntity );
@@ -290,7 +277,6 @@ void TestStyle::testFavorites()
290277

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

0 commit comments

Comments
 (0)
Please sign in to comment.