QGIS API Documentation  3.27.0-Master (aef1b1ec20)
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 && type != QgsVectorLayerProperties::StyleType::Local ) );
63  mFileWidget->setVisible( !vl || ( type != QgsVectorLayerProperties::StyleType::DB && type != QgsVectorLayerProperties::StyleType::Local ) );
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  mStyleTypeComboBox->addItem( tr( "Default from local database" ), QgsVectorLayerProperties::Local );
80 
81  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayer ) )
82  {
83  if ( vl->dataProvider()->isSaveAndLoadStyleToDatabaseSupported() )
84  {
85  mStyleTypeComboBox->addItem( tr( "From Database (%1)" ).arg( providerName ), QgsVectorLayerProperties::StyleType::DB );
86  if ( settings.value( QStringLiteral( "style/lastLoadStyleTypeSelection" ) ) == QgsVectorLayerProperties::StyleType::DB )
87  {
88  mStyleTypeComboBox->setCurrentIndex( mStyleTypeComboBox->findData( QgsVectorLayerProperties::StyleType::DB ) );
89  }
90  }
91  }
92 
93  // fill style categories
94  mModel = new QgsMapLayerStyleCategoriesModel( mLayer->type(), this );
95  const QgsMapLayer::StyleCategories lastStyleCategories = settings.flagValue( QStringLiteral( "style/lastStyleCategories" ), QgsMapLayer::AllStyleCategories );
96  mModel->setCategories( lastStyleCategories );
97  mStyleCategoriesListView->setModel( mModel );
98 
99  // load from file setup
100  switch ( mLayer->type() )
101  {
103  mFileWidget->setFilter( tr( "QGIS Layer Style File, SLD File" ) + QStringLiteral( " (*.qml *.sld)" ) );
104  break;
105 
107  mFileWidget->setFilter( tr( "All Styles" ) + QStringLiteral( " (*.qml *.json);;" )
108  + tr( "QGIS Layer Style File" ) + QStringLiteral( " (*.qml);;" )
109  + tr( "MapBox GL Style JSON File" ) + QStringLiteral( " (*.json)" ) );
110  break;
111 
118  break;
119 
120  }
121 
122  mFileWidget->setStorageMode( QgsFileWidget::GetFile );
123  mFileWidget->setDefaultRoot( myLastUsedDir );
124  connect( mFileWidget, &QgsFileWidget::fileChanged, this, [ = ]( const QString & path )
125  {
126  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayer );
127  mStyleCategoriesListView->setEnabled( !vl || currentStyleType() != QgsVectorLayerProperties::SLD );
128  QgsSettings settings;
129  const QFileInfo tmplFileInfo( path );
130  settings.setValue( QStringLiteral( "style/lastStyleDir" ), tmplFileInfo.absolutePath() );
131 
132  updateLoadButtonState();
133  } );
134 
135  // load from DB
136  mLoadButton->setDisabled( true );
137  mDeleteButton->setDisabled( true );
138  mRelatedTable->setEditTriggers( QTableWidget::NoEditTriggers );
139  mRelatedTable->horizontalHeader()->setStretchLastSection( true );
140  mRelatedTable->setSelectionBehavior( QTableWidget::SelectRows );
141  mRelatedTable->verticalHeader()->setVisible( false );
142  mOthersTable->setEditTriggers( QTableWidget::NoEditTriggers );
143  mOthersTable->horizontalHeader()->setStretchLastSection( true );
144  mOthersTable->setSelectionBehavior( QTableWidget::SelectRows );
145  mOthersTable->verticalHeader()->setVisible( false );
146  connect( mRelatedTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged );
147  connect( mOthersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged );
148  connect( mRelatedTable, &QTableWidget::doubleClicked, this, &QDialog::accept );
149  connect( mOthersTable, &QTableWidget::doubleClicked, this, &QDialog::accept );
150  connect( mCancelButton, &QPushButton::clicked, this, &QDialog::reject );
151  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsMapLayerLoadStyleDialog::showHelp );
152  connect( mLoadButton, &QPushButton::clicked, this, &QDialog::accept );
153  connect( mDeleteButton, &QPushButton::clicked, this, &QgsMapLayerLoadStyleDialog::deleteStyleFromDB );
154  connect( this, &QgsMapLayerLoadStyleDialog::rejected, [ = ]
155  {
156  QgsSettings().setValue( QStringLiteral( "style/lastLoadStyleTypeSelection" ), currentStyleType() );
157  } );
158 
159  setTabOrder( mRelatedTable, mOthersTable );
160 
161  mStyleCategoriesListView->adjustSize();
162 }
163 
164 QgsMapLayer::StyleCategories QgsMapLayerLoadStyleDialog::styleCategories() const
165 {
166  return mModel->categories();
167 }
168 
170 {
171  QgsVectorLayerProperties::StyleType type = mStyleTypeComboBox->currentData().value<QgsVectorLayerProperties::StyleType>();
172  if ( type == QgsVectorLayerProperties::QML )
173  {
174  const QFileInfo fi( mFileWidget->filePath() );
175  if ( fi.exists() && fi.suffix().compare( QStringLiteral( "sld" ), Qt::CaseInsensitive ) == 0 )
177  }
178  return type;
179 }
180 
182 {
183  return QFileInfo( mFileWidget->filePath() ).suffix();
184 }
185 
187 {
188  return mFileWidget->filePath();
189 }
190 
191 void QgsMapLayerLoadStyleDialog::initializeLists( const QStringList &ids, const QStringList &names, const QStringList &descriptions, int sectionLimit )
192 {
193  // -1 means no ids
194  mSectionLimit = sectionLimit;
195  const int relatedTableNOfCols = sectionLimit > 0 ? 2 : 1;
196  const int othersTableNOfCols = ( sectionLimit >= 0 && ids.count() - sectionLimit > 0 ) ? 2 : 1;
197  const QString twoColsHeader( QStringLiteral( "Name;Description" ) );
198  const QString oneColsHeader( QStringLiteral( "No styles found in the database" ) );
199  const QString relatedTableHeader = relatedTableNOfCols == 1 ? oneColsHeader : twoColsHeader;
200  const QString othersTableHeader = othersTableNOfCols == 1 ? oneColsHeader : twoColsHeader;
201 
202  mRelatedTable->setColumnCount( relatedTableNOfCols );
203  mOthersTable->setColumnCount( othersTableNOfCols );
204  mRelatedTable->setHorizontalHeaderLabels( relatedTableHeader.split( ';' ) );
205  mOthersTable->setHorizontalHeaderLabels( othersTableHeader.split( ';' ) );
206  mRelatedTable->setRowCount( sectionLimit );
207  mOthersTable->setRowCount( sectionLimit >= 0 ? ( ids.count() - sectionLimit ) : 0 );
208  mRelatedTable->setDisabled( relatedTableNOfCols == 1 );
209  mOthersTable->setDisabled( othersTableNOfCols == 1 );
210 
211  if ( sectionLimit >= 0 )
212  {
213  for ( int i = 0; i < sectionLimit; i++ )
214  {
215  QTableWidgetItem *item = new QTableWidgetItem( names.value( i, QString() ) );
216  item->setData( Qt::UserRole, ids[i] );
217  mRelatedTable->setItem( i, 0, item );
218  mRelatedTable->setItem( i, 1, new QTableWidgetItem( descriptions.value( i, QString() ) ) );
219  }
220  for ( int i = sectionLimit; i < ids.count(); i++ )
221  {
222  const int j = i - sectionLimit;
223  QTableWidgetItem *item = new QTableWidgetItem( names.value( i, QString() ) );
224  item->setData( Qt::UserRole, ids[i] );
225  mOthersTable->setItem( j, 0, item );
226  mOthersTable->setItem( j, 1, new QTableWidgetItem( descriptions.value( i, QString() ) ) );
227  }
228  }
229 }
230 
232 {
233  return mSelectedStyleId;
234 }
235 
236 void QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged()
237 {
238  selectionChanged( mRelatedTable );
239  if ( mRelatedTable->selectionModel()->hasSelection() )
240  {
241  if ( mOthersTable->selectionModel()->hasSelection() )
242  {
243  disconnect( mOthersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged );
244  const QTableWidgetSelectionRange range( 0, 0, mOthersTable->rowCount() - 1, mOthersTable->columnCount() - 1 );
245  mOthersTable->setRangeSelected( range, false );
246  connect( mOthersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged );
247  }
248  }
249 }
250 
251 void QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged()
252 {
253  selectionChanged( mOthersTable );
254  if ( mOthersTable->selectionModel()->hasSelection() )
255  {
256  if ( mRelatedTable->selectionModel()->hasSelection() )
257  {
258  disconnect( mRelatedTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged );
259  const QTableWidgetSelectionRange range( 0, 0, mRelatedTable->rowCount() - 1, mRelatedTable->columnCount() - 1 );
260  mRelatedTable->setRangeSelected( range, false );
261  connect( mRelatedTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged );
262  }
263  }
264 }
265 
266 void QgsMapLayerLoadStyleDialog::selectionChanged( QTableWidget *styleTable )
267 {
268  QTableWidgetItem *item = nullptr;
269  const QList<QTableWidgetItem *> selected = styleTable->selectedItems();
270 
271  if ( !selected.isEmpty() )
272  {
273  item = selected.at( 0 );
274  mSelectedStyleName = item->text();
275  mSelectedStyleId = item->data( Qt::UserRole ).toString();
276  mLoadButton->setEnabled( true );
277  mDeleteButton->setEnabled( true );
278  }
279  else
280  {
281  mSelectedStyleName.clear();
282  mSelectedStyleId.clear();
283  mLoadButton->setEnabled( false );
284  mDeleteButton->setEnabled( false );
285  }
286 
287  updateLoadButtonState();
288 }
289 
291 {
292  QgsSettings settings;
293  settings.setFlagValue( QStringLiteral( "style/lastStyleCategories" ), styleCategories() );
294  settings.setValue( QStringLiteral( "style/lastLoadStyleTypeSelection" ), currentStyleType() );
295  QDialog::accept();
296 }
297 
298 void QgsMapLayerLoadStyleDialog::deleteStyleFromDB()
299 {
300  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( mLayer );
301  if ( !vl )
302  return;
303 
304  QString msgError;
305  const QString opInfo = QObject::tr( "Delete style %1 from %2" ).arg( mSelectedStyleName, mLayer->providerType() );
306 
307  if ( QMessageBox::question( nullptr, QObject::tr( "Delete Style" ),
308  QObject::tr( "Are you sure you want to delete the style %1?" ).arg( mSelectedStyleName ),
309  QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
310  return;
311 
312  vl->deleteStyleFromDatabase( mSelectedStyleId, msgError );
313  if ( !msgError.isNull() )
314  {
315  QgsDebugMsg( opInfo + " failed." );
316  QMessageBox::warning( this, opInfo, tr( "%1: fail. %2" ).arg( opInfo, msgError ) );
317  }
318  else
319  {
320 // QgisApp::instance()->messageBar()->pushMessage( opInfo, tr( "%1: success" ).arg( opInfo ), Qgis::MessageLevel::Info, QgisApp::instance()->messageTimeout() );
321 
322  //Delete all rows from the UI table widgets
323  mRelatedTable->setRowCount( 0 );
324  mOthersTable->setRowCount( 0 );
325 
326  //Fill UI widgets again from DB. Other users might have changed the styles meanwhile.
327  QString errorMsg;
328  QStringList ids, names, descriptions;
329  //get the list of styles in the db
330  const int sectionLimit = vl->listStylesInDatabase( ids, names, descriptions, errorMsg );
331  if ( !errorMsg.isNull() )
332  {
333  QMessageBox::warning( this, tr( "Error occurred while retrieving styles from database" ), errorMsg );
334  }
335  else
336  {
337  initializeLists( ids, names, descriptions, sectionLimit );
338  }
339  }
340 }
341 
342 void QgsMapLayerLoadStyleDialog::updateLoadButtonState()
343 {
345  if ( mLayer->type() == QgsMapLayerType::VectorLayer )
346  {
347  mLoadButton->setEnabled( ( type == QgsVectorLayerProperties::DB
348  && ( mRelatedTable->selectionModel()->hasSelection() || mOthersTable->selectionModel()->hasSelection()
349  ) ) ||
350  ( type != QgsVectorLayerProperties::DB && !mFileWidget->filePath().isEmpty() ) ||
352  }
353  else
354  {
355  mLoadButton->setEnabled( !mFileWidget->filePath().isEmpty() );
356  }
357 }
358 
359 void QgsMapLayerLoadStyleDialog::showHelp()
360 {
361  QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#save-and-share-layer-properties" ) );
362 }
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:361
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:425
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