Skip to content

Commit 95d65ae

Browse files
committedSep 12, 2018
Add python safe addSmartgroup method, unit tests for smart groups
1 parent c41af12 commit 95d65ae

File tree

4 files changed

+141
-16
lines changed

4 files changed

+141
-16
lines changed
 

‎python/core/auto_generated/symbology/qgsstyle.sip.in

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,21 @@ Adds a new tag and returns the tag's id
8686
%End
8787

8888

89+
int addSmartgroup( const QString &name, const QString &op, const QStringList &matchTag, const QStringList &noMatchTag,
90+
const QStringList &matchName, const QStringList &noMatchName );
91+
%Docstring
92+
Adds a new smartgroup to the database and returns the id.
93+
94+
:param name: is the name of the new Smart Group to be added
95+
:param op: is the operator between the conditions; AND/OR as QString
96+
:param matchTag: list of strings to match within tags
97+
:param noMatchTag: list of strings to exclude matches from tags
98+
:param matchName: list of string to match within names
99+
:param noMatchName: list of strings to exclude matches from names
100+
101+
.. versionadded:: 3.4
102+
%End
103+
89104
QStringList tags() const;
90105
%Docstring
91106
Returns a list of all tags in the style database

‎src/core/symbology/qgsstyle.cpp

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,26 +1169,34 @@ int QgsStyle::smartgroupId( const QString &name )
11691169
}
11701170

11711171
int QgsStyle::addSmartgroup( const QString &name, const QString &op, const QgsSmartConditionMap &conditions )
1172+
{
1173+
return addSmartgroup( name, op, conditions.values( QStringLiteral( "tag" ) ),
1174+
conditions.values( QStringLiteral( "!tag" ) ),
1175+
conditions.values( QStringLiteral( "name" ) ),
1176+
conditions.values( QStringLiteral( "!name" ) ) );
1177+
}
1178+
1179+
int QgsStyle::addSmartgroup( const QString &name, const QString &op, const QStringList &matchTag, const QStringList &noMatchTag, const QStringList &matchName, const QStringList &noMatchName )
11721180
{
11731181
QDomDocument doc( QStringLiteral( "dummy" ) );
11741182
QDomElement smartEl = doc.createElement( QStringLiteral( "smartgroup" ) );
11751183
smartEl.setAttribute( QStringLiteral( "name" ), name );
11761184
smartEl.setAttribute( QStringLiteral( "operator" ), op );
11771185

1178-
QStringList constraints;
1179-
constraints << QStringLiteral( "tag" ) << QStringLiteral( "group" ) << QStringLiteral( "name" ) << QStringLiteral( "!tag" ) << QStringLiteral( "!group" ) << QStringLiteral( "!name" );
1180-
1181-
Q_FOREACH ( const QString &constraint, constraints )
1186+
auto addCondition = [&doc, &smartEl]( const QString & constraint, const QStringList & parameters )
11821187
{
1183-
QStringList parameters = conditions.values( constraint );
1184-
Q_FOREACH ( const QString &param, parameters )
1188+
for ( const QString &param : parameters )
11851189
{
11861190
QDomElement condEl = doc.createElement( QStringLiteral( "condition" ) );
11871191
condEl.setAttribute( QStringLiteral( "constraint" ), constraint );
11881192
condEl.setAttribute( QStringLiteral( "param" ), param );
11891193
smartEl.appendChild( condEl );
11901194
}
1191-
}
1195+
};
1196+
addCondition( QStringLiteral( "tag" ), matchTag );
1197+
addCondition( QStringLiteral( "!tag" ), noMatchTag );
1198+
addCondition( QStringLiteral( "name" ), matchName );
1199+
addCondition( QStringLiteral( "!name" ), noMatchName );
11921200

11931201
QByteArray xmlArray;
11941202
QTextStream stream( &xmlArray );
@@ -1312,16 +1320,16 @@ QStringList QgsStyle::symbolsOfSmartgroup( StyleEntity type, int id )
13121320
else if ( constraint == QLatin1String( "!tag" ) )
13131321
{
13141322
resultNames = type == SymbolEntity ? symbolNames() : colorRampNames();
1315-
QStringList unwanted = symbolsWithTag( type, tagId( param ) );
1316-
Q_FOREACH ( const QString &name, unwanted )
1323+
const QStringList unwanted = symbolsWithTag( type, tagId( param ) );
1324+
for ( const QString &name : unwanted )
13171325
{
13181326
resultNames.removeAll( name );
13191327
}
13201328
}
13211329
else if ( constraint == QLatin1String( "!name" ) )
13221330
{
1323-
QStringList all = type == SymbolEntity ? symbolNames() : colorRampNames();
1324-
Q_FOREACH ( const QString &str, all )
1331+
const QStringList all = type == SymbolEntity ? symbolNames() : colorRampNames();
1332+
for ( const QString &str : all )
13251333
{
13261334
if ( !str.contains( param, Qt::CaseInsensitive ) )
13271335
resultNames << str;
@@ -1344,7 +1352,7 @@ QStringList QgsStyle::symbolsOfSmartgroup( StyleEntity type, int id )
13441352
{
13451353
QStringList dummy = symbols;
13461354
symbols.clear();
1347-
Q_FOREACH ( const QString &result, resultNames )
1355+
for ( const QString &result : qgis::as_const( resultNames ) )
13481356
{
13491357
if ( dummy.contains( result ) )
13501358
symbols << result;
@@ -1354,7 +1362,10 @@ QStringList QgsStyle::symbolsOfSmartgroup( StyleEntity type, int id )
13541362
} // DOM loop ends here
13551363
}
13561364

1357-
return symbols;
1365+
// return sorted, unique list
1366+
QStringList unique = symbols.toSet().toList();
1367+
std::sort( unique.begin(), unique.end() );
1368+
return unique;
13581369
}
13591370

13601371
QgsSmartConditionMap QgsStyle::smartgroup( int id )
@@ -1437,7 +1448,7 @@ bool QgsStyle::exportXml( const QString &filename )
14371448

14381449
QDomDocument doc( QStringLiteral( "qgis_style" ) );
14391450
QDomElement root = doc.createElement( QStringLiteral( "qgis_style" ) );
1440-
root.setAttribute( QStringLiteral( "version" ), STYLE_CURRENT_VERSION );
1451+
root.setAttribute( QStringLiteral( "version" ), QStringLiteral( STYLE_CURRENT_VERSION ) );
14411452
doc.appendChild( root );
14421453

14431454
QStringList favoriteSymbols = symbolsOfFavorite( SymbolEntity );
@@ -1528,7 +1539,7 @@ bool QgsStyle::importXml( const QString &filename )
15281539
}
15291540

15301541
QString version = docEl.attribute( QStringLiteral( "version" ) );
1531-
if ( version != STYLE_CURRENT_VERSION && version != QLatin1String( "0" ) )
1542+
if ( version != QLatin1String( STYLE_CURRENT_VERSION ) && version != QLatin1String( "0" ) )
15321543
{
15331544
mErrorString = "Unknown style file version: " + version;
15341545
return false;
@@ -1543,7 +1554,7 @@ bool QgsStyle::importXml( const QString &filename )
15431554
auto query = QgsSqlite3Mprintf( "BEGIN TRANSACTION;" );
15441555
runEmptyQuery( query );
15451556

1546-
if ( version == STYLE_CURRENT_VERSION )
1557+
if ( version == QLatin1String( STYLE_CURRENT_VERSION ) )
15471558
{
15481559
// For the new style, load symbols individually
15491560
while ( !e.isNull() )

‎src/core/symbology/qgsstyle.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,21 @@ class CORE_EXPORT QgsStyle : public QObject
140140
*/
141141
int addSmartgroup( const QString &name, const QString &op, const QgsSmartConditionMap &conditions ) SIP_SKIP;
142142

143+
/**
144+
* Adds a new smartgroup to the database and returns the id.
145+
*
146+
* \param name is the name of the new Smart Group to be added
147+
* \param op is the operator between the conditions; AND/OR as QString
148+
* \param matchTag list of strings to match within tags
149+
* \param noMatchTag list of strings to exclude matches from tags
150+
* \param matchName list of string to match within names
151+
* \param noMatchName list of strings to exclude matches from names
152+
*
153+
* \since QGIS 3.4
154+
*/
155+
int addSmartgroup( const QString &name, const QString &op, const QStringList &matchTag, const QStringList &noMatchTag,
156+
const QStringList &matchName, const QStringList &noMatchName );
157+
143158
/**
144159
* Returns a list of all tags in the style database
145160
*

‎tests/src/core/testqgsstyle.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class TestStyle : public QObject
6767
void testSaveLoad();
6868
void testFavorites();
6969
void testTags();
70+
void testSmartGroup();
7071

7172
};
7273

@@ -551,5 +552,88 @@ void TestStyle::testTags()
551552
QCOMPARE( tagsChangedSpy.at( 13 ).at( 2 ).toStringList(), QStringList() );
552553
}
553554

555+
void TestStyle::testSmartGroup()
556+
{
557+
QgsStyle style;
558+
style.createMemoryDatabase();
559+
560+
QSignalSpy groupModifiedSpy( &style, &QgsStyle::groupsModified );
561+
562+
std::unique_ptr< QgsMarkerSymbol > sym1( QgsMarkerSymbol::createSimple( QgsStringMap() ) );
563+
std::unique_ptr< QgsMarkerSymbol > sym2( QgsMarkerSymbol::createSimple( QgsStringMap() ) );
564+
std::unique_ptr< QgsMarkerSymbol > sym3( QgsMarkerSymbol::createSimple( QgsStringMap() ) );
565+
style.addSymbol( QStringLiteral( "symbolA" ), sym1->clone(), true );
566+
style.addSymbol( QStringLiteral( "symbolB" ), sym2->clone(), true );
567+
style.addSymbol( QStringLiteral( "symbolC" ), sym3->clone(), true );
568+
QgsLimitedRandomColorRamp *randomRamp = new QgsLimitedRandomColorRamp();
569+
QVERIFY( style.addColorRamp( "ramp a", randomRamp, true ) );
570+
randomRamp = new QgsLimitedRandomColorRamp();
571+
QVERIFY( style.addColorRamp( "different bbb", randomRamp, true ) );
572+
573+
QVERIFY( style.smartgroupNames().empty() );
574+
QVERIFY( style.smartgroup( 5 ).isEmpty() );
575+
QCOMPARE( style.smartgroupId( QStringLiteral( "no exist" ) ), 0 );
576+
577+
int res = style.addSmartgroup( QStringLiteral( "mine" ), QStringLiteral( "AND" ), QStringList(), QStringList(), QStringList() << QStringLiteral( "a" ), QStringList() );
578+
QCOMPARE( res, 1 );
579+
QCOMPARE( style.smartgroupNames(), QStringList() << QStringLiteral( "mine" ) );
580+
QCOMPARE( style.smartgroup( 1 ).values( QStringLiteral( "name" ) ), QStringList() << QStringLiteral( "a" ) );
581+
QCOMPARE( style.smartgroupId( QStringLiteral( "mine" ) ), 1 );
582+
QCOMPARE( groupModifiedSpy.count(), 1 );
583+
584+
QCOMPARE( style.symbolsOfSmartgroup( QgsStyle::SymbolEntity, 1 ), QStringList() << QStringLiteral( "symbolA" ) );
585+
QCOMPARE( style.symbolsOfSmartgroup( QgsStyle::ColorrampEntity, 1 ), QStringList() << QStringLiteral( "ramp a" ) );
586+
587+
res = style.addSmartgroup( QStringLiteral( "tag" ), QStringLiteral( "OR" ), QStringList(), QStringList(), QStringList() << "c", QStringList() << "a" );
588+
QCOMPARE( res, 2 );
589+
QCOMPARE( style.smartgroupNames(), QStringList() << QStringLiteral( "mine" ) << QStringLiteral( "tag" ) );
590+
QCOMPARE( style.smartgroup( 2 ).values( QStringLiteral( "name" ) ), QStringList() << QStringLiteral( "c" ) );
591+
QCOMPARE( style.smartgroup( 2 ).values( QStringLiteral( "!name" ) ), QStringList() << QStringLiteral( "a" ) );
592+
QCOMPARE( style.smartgroupId( QStringLiteral( "tag" ) ), 2 );
593+
QCOMPARE( groupModifiedSpy.count(), 2 );
594+
595+
QCOMPARE( style.symbolsOfSmartgroup( QgsStyle::SymbolEntity, 2 ), QStringList() << QStringLiteral( "symbolB" ) << QStringLiteral( "symbolC" ) );
596+
QCOMPARE( style.symbolsOfSmartgroup( QgsStyle::ColorrampEntity, 2 ), QStringList() << QStringLiteral( "different bbb" ) );
597+
598+
// tag some symbols
599+
style.tagSymbol( QgsStyle::SymbolEntity, "symbolA", QStringList() << "red" << "blue" );
600+
style.tagSymbol( QgsStyle::SymbolEntity, "symbolB", QStringList() << "blue" );
601+
style.tagSymbol( QgsStyle::ColorrampEntity, "ramp a", QStringList() << "blue" );
602+
style.tagSymbol( QgsStyle::ColorrampEntity, "different bbb", QStringList() << "blue" << "red" );
603+
604+
// adding tags modifies groups!
605+
QCOMPARE( groupModifiedSpy.count(), 4 );
606+
607+
res = style.addSmartgroup( QStringLiteral( "tags" ), QStringLiteral( "AND" ), QStringList() << "blue", QStringList() << "red", QStringList(), QStringList() );
608+
QCOMPARE( res, 3 );
609+
QCOMPARE( style.smartgroupNames(), QStringList() << QStringLiteral( "mine" ) << QStringLiteral( "tag" ) << QStringLiteral( "tags" ) );
610+
QCOMPARE( style.smartgroup( 3 ).values( QStringLiteral( "tag" ) ), QStringList() << QStringLiteral( "blue" ) );
611+
QCOMPARE( style.smartgroup( 3 ).values( QStringLiteral( "!tag" ) ), QStringList() << QStringLiteral( "red" ) );
612+
QCOMPARE( style.smartgroupId( QStringLiteral( "tags" ) ), 3 );
613+
QCOMPARE( groupModifiedSpy.count(), 5 );
614+
615+
QCOMPARE( style.symbolsOfSmartgroup( QgsStyle::SymbolEntity, 3 ), QStringList() << QStringLiteral( "symbolB" ) );
616+
QCOMPARE( style.symbolsOfSmartgroup( QgsStyle::ColorrampEntity, 3 ), QStringList() << QStringLiteral( "ramp a" ) );
617+
618+
res = style.addSmartgroup( QStringLiteral( "combined" ), QStringLiteral( "AND" ), QStringList() << "blue", QStringList(), QStringList(), QStringList() << "a" );
619+
QCOMPARE( res, 4 );
620+
QCOMPARE( style.smartgroupNames(), QStringList() << QStringLiteral( "mine" ) << QStringLiteral( "tag" ) << QStringLiteral( "tags" ) << QStringLiteral( "combined" ) );
621+
QCOMPARE( style.smartgroup( 4 ).values( QStringLiteral( "tag" ) ), QStringList() << QStringLiteral( "blue" ) );
622+
QCOMPARE( style.smartgroup( 4 ).values( QStringLiteral( "!name" ) ), QStringList() << QStringLiteral( "a" ) );
623+
QCOMPARE( style.smartgroupId( QStringLiteral( "combined" ) ), 4 );
624+
QCOMPARE( groupModifiedSpy.count(), 6 );
625+
626+
QCOMPARE( style.symbolsOfSmartgroup( QgsStyle::SymbolEntity, 4 ), QStringList() << QStringLiteral( "symbolB" ) );
627+
QCOMPARE( style.symbolsOfSmartgroup( QgsStyle::ColorrampEntity, 4 ), QStringList() << QStringLiteral( "different bbb" ) );
628+
629+
style.remove( QgsStyle::SmartgroupEntity, 1 );
630+
QCOMPARE( style.smartgroupNames(), QStringList() << QStringLiteral( "tag" ) << QStringLiteral( "tags" ) << QStringLiteral( "combined" ) );
631+
QCOMPARE( groupModifiedSpy.count(), 7 );
632+
633+
style.remove( QgsStyle::SmartgroupEntity, 4 );
634+
QCOMPARE( style.smartgroupNames(), QStringList() << QStringLiteral( "tag" ) << QStringLiteral( "tags" ) );
635+
QCOMPARE( groupModifiedSpy.count(), 8 );
636+
}
637+
554638
QGSTEST_MAIN( TestStyle )
555639
#include "testqgsstyle.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.