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