Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsprojectstylealgorithms.cpp
3 : : ---------------------
4 : : begin : July 2019
5 : : copyright : (C) 2019 by Nyall Dawson
6 : : email : nyall dot dawson at gmail dot com
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #include "qgsprojectstylealgorithms.h"
19 : : #include "qgsstyle.h"
20 : :
21 : : ///@cond PRIVATE
22 : :
23 : : //
24 : : // QgsProjectToStyleVisitor
25 : : //
26 : :
27 : 0 : QgsSaveToStyleVisitor::QgsSaveToStyleVisitor( QgsStyle *style, const QList<QgsStyle::StyleEntity> &objects )
28 : 0 : : mStyle( style )
29 : 0 : , mObjects( objects )
30 : 0 : {
31 : 0 : }
32 : :
33 : 0 : bool QgsSaveToStyleVisitor::visit( const QgsStyleEntityVisitorInterface::StyleLeaf &entity )
34 : : {
35 : 0 : if ( mObjects.empty() || mObjects.contains( entity.entity->type() ) )
36 : : {
37 : 0 : const QString name = QString( mParentNames.join( ' ' ) + ' ' + entity.description ).trimmed();
38 : 0 : QString candidate = name;
39 : 0 : int i = 1;
40 : 0 : bool exists = true;
41 : 0 : while ( exists )
42 : : {
43 : 0 : exists = mStyle->allNames( entity.entity->type() ).contains( candidate );
44 : 0 : if ( !exists )
45 : 0 : break;
46 : :
47 : 0 : i++;
48 : 0 : candidate = name + QStringLiteral( " (%1)" ).arg( i );
49 : : }
50 : 0 : mStyle->addEntity( candidate, entity.entity, true );
51 : 0 : }
52 : 0 : return true;
53 : 0 : }
54 : :
55 : 0 : bool QgsSaveToStyleVisitor::visitEnter( const QgsStyleEntityVisitorInterface::Node &node )
56 : : {
57 : 0 : switch ( node.type )
58 : : {
59 : : case QgsStyleEntityVisitorInterface::NodeType::Project:
60 : : case QgsStyleEntityVisitorInterface::NodeType::Layouts:
61 : : case QgsStyleEntityVisitorInterface::NodeType::LayoutItem:
62 : : case QgsStyleEntityVisitorInterface::NodeType::ReportHeader:
63 : : case QgsStyleEntityVisitorInterface::NodeType::ReportFooter:
64 : : case QgsStyleEntityVisitorInterface::NodeType::ReportSection:
65 : : case QgsStyleEntityVisitorInterface::NodeType::Annotations:
66 : 0 : break;
67 : :
68 : : case QgsStyleEntityVisitorInterface::NodeType::Layer:
69 : : case QgsStyleEntityVisitorInterface::NodeType::PrintLayout:
70 : : case QgsStyleEntityVisitorInterface::NodeType::Report:
71 : : case QgsStyleEntityVisitorInterface::NodeType::Annotation:
72 : : case QgsStyleEntityVisitorInterface::NodeType::SymbolRule:
73 : 0 : mParentNames << node.description;
74 : 0 : break;
75 : : }
76 : 0 : return true;
77 : : }
78 : :
79 : 0 : bool QgsSaveToStyleVisitor::visitExit( const QgsStyleEntityVisitorInterface::Node &node )
80 : : {
81 : 0 : switch ( node.type )
82 : : {
83 : : case QgsStyleEntityVisitorInterface::NodeType::Project:
84 : : case QgsStyleEntityVisitorInterface::NodeType::Layouts:
85 : : case QgsStyleEntityVisitorInterface::NodeType::LayoutItem:
86 : : case QgsStyleEntityVisitorInterface::NodeType::ReportHeader:
87 : : case QgsStyleEntityVisitorInterface::NodeType::ReportFooter:
88 : : case QgsStyleEntityVisitorInterface::NodeType::ReportSection:
89 : : case QgsStyleEntityVisitorInterface::NodeType::Annotations:
90 : 0 : break;
91 : :
92 : : case QgsStyleEntityVisitorInterface::NodeType::Layer:
93 : : case QgsStyleEntityVisitorInterface::NodeType::PrintLayout:
94 : : case QgsStyleEntityVisitorInterface::NodeType::Report:
95 : : case QgsStyleEntityVisitorInterface::NodeType::Annotation:
96 : : case QgsStyleEntityVisitorInterface::NodeType::SymbolRule:
97 : 0 : mParentNames.pop_back();
98 : 0 : break;
99 : : }
100 : 0 : return true;
101 : : }
102 : :
103 : : //
104 : : // QgsStyleFromProjectAlgorithm
105 : : //
106 : :
107 : 0 : QgsStyleFromProjectAlgorithm::QgsStyleFromProjectAlgorithm() = default;
108 : :
109 : 0 : QgsStyleFromProjectAlgorithm::~QgsStyleFromProjectAlgorithm() = default;
110 : :
111 : 0 : void QgsStyleFromProjectAlgorithm::initAlgorithm( const QVariantMap & )
112 : : {
113 : 0 : addParameter( new QgsProcessingParameterFile( QStringLiteral( "INPUT" ), QObject::tr( "Input project (leave blank to use current)" ), QgsProcessingParameterFile::File,
114 : 0 : QString(), QVariant(), true, QObject::tr( "QGIS files" ) + QStringLiteral( " (*.qgs *.qgz *.QGS)" ) ) );
115 : :
116 : 0 : addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Output style database" ),
117 : 0 : QObject::tr( "Style files (*.xml)" ) ) );
118 : :
119 : 0 : QStringList options = QStringList()
120 : 0 : << QObject::tr( "Symbols" )
121 : 0 : << QObject::tr( "Color ramps" )
122 : 0 : << QObject::tr( "Text formats" )
123 : 0 : << QObject::tr( "Label settings" );
124 : 0 : addParameter( new QgsProcessingParameterEnum( QStringLiteral( "OBJECTS" ), QObject::tr( "Objects to extract" ), options, true, QVariantList() << 0 << 1 << 2 << 3 ) );
125 : 0 : addOutput( new QgsProcessingOutputNumber( QStringLiteral( "SYMBOLS" ), QObject::tr( "Symbol count" ) ) );
126 : 0 : addOutput( new QgsProcessingOutputNumber( QStringLiteral( "COLORRAMPS" ), QObject::tr( "Color ramp count" ) ) );
127 : 0 : addOutput( new QgsProcessingOutputNumber( QStringLiteral( "TEXTFORMATS" ), QObject::tr( "Text format count" ) ) );
128 : 0 : addOutput( new QgsProcessingOutputNumber( QStringLiteral( "LABELSETTINGS" ), QObject::tr( "Label settings count" ) ) );
129 : 0 : }
130 : :
131 : 0 : QString QgsStyleFromProjectAlgorithm::name() const
132 : : {
133 : 0 : return QStringLiteral( "stylefromproject" );
134 : : }
135 : :
136 : 0 : QString QgsStyleFromProjectAlgorithm::displayName() const
137 : : {
138 : 0 : return QObject::tr( "Create style database from project" );
139 : : }
140 : :
141 : 0 : QStringList QgsStyleFromProjectAlgorithm::tags() const
142 : : {
143 : 0 : return QObject::tr( "symbols,color,ramps,colors,formats,labels,text,fonts" ).split( ',' );
144 : 0 : }
145 : :
146 : 0 : QString QgsStyleFromProjectAlgorithm::group() const
147 : : {
148 : 0 : return QObject::tr( "Cartography" );
149 : : }
150 : :
151 : 0 : QString QgsStyleFromProjectAlgorithm::groupId() const
152 : : {
153 : 0 : return QStringLiteral( "cartography" );
154 : : }
155 : :
156 : 0 : QString QgsStyleFromProjectAlgorithm::shortHelpString() const
157 : : {
158 : 0 : return QObject::tr( "This algorithm extracts all style objects (including symbols, color ramps, text formats and label settings) from a QGIS project.\n\n"
159 : : "The extracted symbols are saved to a QGIS style database (XML format), which can be managed and imported via the Style Manager dialog." );
160 : : }
161 : :
162 : 0 : QString QgsStyleFromProjectAlgorithm::shortDescription() const
163 : : {
164 : 0 : return QObject::tr( "Creates a style database by extracting all symbols, color ramps, text formats and label settings from a QGIS project." );
165 : : }
166 : :
167 : 0 : QgsStyleFromProjectAlgorithm *QgsStyleFromProjectAlgorithm::createInstance() const
168 : : {
169 : 0 : return new QgsStyleFromProjectAlgorithm();
170 : 0 : }
171 : :
172 : 0 : bool QgsStyleFromProjectAlgorithm::prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback * )
173 : : {
174 : 0 : mProjectPath = parameterAsFile( parameters, QStringLiteral( "INPUT" ), context );
175 : 0 : if ( mProjectPath.isEmpty() && !context.project() )
176 : 0 : return false;
177 : :
178 : 0 : const QList< int > selectedObjects = parameterAsEnums( parameters, QStringLiteral( "OBJECTS" ), context );
179 : 0 : if ( selectedObjects.contains( 0 ) )
180 : 0 : mObjects << QgsStyle::SymbolEntity;
181 : 0 : if ( selectedObjects.contains( 1 ) )
182 : 0 : mObjects << QgsStyle::ColorrampEntity;
183 : 0 : if ( selectedObjects.contains( 2 ) )
184 : 0 : mObjects << QgsStyle::TextFormatEntity;
185 : 0 : if ( selectedObjects.contains( 3 ) )
186 : 0 : mObjects << QgsStyle::LabelSettingsEntity;
187 : :
188 : 0 : mStyle = std::make_unique< QgsStyle >();
189 : 0 : mStyle->createMemoryDatabase();
190 : :
191 : 0 : if ( mProjectPath.isEmpty() )
192 : : {
193 : : // using current project -- not thread safe, so prepare in the main thread
194 : 0 : QgsSaveToStyleVisitor visitor( mStyle.get(), mObjects );
195 : 0 : context.project()->accept( &visitor );
196 : 0 : }
197 : 0 : return true;
198 : 0 : }
199 : :
200 : 0 : QVariantMap QgsStyleFromProjectAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback * )
201 : : {
202 : 0 : if ( !mProjectPath.isEmpty() )
203 : : {
204 : : // load project from path
205 : 0 : QgsProject p;
206 : 0 : if ( !p.read( mProjectPath, QgsProject::ReadFlag::FlagDontResolveLayers ) )
207 : : {
208 : 0 : throw QgsProcessingException( QObject::tr( "Could not read project %1" ).arg( mProjectPath ) );
209 : : }
210 : :
211 : 0 : QgsSaveToStyleVisitor visitor( mStyle.get(), mObjects );
212 : 0 : p.accept( &visitor );
213 : 0 : }
214 : :
215 : 0 : const QString file = parameterAsString( parameters, QStringLiteral( "OUTPUT" ), context );
216 : 0 : if ( !mStyle->exportXml( file ) )
217 : : {
218 : 0 : throw QgsProcessingException( QObject::tr( "Error saving style database as %1" ).arg( file ) );
219 : : }
220 : :
221 : 0 : QVariantMap results;
222 : 0 : results.insert( QStringLiteral( "OUTPUT" ), file );
223 : 0 : results.insert( QStringLiteral( "SYMBOLS" ), mStyle->symbolCount() );
224 : 0 : results.insert( QStringLiteral( "COLORRAMPS" ), mStyle->colorRampCount() );
225 : 0 : results.insert( QStringLiteral( "TEXTFORMATS" ), mStyle->textFormatCount() );
226 : 0 : results.insert( QStringLiteral( "LABELSETTINGS" ), mStyle->labelSettingsCount() );
227 : 0 : return results;
228 : 0 : }
229 : :
230 : : ///@endcond
231 : :
232 : :
233 : :
234 : :
|