QGIS API Documentation  3.25.0-Master (dec16ba68b)
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  errorMsg = mLayer->saveDefaultStyle( defaultSavedFlag );
247  if ( !defaultSavedFlag )
248  {
249  QMessageBox::warning( this, tr( "Default Style" ), errorMsg );
250  }
251 
252 }
253 
254 void QgsMapLayerStyleManagerWidget::loadDefault()
255 {
256  QString msg;
257  bool defaultLoadedFlag = false;
258 
259  if ( QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mLayer ) )
260  {
261  if ( layer->dataProvider()->isSaveAndLoadStyleToDatabaseSupported() )
262  {
263  QMessageBox askToUser;
264  askToUser.setWindowTitle( tr( "Load Style" ) );
265  askToUser.setText( tr( "Load default style from: " ) );
266  askToUser.setIcon( QMessageBox::Question );
267  askToUser.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
268  askToUser.addButton( tr( "Local Database" ), QMessageBox::NoRole );
269  askToUser.addButton( tr( "Datasource Database" ), QMessageBox::YesRole );
270 
271  switch ( askToUser.exec() )
272  {
273  case 0:
274  return;
275  case 2:
276  msg = layer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag );
277  if ( !defaultLoadedFlag )
278  {
279  //something went wrong - let them know why
280  QMessageBox::information( this, tr( "Default Style" ), msg );
281  }
282  if ( msg.compare( tr( "Loaded from Provider" ) ) )
283  {
284  QMessageBox::information( this, tr( "Default Style" ),
285  tr( "No default style was found for this layer" ) );
286  }
287  return;
288  default:
289  break;
290  }
291  }
292  }
293 
294  QString myMessage;
295  if ( QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mLayer ) )
296  {
297  myMessage = layer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag, true );
298  }
299  if ( QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( mLayer ) )
300  {
301  myMessage = layer->loadNamedStyle( mLayer->styleURI(), defaultLoadedFlag );
302  }
303 
304 // QString myMessage = layer->loadDefaultStyle( defaultLoadedFlag );
305  //reset if the default style was loaded OK only
306 
307 
308  if ( !defaultLoadedFlag )
309  {
310  //something went wrong - let them know why
311  QMessageBox::information( this, tr( "Default Style" ), myMessage );
312  }
313  else
314  {
315  emit widgetChanged();
316  }
317 
318 }
319 
320 void QgsMapLayerStyleManagerWidget::saveStyle()
321 {
322 
323 }
324 
325 void QgsMapLayerStyleManagerWidget::loadStyle()
326 {
327  QgsSettings myQSettings; // where we keep last used filter in persistent state
328  const QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
329 
330  const QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load layer properties from style file" ), myLastUsedDir,
331  tr( "QGIS Layer Style File" ) + " (*.qml);;" + tr( "SLD File" ) + " (*.sld)" );
332  if ( myFileName.isNull() )
333  {
334  return;
335  }
336 
337  QString myMessage;
338  bool defaultLoadedFlag = false;
339 
340  if ( myFileName.endsWith( QLatin1String( ".sld" ), Qt::CaseInsensitive ) )
341  {
342  // load from SLD
343  myMessage = mLayer->loadSldStyle( myFileName, defaultLoadedFlag );
344  }
345  else
346  {
347  myMessage = mLayer->loadNamedStyle( myFileName, defaultLoadedFlag );
348  }
349  //reset if the default style was loaded OK only
350  if ( defaultLoadedFlag )
351  {
352  emit widgetChanged();
353  }
354  else
355  {
356  //let the user know what went wrong
357  QMessageBox::warning( this, tr( "Load Style" ), myMessage );
358  }
359 
360  const QFileInfo myFI( myFileName );
361  const QString myPath = myFI.path();
362  myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), myPath );
363 
364 }
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 saveDefaultStyle(bool &resultFlag)
Save the properties of this layer as the default style (either as a .qml file on disk or as a record ...
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.
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:62
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.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:1929
#define QgsDebugMsg(str)
Definition: qgslogger.h:38