QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 }
QgsFileWidget::fileChanged
void fileChanged(const QString &path)
Emitted whenever the current file or directory path is changed.
QgsMapLayerLoadStyleDialog::selectedStyleId
QString selectedStyleId()
Returns the ID of the selected database stored style.
Definition: qgsmaplayerloadstyledialog.cpp:231
QgsSettings::setFlagValue
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:424
QgsVectorLayer::dataProvider
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Definition: qgsvectorlayer.cpp:676
QgsMapLayerLoadStyleDialog::QgsMapLayerLoadStyleDialog
QgsMapLayerLoadStyleDialog(QgsMapLayer *layer, QWidget *parent=nullptr)
Constructor for QgsMapLayerLoadStyleDialog, associated with the specified map layer.
Definition: qgsmaplayerloadstyledialog.cpp:29
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
QgsMapLayerType::MeshLayer
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
QgsMapLayerLoadStyleDialog::accept
void accept() override
Definition: qgsmaplayerloadstyledialog.cpp:290
QgsMapLayerType::VectorLayer
@ VectorLayer
Vector layer.
QgsMapLayerLoadStyleDialog::initializeLists
void initializeLists(const QStringList &ids, const QStringList &names, const QStringList &descriptions, int sectionLimit)
Initialize list of database stored styles.
Definition: qgsmaplayerloadstyledialog.cpp:191
qgsgui.h
QgsMapLayerType::AnnotationLayer
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
QgsMapLayerLoadStyleDialog::filePath
QString filePath() const
Returns the full path to the selected layer style source file.
Definition: qgsmaplayerloadstyledialog.cpp:186
QgsFileWidget::GetFile
@ GetFile
Select a single file.
Definition: qgsfilewidget.h:68
QgsVectorDataProvider::isDeleteStyleFromDatabaseSupported
virtual bool isDeleteStyleFromDatabaseSupported() const
It returns false by default.
Definition: qgsvectordataprovider.cpp:749
QgsVectorLayer::deleteStyleFromDatabase
virtual bool deleteStyleFromDatabase(const QString &styleId, QString &msgError)
Deletes a style from the database.
Definition: qgsvectorlayer.cpp:5429
QgsMapLayerStyleCategoriesModel::setCategories
void setCategories(QgsMapLayer::StyleCategories categories)
Reset the model data.
Definition: qgsmaplayerstylecategoriesmodel.cpp:46
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsVectorLayerProperties::StyleType
StyleType
Definition: qgsvectorlayerproperties.h:66
QgsMapLayer::providerType
QString providerType() const
Returns the provider type (provider key) for this layer.
Definition: qgsmaplayer.cpp:1864
QgsMapLayerStyleCategoriesModel
Model for layer style categories.
Definition: qgsmaplayerstylecategoriesmodel.h:35
qgsapplication.h
QgsGui::enableAutoGeometryRestore
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
QgsMapLayerLoadStyleDialog::currentStyleType
QgsVectorLayerProperties::StyleType currentStyleType() const
Returns the selected vector style type, for vector layers only.
Definition: qgsmaplayerloadstyledialog.cpp:169
QgsVectorLayerProperties::DB
@ DB
Definition: qgsvectorlayerproperties.h:70
QgsVectorLayerProperties::Local
@ Local
Definition: qgsvectorlayerproperties.h:71
QgsMapLayerType::GroupLayer
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
QgsSettings::flagValue
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:360
QgsMapLayerType::RasterLayer
@ RasterLayer
Raster layer.
qgsmaplayerstylecategoriesmodel.h
QgsVectorLayerProperties::QML
@ QML
Definition: qgsvectorlayerproperties.h:68
QgsVectorDataProvider::storageType
virtual QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
Definition: qgsvectordataprovider.cpp:46
QgsMapLayerStyleCategoriesModel::categories
QgsMapLayer::StyleCategories categories() const
Returns the categories as defined in the model.
Definition: qgsmaplayerstylecategoriesmodel.cpp:56
qgsmessagebar.h
QgsVectorLayerProperties::SLD
@ SLD
Definition: qgsvectorlayerproperties.h:69
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
qgsmaplayerloadstyledialog.h
QgsMapLayerLoadStyleDialog::styleCategories
QgsMapLayer::StyleCategories styleCategories() const
Returns the list of selected style categories the user has opted to load.
Definition: qgsmaplayerloadstyledialog.cpp:164
QgsVectorLayer::listStylesInDatabase
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.
Definition: qgsvectorlayer.cpp:5419
QgsHelp::openHelp
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
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
qgssettings.h
QgsMapLayerLoadStyleDialog::fileExtension
QString fileExtension() const
Returns the file extension for the selected layer style source file.
Definition: qgsmaplayerloadstyledialog.cpp:181
QgsMapLayerType::VectorTileLayer
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
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
QgsMapLayer::AllStyleCategories
@ AllStyleCategories
Definition: qgsmaplayer.h:178
QgsMapLayerType::PointCloudLayer
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
QgsMapLayerType::PluginLayer
@ PluginLayer
Plugin based layer.
qgsvectorlayerproperties.h
QgsMapLayer::type
QgsMapLayerType type
Definition: qgsmaplayer.h:80