QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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"
29 #include "qgsvectordataprovider.h"
30 #include "qgsrasterdataprovider.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 
96 void QgsMapLayerStyleManagerWidget::styleClicked( const QModelIndex &index )
97 {
98  if ( !mLayer || !index.isValid() )
99  return;
100 
101  const QString name = index.data().toString();
102  mLayer->styleManager()->setCurrentStyle( name );
103 }
104 
105 void 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 
116 void 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 
124 void 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 
134 void 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 
145 void 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  {
157  mLayer->styleManager()->setCurrentStyle( text );
158  }
159  else
160  {
161  QgsDebugMsg( "Failed to add style: " + text );
162  }
163 }
164 
165 void 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 
185 void 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 
193 void 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 
258 void 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 
324 void QgsMapLayerStyleManagerWidget::saveStyle()
325 {
326 
327 }
328 
329 void 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 }
QgsMapLayerStyleManager::styles
QStringList styles() const
Returns list of all defined style names.
Definition: qgsmaplayerstylemanager.cpp:86
QgsMapLayer::loadSldStyle
virtual QString loadSldStyle(const QString &uri, bool &resultFlag)
Attempts to style the layer using the formatting from an SLD type file.
Definition: qgsmaplayer.cpp:1723
QgsMapLayerStyleManager::styleRenamed
void styleRenamed(const QString &oldName, const QString &newName)
Emitted when a style has been renamed.
qgsmaplayerstylemanager.h
qgsrasterlayer.h
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
qgsmapcanvas.h
QgsMapCanvas
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:89
QgsMapLayer::styleManager
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer's style manager.
Definition: qgsmaplayer.cpp:2092
QgsMapLayerConfigWidget::mLayer
QgsMapLayer * mLayer
Definition: qgsmaplayerconfigwidget.h:182
QgsMapLayerStyleManager::currentStyleChanged
void currentStyleChanged(const QString &currentName)
Emitted when the current style has been changed.
QgsMapLayerStyleManager::removeStyle
bool removeStyle(const QString &name)
Remove a stored style.
Definition: qgsmaplayerstylemanager.cpp:128
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsMapLayer::saveDefaultStyle
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 ...
Definition: qgsmaplayer.cpp:1330
QgsMapLayer::isValid
bool isValid
Definition: qgsmaplayer.h:81
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
qgsmaplayerstylemanagerwidget.h
QgsMapLayer::styleURI
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...
Definition: qgsmaplayer.cpp:1045
QgsMapLayerConfigWidget
A panel widget that can be shown in the map style dock.
Definition: qgsmaplayerconfigwidget.h:115
qgsapplication.h
Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2820
qgsproviderregistry.h
whileBlocking
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:2191
QgsMapLayerStyleManagerWidget::QgsMapLayerStyleManagerWidget
QgsMapLayerStyleManagerWidget(QgsMapLayer *layer, QgsMapCanvas *canvas, QWidget *parent=nullptr)
Style manager widget to manage the layers styles.
Definition: qgsmaplayerstylemanagerwidget.cpp:36
qgsmaplayer.h
QgsMapLayerStyleManager::styleAdded
void styleAdded(const QString &name)
Emitted when a new style has been added.
qgsvectordataprovider.h
QgsPanelWidget::widgetChanged
void widgetChanged()
Emitted when the widget state changes.
QgsMapLayerStyleManager::styleRemoved
void styleRemoved(const QString &name)
Emitted when a style has been removed.
QgsSettings::setValue
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Definition: qgssettings.cpp:279
QgsRasterLayer
Represents a raster layer.
Definition: qgsrasterlayer.h:76
QgsMapLayerStyleManager::currentStyle
QString currentStyle() const
Returns name of the current style.
Definition: qgsmaplayerstylemanager.cpp:164
qgsvectorlayer.h
qgsmaplayerconfigwidget.h
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsMapLayer
Base class for all map layer types. This is the base class for all map layer types (vector,...
Definition: qgsmaplayer.h:72
QgsMapLayerStyleManager::addStyleFromLayer
bool addStyleFromLayer(const QString &name)
Add style by cloning the current one.
Definition: qgsmaplayerstylemanager.cpp:121
qgssettings.h
QgsMapLayerStyleManager::setCurrentStyle
bool setCurrentStyle(const QString &name)
Set a different style as the current style - will apply it to the layer.
Definition: qgsmaplayerstylemanager.cpp:169
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Definition: qgsapplication.cpp:693
qgslogger.h
Q_NOWARN_DEPRECATED_PUSH
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2819
QgsProviderRegistry::instance
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Definition: qgsproviderregistry.cpp:73
QgsMapLayer::loadNamedStyle
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 ...
Definition: qgsmaplayer.cpp:1116
qgsrasterdataprovider.h