QGIS API Documentation  3.25.0-Master (dec16ba68b)
qgsmaplayerloadstyledialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaplayerloadstyledialog.cpp
3  ---------------------
4  begin : April 2013
5  copyright : (C) 2013 by Emilio Loi
6  email : loi at faunalia dot it
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 
16 #include <QMessageBox>
17 #include <QVector>
18 
20 #include "qgslogger.h"
21 #include "qgssettings.h"
24 #include "qgsmessagebar.h"
25 #include "qgsapplication.h"
26 #include "qgsgui.h"
27 
28 
30  : QDialog( parent )
31  , mLayer( layer )
32 {
33  setupUi( this );
35  setWindowTitle( tr( "Database Styles Manager" ) );
36 
37  mDeleteButton = mButtonBox->button( QDialogButtonBox::StandardButton::Close );
38  mDeleteButton->setText( tr( "Delete Style" ) );
39  mDeleteButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ) );
40  mLoadButton = mButtonBox->button( QDialogButtonBox::StandardButton::Open );
41  mLoadButton->setText( tr( "Load Style" ) );
42  mCancelButton = mButtonBox->button( QDialogButtonBox::StandardButton::Cancel );
43 
44  QgsSettings settings;
45 
46  QString providerName = mLayer->providerType();
47  if ( providerName == QLatin1String( "ogr" ) )
48  {
49  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayer );
50  providerName = vl->dataProvider()->storageType();
51  if ( providerName == QLatin1String( "GPKG" ) )
52  providerName = QStringLiteral( "GeoPackage" );
53  }
54 
55  const QString myLastUsedDir = settings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
56 
57  // load style type combobox
58  connect( mStyleTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [ = ]( int )
59  {
61  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayer );
62  mFileLabel->setVisible( !vl || type != QgsVectorLayerProperties::StyleType::DB );
63  mFileWidget->setVisible( !vl || type != QgsVectorLayerProperties::StyleType::DB );
64  if ( vl )
65  {
66  mFromDbWidget->setVisible( type == QgsVectorLayerProperties::StyleType::DB );
67  mDeleteButton->setVisible( type == QgsVectorLayerProperties::StyleType::DB && vl->dataProvider()->isDeleteStyleFromDatabaseSupported() );
68  }
69  else
70  {
71  mFromDbWidget->setVisible( false );
72  mDeleteButton->setVisible( false );
73  }
74 
75  mStyleCategoriesListView->setEnabled( !vl || currentStyleType() != QgsVectorLayerProperties::StyleType::SLD );
76  updateLoadButtonState();
77  } );
78  mStyleTypeComboBox->addItem( tr( "From File" ), QgsVectorLayerProperties::QML ); // QML is used as entry, but works for SLD too, see currentStyleType()
79 
80  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayer ) )
81  {
82  if ( vl->dataProvider()->isSaveAndLoadStyleToDatabaseSupported() )
83  {
84  mStyleTypeComboBox->addItem( tr( "From Database (%1)" ).arg( providerName ), QgsVectorLayerProperties::StyleType::DB );
85  if ( settings.value( QStringLiteral( "style/lastLoadStyleTypeSelection" ) ) == QgsVectorLayerProperties::StyleType::DB )
86  {
87  mStyleTypeComboBox->setCurrentIndex( mStyleTypeComboBox->findData( QgsVectorLayerProperties::StyleType::DB ) );
88  }
89  }
90  }
91 
92  // fill style categories
93  mModel = new QgsMapLayerStyleCategoriesModel( mLayer->type(), this );
94  const QgsMapLayer::StyleCategories lastStyleCategories = settings.flagValue( QStringLiteral( "style/lastStyleCategories" ), QgsMapLayer::AllStyleCategories );
95  mModel->setCategories( lastStyleCategories );
96  mStyleCategoriesListView->setModel( mModel );
97 
98  // load from file setup
99  switch ( mLayer->type() )
100  {
102  mFileWidget->setFilter( tr( "QGIS Layer Style File, SLD File" ) + QStringLiteral( " (*.qml *.sld)" ) );
103  break;
104 
106  mFileWidget->setFilter( tr( "All Styles" ) + QStringLiteral( " (*.qml *.json);;" )
107  + tr( "QGIS Layer Style File" ) + QStringLiteral( " (*.qml);;" )
108  + tr( "MapBox GL Style JSON File" ) + QStringLiteral( " (*.json)" ) );
109  break;
110 
117  break;
118 
119  }
120 
121  mFileWidget->setStorageMode( QgsFileWidget::GetFile );
122  mFileWidget->setDefaultRoot( myLastUsedDir );
123  connect( mFileWidget, &QgsFileWidget::fileChanged, this, [ = ]( const QString & path )
124  {
125  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayer );
126  mStyleCategoriesListView->setEnabled( !vl || currentStyleType() != QgsVectorLayerProperties::SLD );
127  QgsSettings settings;
128  const QFileInfo tmplFileInfo( path );
129  settings.setValue( QStringLiteral( "style/lastStyleDir" ), tmplFileInfo.absolutePath() );
130 
131  updateLoadButtonState();
132  } );
133 
134  // load from DB
135  mLoadButton->setDisabled( true );
136  mDeleteButton->setDisabled( true );
137  mRelatedTable->setEditTriggers( QTableWidget::NoEditTriggers );
138  mRelatedTable->horizontalHeader()->setStretchLastSection( true );
139  mRelatedTable->setSelectionBehavior( QTableWidget::SelectRows );
140  mRelatedTable->verticalHeader()->setVisible( false );
141  mOthersTable->setEditTriggers( QTableWidget::NoEditTriggers );
142  mOthersTable->horizontalHeader()->setStretchLastSection( true );
143  mOthersTable->setSelectionBehavior( QTableWidget::SelectRows );
144  mOthersTable->verticalHeader()->setVisible( false );
145  connect( mRelatedTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged );
146  connect( mOthersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged );
147  connect( mRelatedTable, &QTableWidget::doubleClicked, this, &QDialog::accept );
148  connect( mOthersTable, &QTableWidget::doubleClicked, this, &QDialog::accept );
149  connect( mCancelButton, &QPushButton::clicked, this, &QDialog::reject );
150  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsMapLayerLoadStyleDialog::showHelp );
151  connect( mLoadButton, &QPushButton::clicked, this, &QDialog::accept );
152  connect( mDeleteButton, &QPushButton::clicked, this, &QgsMapLayerLoadStyleDialog::deleteStyleFromDB );
153  connect( this, &QgsMapLayerLoadStyleDialog::rejected, [ = ]
154  {
155  QgsSettings().setValue( QStringLiteral( "style/lastLoadStyleTypeSelection" ), currentStyleType() );
156  } );
157 
158  setTabOrder( mRelatedTable, mOthersTable );
159 
160  mStyleCategoriesListView->adjustSize();
161 }
162 
163 QgsMapLayer::StyleCategories QgsMapLayerLoadStyleDialog::styleCategories() const
164 {
165  return mModel->categories();
166 }
167 
169 {
170  QgsVectorLayerProperties::StyleType type = mStyleTypeComboBox->currentData().value<QgsVectorLayerProperties::StyleType>();
171  if ( type == QgsVectorLayerProperties::QML )
172  {
173  const QFileInfo fi( mFileWidget->filePath() );
174  if ( fi.exists() && fi.suffix().compare( QStringLiteral( "sld" ), Qt::CaseInsensitive ) == 0 )
176  }
177  return type;
178 }
179 
181 {
182  return QFileInfo( mFileWidget->filePath() ).suffix();
183 }
184 
186 {
187  return mFileWidget->filePath();
188 }
189 
190 void QgsMapLayerLoadStyleDialog::initializeLists( const QStringList &ids, const QStringList &names, const QStringList &descriptions, int sectionLimit )
191 {
192  // -1 means no ids
193  mSectionLimit = sectionLimit;
194  const int relatedTableNOfCols = sectionLimit > 0 ? 2 : 1;
195  const int othersTableNOfCols = ( sectionLimit >= 0 && ids.count() - sectionLimit > 0 ) ? 2 : 1;
196  const QString twoColsHeader( QStringLiteral( "Name;Description" ) );
197  const QString oneColsHeader( QStringLiteral( "No styles found in the database" ) );
198  const QString relatedTableHeader = relatedTableNOfCols == 1 ? oneColsHeader : twoColsHeader;
199  const QString othersTableHeader = othersTableNOfCols == 1 ? oneColsHeader : twoColsHeader;
200 
201  mRelatedTable->setColumnCount( relatedTableNOfCols );
202  mOthersTable->setColumnCount( othersTableNOfCols );
203  mRelatedTable->setHorizontalHeaderLabels( relatedTableHeader.split( ';' ) );
204  mOthersTable->setHorizontalHeaderLabels( othersTableHeader.split( ';' ) );
205  mRelatedTable->setRowCount( sectionLimit );
206  mOthersTable->setRowCount( sectionLimit >= 0 ? ( ids.count() - sectionLimit ) : 0 );
207  mRelatedTable->setDisabled( relatedTableNOfCols == 1 );
208  mOthersTable->setDisabled( othersTableNOfCols == 1 );
209 
210  if ( sectionLimit >= 0 )
211  {
212  for ( int i = 0; i < sectionLimit; i++ )
213  {
214  QTableWidgetItem *item = new QTableWidgetItem( names.value( i, QString() ) );
215  item->setData( Qt::UserRole, ids[i] );
216  mRelatedTable->setItem( i, 0, item );
217  mRelatedTable->setItem( i, 1, new QTableWidgetItem( descriptions.value( i, QString() ) ) );
218  }
219  for ( int i = sectionLimit; i < ids.count(); i++ )
220  {
221  const int j = i - sectionLimit;
222  QTableWidgetItem *item = new QTableWidgetItem( names.value( i, QString() ) );
223  item->setData( Qt::UserRole, ids[i] );
224  mOthersTable->setItem( j, 0, item );
225  mOthersTable->setItem( j, 1, new QTableWidgetItem( descriptions.value( i, QString() ) ) );
226  }
227  }
228 }
229 
231 {
232  return mSelectedStyleId;
233 }
234 
235 void QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged()
236 {
237  selectionChanged( mRelatedTable );
238  if ( mRelatedTable->selectionModel()->hasSelection() )
239  {
240  if ( mOthersTable->selectionModel()->hasSelection() )
241  {
242  disconnect( mOthersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged );
243  const QTableWidgetSelectionRange range( 0, 0, mOthersTable->rowCount() - 1, mOthersTable->columnCount() - 1 );
244  mOthersTable->setRangeSelected( range, false );
245  connect( mOthersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged );
246  }
247  }
248 }
249 
250 void QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged()
251 {
252  selectionChanged( mOthersTable );
253  if ( mOthersTable->selectionModel()->hasSelection() )
254  {
255  if ( mRelatedTable->selectionModel()->hasSelection() )
256  {
257  disconnect( mRelatedTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged );
258  const QTableWidgetSelectionRange range( 0, 0, mRelatedTable->rowCount() - 1, mRelatedTable->columnCount() - 1 );
259  mRelatedTable->setRangeSelected( range, false );
260  connect( mRelatedTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged );
261  }
262  }
263 }
264 
265 void QgsMapLayerLoadStyleDialog::selectionChanged( QTableWidget *styleTable )
266 {
267  QTableWidgetItem *item = nullptr;
268  const QList<QTableWidgetItem *> selected = styleTable->selectedItems();
269 
270  if ( !selected.isEmpty() )
271  {
272  item = selected.at( 0 );
273  mSelectedStyleName = item->text();
274  mSelectedStyleId = item->data( Qt::UserRole ).toString();
275  mLoadButton->setEnabled( true );
276  mDeleteButton->setEnabled( true );
277  }
278  else
279  {
280  mSelectedStyleName.clear();
281  mSelectedStyleId.clear();
282  mLoadButton->setEnabled( false );
283  mDeleteButton->setEnabled( false );
284  }
285 
286  updateLoadButtonState();
287 }
288 
290 {
291  QgsSettings settings;
292  settings.setFlagValue( QStringLiteral( "style/lastStyleCategories" ), styleCategories() );
293  settings.setValue( QStringLiteral( "style/lastLoadStyleTypeSelection" ), currentStyleType() );
294  QDialog::accept();
295 }
296 
297 void QgsMapLayerLoadStyleDialog::deleteStyleFromDB()
298 {
299  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( mLayer );
300  if ( !vl )
301  return;
302 
303  QString msgError;
304  const QString opInfo = QObject::tr( "Delete style %1 from %2" ).arg( mSelectedStyleName, mLayer->providerType() );
305 
306  if ( QMessageBox::question( nullptr, QObject::tr( "Delete Style" ),
307  QObject::tr( "Are you sure you want to delete the style %1?" ).arg( mSelectedStyleName ),
308  QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
309  return;
310 
311  vl->deleteStyleFromDatabase( mSelectedStyleId, msgError );
312  if ( !msgError.isNull() )
313  {
314  QgsDebugMsg( opInfo + " failed." );
315  QMessageBox::warning( this, opInfo, tr( "%1: fail. %2" ).arg( opInfo, msgError ) );
316  }
317  else
318  {
319 // QgisApp::instance()->messageBar()->pushMessage( opInfo, tr( "%1: success" ).arg( opInfo ), Qgis::MessageLevel::Info, QgisApp::instance()->messageTimeout() );
320 
321  //Delete all rows from the UI table widgets
322  mRelatedTable->setRowCount( 0 );
323  mOthersTable->setRowCount( 0 );
324 
325  //Fill UI widgets again from DB. Other users might have changed the styles meanwhile.
326  QString errorMsg;
327  QStringList ids, names, descriptions;
328  //get the list of styles in the db
329  const int sectionLimit = vl->listStylesInDatabase( ids, names, descriptions, errorMsg );
330  if ( !errorMsg.isNull() )
331  {
332  QMessageBox::warning( this, tr( "Error occurred while retrieving styles from database" ), errorMsg );
333  }
334  else
335  {
336  initializeLists( ids, names, descriptions, sectionLimit );
337  }
338  }
339 }
340 
341 void QgsMapLayerLoadStyleDialog::updateLoadButtonState()
342 {
344  if ( mLayer->type() == QgsMapLayerType::VectorLayer )
345  {
346  mLoadButton->setEnabled( ( type == QgsVectorLayerProperties::DB
347  && ( mRelatedTable->selectionModel()->hasSelection() || mOthersTable->selectionModel()->hasSelection()
348  ) ) ||
349  ( type != QgsVectorLayerProperties::DB && !mFileWidget->filePath().isEmpty() ) );
350  }
351  else
352  {
353  mLoadButton->setEnabled( !mFileWidget->filePath().isEmpty() );
354  }
355 }
356 
357 void QgsMapLayerLoadStyleDialog::showHelp()
358 {
359  QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#save-and-share-layer-properties" ) );
360 }
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
@ GetFile
Select a single file.
Definition: qgsfilewidget.h:68
void fileChanged(const QString &path)
Emitted whenever the current file or directory path is changed.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:180
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
QgsMapLayer::StyleCategories styleCategories() const
Returns the list of selected style categories the user has opted to load.
void initializeLists(const QStringList &ids, const QStringList &names, const QStringList &descriptions, int sectionLimit)
Initialize list of database stored styles.
QString selectedStyleId()
Returns the ID of the selected database stored style.
QgsMapLayerLoadStyleDialog(QgsMapLayer *layer, QWidget *parent=nullptr)
Constructor for QgsMapLayerLoadStyleDialog, associated with the specified map layer.
QString filePath() const
Returns the full path to the selected layer style source file.
QString fileExtension() const
Returns the file extension for the selected layer style source file.
QgsVectorLayerProperties::StyleType currentStyleType() const
Returns the selected vector style type, for vector layers only.
Model for layer style categories.
void setCategories(QgsMapLayer::StyleCategories categories)
Reset the model data.
QgsMapLayer::StyleCategories categories() const
Returns the categories as defined in the model.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsMapLayerType type
Definition: qgsmaplayer.h:80
@ AllStyleCategories
Definition: qgsmaplayer.h:178
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
T flagValue(const QString &key, const T &defaultValue, const Section section=NoSection)
Returns the setting value for a setting based on a flag.
Definition: qgssettings.h:359
void setFlagValue(const QString &key, const T &value, const Section section=NoSection)
Set the value of a setting based on a flag.
Definition: qgssettings.h:423
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.
virtual QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
virtual bool isDeleteStyleFromDatabaseSupported() const
It returns false by default.
Represents a vector layer which manages a vector based data sets.
virtual int listStylesInDatabase(QStringList &ids, QStringList &names, QStringList &descriptions, QString &msgError)
Lists all the style in db split into related to the layer and not related to.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
virtual bool deleteStyleFromDatabase(const QString &styleId, QString &msgError)
Deletes a style from the database.
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38