QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgsmaplayerstylemanagerwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmaplayerstylemanagerwidget.cpp
3 ---------------------
4 begin : June 2016
5 copyright : (C) 2016 by Nathan Woodrow
6 email : woodrow dot nathan at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15#include <QAction>
16#include <QVBoxLayout>
17#include <QToolBar>
18#include <QInputDialog>
19#include <QMessageBox>
20#include <QFileDialog>
21
23#include "qgssettings.h"
24#include "qgslogger.h"
25#include "qgsmaplayer.h"
26#include "qgsmapcanvas.h"
31#include "qgsvectorlayer.h"
32#include "qgsrasterlayer.h"
33#include "qgsapplication.h"
34#include "qgsproviderregistry.h"
35
37 : QgsMapLayerConfigWidget( layer, canvas, parent )
38{
39 mModel = new QStandardItemModel( this );
40 mStyleList = new QListView( this );
41 mStyleList->setModel( mModel );
42 mStyleList->setViewMode( QListView::ListMode );
43 mStyleList->setResizeMode( QListView::Adjust );
44
45 QToolBar *toolbar = new QToolBar( this );
46 QAction *addAction = toolbar->addAction( tr( "Add" ) );
47 addAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "symbologyAdd.svg" ) ) );
48 connect( addAction, &QAction::triggered, this, &QgsMapLayerStyleManagerWidget::addStyle );
49 QAction *removeAction = toolbar->addAction( tr( "Remove Current" ) );
50 removeAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "symbologyRemove.svg" ) ) );
51 connect( removeAction, &QAction::triggered, this, &QgsMapLayerStyleManagerWidget::removeStyle );
52 QAction *loadFromFileAction = toolbar->addAction( tr( "Load Style" ) );
53 loadFromFileAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileOpen.svg" ) ) );
54 connect( loadFromFileAction, &QAction::triggered, this, &QgsMapLayerStyleManagerWidget::loadStyle );
55 QAction *saveAsDefaultAction = toolbar->addAction( tr( "Save as Default" ) );
56 connect( saveAsDefaultAction, &QAction::triggered, this, &QgsMapLayerStyleManagerWidget::saveAsDefault );
57 QAction *loadDefaultAction = toolbar->addAction( tr( "Restore Default" ) );
58 connect( loadDefaultAction, &QAction::triggered, this, &QgsMapLayerStyleManagerWidget::loadDefault );
59
60
61 // Save style doesn't work correctly yet so just disable for now.
62// QAction* saveToFileAction = toolbar->addAction( tr( "Save Style" ) );
63// connect( saveToFileAction, SIGNAL( triggered() ), this, SLOT( saveStyle() ) );
64
65 //broken connect - not sure what the purpose of this was?
66// connect( canvas, &QgsMapCanvas::mapCanvasRefreshed, this, SLOT( updateCurrent() ) );
67
68 connect( mStyleList, &QAbstractItemView::clicked, this, &QgsMapLayerStyleManagerWidget::styleClicked );
69
70 setLayout( new QVBoxLayout() );
71 layout()->setContentsMargins( 0, 0, 0, 0 );
72 layout()->addWidget( toolbar );
73 layout()->addWidget( mStyleList );
74
75 connect( mLayer->styleManager(), &QgsMapLayerStyleManager::currentStyleChanged, this, &QgsMapLayerStyleManagerWidget::currentStyleChanged );
76 connect( mLayer->styleManager(), &QgsMapLayerStyleManager::styleAdded, this, &QgsMapLayerStyleManagerWidget::styleAdded );
77 connect( mLayer->styleManager(), &QgsMapLayerStyleManager::styleRemoved, this, &QgsMapLayerStyleManagerWidget::styleRemoved );
78 connect( mLayer->styleManager(), &QgsMapLayerStyleManager::styleRenamed, this, &QgsMapLayerStyleManagerWidget::styleRenamed );
79
80 mModel->clear();
81
82 const QStringList styles = mLayer->styleManager()->styles();
83 for ( const QString &styleName : styles )
84 {
85 QStandardItem *item = new QStandardItem( styleName );
86 item->setData( styleName );
87 mModel->appendRow( item );
88 }
89
90 const QString active = mLayer->styleManager()->currentStyle();
91 currentStyleChanged( active );
92
93 connect( mModel, &QStandardItemModel::itemChanged, this, &QgsMapLayerStyleManagerWidget::renameStyle );
94}
95
96void QgsMapLayerStyleManagerWidget::styleClicked( const QModelIndex &index )
97{
98 if ( !mLayer || !index.isValid() )
99 return;
100
101 const QString name = index.data().toString();
103}
104
105void QgsMapLayerStyleManagerWidget::currentStyleChanged( const QString &name )
106{
107 const QList<QStandardItem *> items = mModel->findItems( name );
108 if ( items.isEmpty() )
109 return;
110
111 QStandardItem *item = items.at( 0 );
112
113 mStyleList->setCurrentIndex( item->index() );
114}
115
116void QgsMapLayerStyleManagerWidget::styleAdded( const QString &name )
117{
118 QgsDebugMsg( QStringLiteral( "Style added" ) );
119 QStandardItem *item = new QStandardItem( name );
120 item->setData( name );
121 mModel->appendRow( item );
122}
123
124void QgsMapLayerStyleManagerWidget::styleRemoved( const QString &name )
125{
126 const QList<QStandardItem *> items = mModel->findItems( name );
127 if ( items.isEmpty() )
128 return;
129
130 QStandardItem *item = items.at( 0 );
131 mModel->removeRow( item->row() );
132}
133
134void QgsMapLayerStyleManagerWidget::styleRenamed( const QString &oldname, const QString &newname )
135{
136 const QList<QStandardItem *> items = mModel->findItems( oldname );
137 if ( items.isEmpty() )
138 return;
139
140 QStandardItem *item = items.at( 0 );
141 item->setText( newname );
142 item->setData( newname );
143}
144
145void QgsMapLayerStyleManagerWidget::addStyle()
146{
147 bool ok;
148 const QString text = QInputDialog::getText( nullptr, tr( "New Style" ),
149 tr( "Style name:" ), QLineEdit::Normal,
150 QStringLiteral( "new style" ), &ok );
151 if ( !ok || text.isEmpty() )
152 return;
153
154 const bool res = mLayer->styleManager()->addStyleFromLayer( text );
155 if ( res ) // make it active!
156 {
158 }
159 else
160 {
161 QgsDebugMsg( "Failed to add style: " + text );
162 }
163}
164
165void QgsMapLayerStyleManagerWidget::removeStyle()
166{
167 const QString current = mLayer->styleManager()->currentStyle();
168 const QList<QStandardItem *> items = mModel->findItems( current );
169 if ( items.isEmpty() )
170 return;
171
172 QStandardItem *item = items.at( 0 );
173 const bool res = mLayer->styleManager()->removeStyle( current );
174 if ( res )
175 {
176 mModel->removeRow( item->row() );
177 }
178 else
179 {
180 QgsDebugMsg( QStringLiteral( "Failed to remove current style" ) );
181 }
182
183}
184
185void QgsMapLayerStyleManagerWidget::renameStyle( QStandardItem *item )
186{
187 const QString oldName = item->data().toString();
188 const QString newName = item->text();
189 item->setData( newName );
190 whileBlocking( this )->mLayer->styleManager()->renameStyle( oldName, newName );
191}
192
193void QgsMapLayerStyleManagerWidget::saveAsDefault()
194{
195 QString errorMsg;
196
197 if ( QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mLayer ) )
198 {
199 if ( layer->dataProvider()->isSaveAndLoadStyleToDatabaseSupported() )
200 {
201 QMessageBox askToUser;
202 askToUser.setWindowTitle( tr( "Save Style" ) );
203 askToUser.setText( tr( "Save default style to: " ) );
204 askToUser.setIcon( QMessageBox::Question );
205 askToUser.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
206 askToUser.addButton( tr( "Local Database" ), QMessageBox::NoRole );
207 askToUser.addButton( tr( "Datasource Database" ), QMessageBox::YesRole );
208
209 switch ( askToUser.exec() )
210 {
211 case 0:
212 return;
213 case 2:
214 {
215 QString errorMessage;
216 if ( QgsProviderRegistry::instance()->styleExists( layer->providerType(), layer->source(), QString(), errorMessage ) )
217 {
218 if ( QMessageBox::question( nullptr, tr( "Save style in database" ),
219 tr( "A matching style already exists in the database for this layer. Do you want to overwrite it?" ),
220 QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No )
221 {
222 return;
223 }
224 }
225 else if ( !errorMessage.isEmpty() )
226 {
227 QMessageBox::warning( nullptr, tr( "Save style in database" ),
228 errorMessage );
229 return;
230 }
231
232 layer->saveStyleToDatabase( QString(), QString(), true, QString(), errorMsg );
233 if ( errorMsg.isNull() )
234 {
235 return;
236 }
237 break;
238 }
239 default:
240 break;
241 }
242 }
243 }
244
245 bool defaultSavedFlag = false;
246 // TODO Once the deprecated `saveDefaultStyle()` method is gone, just
247 // remove the NOWARN_DEPRECATED tags
249 errorMsg = mLayer->saveDefaultStyle( defaultSavedFlag );
251 if ( !defaultSavedFlag )
252 {
253 QMessageBox::warning( this, tr( "Default Style" ), errorMsg );
254 }
255
256}
257
258void QgsMapLayerStyleManagerWidget::loadDefault()
259{
260 QString msg;
261 bool defaultLoadedFlag = false;
262
263 if ( QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mLayer ) )
264 {
265 if ( layer->dataProvider()->isSaveAndLoadStyleToDatabaseSupported() )
266 {
267 QMessageBox askToUser;
268 askToUser.setWindowTitle( tr( "Load Style" ) );
269 askToUser.setText( tr( "Load default style from: " ) );
270 askToUser.setIcon( QMessageBox::Question );
271 askToUser.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
272 askToUser.addButton( tr( "Local Database" ), QMessageBox::NoRole );
273 askToUser.addButton( tr( "Datasource Database" ), QMessageBox::YesRole );
274
275 switch ( askToUser.exec() )
276 {
277 case 0:
278 return;
279 case 2:
280 msg = layer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag );
281 if ( !defaultLoadedFlag )
282 {
283 //something went wrong - let them know why
284 QMessageBox::information( this, tr( "Default Style" ), msg );
285 }
286 if ( msg.compare( tr( "Loaded from Provider" ) ) )
287 {
288 QMessageBox::information( this, tr( "Default Style" ),
289 tr( "No default style was found for this layer" ) );
290 }
291 return;
292 default:
293 break;
294 }
295 }
296 }
297
298 QString myMessage;
299 if ( QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mLayer ) )
300 {
301 myMessage = layer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag, true );
302 }
303 if ( QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( mLayer ) )
304 {
305 myMessage = layer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag );
306 }
307
308// QString myMessage = layer->loadDefaultStyle( defaultLoadedFlag );
309 //reset if the default style was loaded OK only
310
311
312 if ( !defaultLoadedFlag )
313 {
314 //something went wrong - let them know why
315 QMessageBox::information( this, tr( "Default Style" ), myMessage );
316 }
317 else
318 {
319 emit widgetChanged();
320 }
321
322}
323
324void QgsMapLayerStyleManagerWidget::saveStyle()
325{
326
327}
328
329void QgsMapLayerStyleManagerWidget::loadStyle()
330{
331 QgsSettings myQSettings; // where we keep last used filter in persistent state
332 const QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
333
334 const QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load layer properties from style file" ), myLastUsedDir,
335 tr( "QGIS Layer Style File" ) + " (*.qml);;" + tr( "SLD File" ) + " (*.sld)" );
336 if ( myFileName.isNull() )
337 {
338 return;
339 }
340
341 QString myMessage;
342 bool defaultLoadedFlag = false;
343
344 if ( myFileName.endsWith( QLatin1String( ".sld" ), Qt::CaseInsensitive ) )
345 {
346 // load from SLD
347 myMessage = mLayer->loadSldStyle( myFileName, defaultLoadedFlag );
348 }
349 else
350 {
351 myMessage = mLayer->loadNamedStyle( myFileName, defaultLoadedFlag );
352 }
353 //reset if the default style was loaded OK only
354 if ( defaultLoadedFlag )
355 {
356 emit widgetChanged();
357 }
358 else
359 {
360 //let the user know what went wrong
361 QMessageBox::warning( this, tr( "Load Style" ), myMessage );
362 }
363
364 const QFileInfo myFI( myFileName );
365 const QString myPath = myFI.path();
366 myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), myPath );
367
368}
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:90
A panel widget that can be shown in the map style dock.
QgsMapLayerStyleManagerWidget(QgsMapLayer *layer, QgsMapCanvas *canvas, QWidget *parent=nullptr)
Style manager widget to manage the layers styles.
QString currentStyle() const
Returns name of the current style.
bool removeStyle(const QString &name)
Remove a stored style.
QStringList styles() const
Returns list of all defined style names.
bool setCurrentStyle(const QString &name)
Set a different style as the current style - will apply it to the layer.
void styleAdded(const QString &name)
Emitted when a new style has been added.
void styleRenamed(const QString &oldName, const QString &newName)
Emitted when a style has been renamed.
bool addStyleFromLayer(const QString &name)
Add style by cloning the current one.
void currentStyleChanged(const QString &currentName)
Emitted when the current style has been changed.
void styleRemoved(const QString &name)
Emitted when a style has been removed.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
virtual QString loadSldStyle(const QString &uri, bool &resultFlag)
Attempts to style the layer using the formatting from an SLD type file.
virtual QString styleURI() const
Retrieve the style URI for this layer (either as a .qml file on disk or as a record in the users styl...
virtual QString loadNamedStyle(const QString &uri, bool &resultFlag, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories)
Retrieve a named style for this layer if one exists (either as a .qml file on disk or as a record in ...
bool isValid
Definition: qgsmaplayer.h:81
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer's style manager.
virtual QString saveDefaultStyle(bool &resultFlag, StyleCategories categories)
Save the properties of this layer as the default style (either as a .qml file on disk or as a record ...
void widgetChanged()
Emitted when the widget state changes.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Represents a raster layer.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:63
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Represents a vector layer which manages a vector based data sets.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:4093
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:4092
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:3435
#define QgsDebugMsg(str)
Definition: qgslogger.h:38