QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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"
22 #include "qgsvectorlayerproperties.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  QString myLastUsedDir = settings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
56 
57  // load style type combobox
58  connect( mStyleTypeComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, [ = ]( int )
59  {
60  QgsVectorLayerProperties::StyleType type = currentStyleType();
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  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 
115  break;
116 
117  }
118 
119  mFileWidget->setStorageMode( QgsFileWidget::GetFile );
120  mFileWidget->setDefaultRoot( myLastUsedDir );
121  connect( mFileWidget, &QgsFileWidget::fileChanged, this, [ = ]( const QString & path )
122  {
123  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( mLayer );
124  mStyleCategoriesListView->setEnabled( !vl || currentStyleType() != QgsVectorLayerProperties::SLD );
125  QgsSettings settings;
126  QFileInfo tmplFileInfo( path );
127  settings.setValue( QStringLiteral( "style/lastStyleDir" ), tmplFileInfo.absolutePath() );
128 
129  updateLoadButtonState();
130  } );
131 
132  // load from DB
133  mLoadButton->setDisabled( true );
134  mDeleteButton->setDisabled( true );
135  mRelatedTable->setEditTriggers( QTableWidget::NoEditTriggers );
136  mRelatedTable->horizontalHeader()->setStretchLastSection( true );
137  mRelatedTable->setSelectionBehavior( QTableWidget::SelectRows );
138  mRelatedTable->verticalHeader()->setVisible( false );
139  mOthersTable->setEditTriggers( QTableWidget::NoEditTriggers );
140  mOthersTable->horizontalHeader()->setStretchLastSection( true );
141  mOthersTable->setSelectionBehavior( QTableWidget::SelectRows );
142  mOthersTable->verticalHeader()->setVisible( false );
143  connect( mRelatedTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged );
144  connect( mOthersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged );
145  connect( mRelatedTable, &QTableWidget::doubleClicked, this, &QDialog::accept );
146  connect( mOthersTable, &QTableWidget::doubleClicked, this, &QDialog::accept );
147  connect( mCancelButton, &QPushButton::clicked, this, &QDialog::reject );
148  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsMapLayerLoadStyleDialog::showHelp );
149  connect( mLoadButton, &QPushButton::clicked, this, &QDialog::accept );
150  connect( mDeleteButton, &QPushButton::clicked, this, &QgsMapLayerLoadStyleDialog::deleteStyleFromDB );
151  connect( this, &QgsMapLayerLoadStyleDialog::rejected, [ = ]
152  {
153  QgsSettings().setValue( QStringLiteral( "style/lastLoadStyleTypeSelection" ), currentStyleType() );
154  } );
155 
156  setTabOrder( mRelatedTable, mOthersTable );
157 
158  mStyleCategoriesListView->adjustSize();
159 }
160 
161 QgsMapLayer::StyleCategories QgsMapLayerLoadStyleDialog::styleCategories() const
162 {
163  return mModel->categories();
164 }
165 
166 QgsVectorLayerProperties::StyleType QgsMapLayerLoadStyleDialog::currentStyleType() const
167 {
168  QgsVectorLayerProperties::StyleType type = mStyleTypeComboBox->currentData().value<QgsVectorLayerProperties::StyleType>();
169  if ( type == QgsVectorLayerProperties::QML )
170  {
171  QFileInfo fi( mFileWidget->filePath() );
172  if ( fi.exists() && fi.suffix().compare( QStringLiteral( "sld" ), Qt::CaseInsensitive ) == 0 )
173  type = QgsVectorLayerProperties::SLD;
174  }
175  return type;
176 }
177 
179 {
180  return QFileInfo( mFileWidget->filePath() ).suffix();
181 }
182 
184 {
185  return mFileWidget->filePath();
186 }
187 
188 void QgsMapLayerLoadStyleDialog::initializeLists( const QStringList &ids, const QStringList &names, const QStringList &descriptions, int sectionLimit )
189 {
190  // -1 means no ids
191  mSectionLimit = sectionLimit;
192  int relatedTableNOfCols = sectionLimit > 0 ? 2 : 1;
193  int othersTableNOfCols = ( sectionLimit >= 0 && ids.count() - sectionLimit > 0 ) ? 2 : 1;
194  QString twoColsHeader( QStringLiteral( "Name;Description" ) );
195  QString oneColsHeader( QStringLiteral( "No styles found in the database" ) );
196  QString relatedTableHeader = relatedTableNOfCols == 1 ? oneColsHeader : twoColsHeader;
197  QString othersTableHeader = othersTableNOfCols == 1 ? oneColsHeader : twoColsHeader;
198 
199  mRelatedTable->setColumnCount( relatedTableNOfCols );
200  mOthersTable->setColumnCount( othersTableNOfCols );
201  mRelatedTable->setHorizontalHeaderLabels( relatedTableHeader.split( ';' ) );
202  mOthersTable->setHorizontalHeaderLabels( othersTableHeader.split( ';' ) );
203  mRelatedTable->setRowCount( sectionLimit );
204  mOthersTable->setRowCount( sectionLimit >= 0 ? ( ids.count() - sectionLimit ) : 0 );
205  mRelatedTable->setDisabled( relatedTableNOfCols == 1 );
206  mOthersTable->setDisabled( othersTableNOfCols == 1 );
207 
208  if ( sectionLimit >= 0 )
209  {
210  for ( int i = 0; i < sectionLimit; i++ )
211  {
212  QTableWidgetItem *item = new QTableWidgetItem( names.value( i, QString() ) );
213  item->setData( Qt::UserRole, ids[i] );
214  mRelatedTable->setItem( i, 0, item );
215  mRelatedTable->setItem( i, 1, new QTableWidgetItem( descriptions.value( i, QString() ) ) );
216  }
217  for ( int i = sectionLimit; i < ids.count(); i++ )
218  {
219  int j = i - sectionLimit;
220  QTableWidgetItem *item = new QTableWidgetItem( names.value( i, QString() ) );
221  item->setData( Qt::UserRole, ids[i] );
222  mOthersTable->setItem( j, 0, item );
223  mOthersTable->setItem( j, 1, new QTableWidgetItem( descriptions.value( i, QString() ) ) );
224  }
225  }
226 }
227 
229 {
230  return mSelectedStyleId;
231 }
232 
233 void QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged()
234 {
235  selectionChanged( mRelatedTable );
236  if ( mRelatedTable->selectionModel()->hasSelection() )
237  {
238  if ( mOthersTable->selectionModel()->hasSelection() )
239  {
240  disconnect( mOthersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged );
241  QTableWidgetSelectionRange range( 0, 0, mOthersTable->rowCount() - 1, mOthersTable->columnCount() - 1 );
242  mOthersTable->setRangeSelected( range, false );
243  connect( mOthersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged );
244  }
245  }
246 }
247 
248 void QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged()
249 {
250  selectionChanged( mOthersTable );
251  if ( mOthersTable->selectionModel()->hasSelection() )
252  {
253  if ( mRelatedTable->selectionModel()->hasSelection() )
254  {
255  disconnect( mRelatedTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged );
256  QTableWidgetSelectionRange range( 0, 0, mRelatedTable->rowCount() - 1, mRelatedTable->columnCount() - 1 );
257  mRelatedTable->setRangeSelected( range, false );
258  connect( mRelatedTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged );
259  }
260  }
261 }
262 
263 void QgsMapLayerLoadStyleDialog::selectionChanged( QTableWidget *styleTable )
264 {
265  QTableWidgetItem *item = nullptr;
266  QList<QTableWidgetItem *> selected = styleTable->selectedItems();
267 
268  if ( !selected.isEmpty() )
269  {
270  item = selected.at( 0 );
271  mSelectedStyleName = item->text();
272  mSelectedStyleId = item->data( Qt::UserRole ).toString();
273  mLoadButton->setEnabled( true );
274  mDeleteButton->setEnabled( true );
275  }
276  else
277  {
278  mSelectedStyleName.clear();
279  mSelectedStyleId.clear();
280  mLoadButton->setEnabled( false );
281  mDeleteButton->setEnabled( false );
282  }
283 
284  updateLoadButtonState();
285 }
286 
288 {
289  QgsSettings settings;
290  settings.setFlagValue( QStringLiteral( "style/lastStyleCategories" ), styleCategories() );
291  settings.setValue( QStringLiteral( "style/lastLoadStyleTypeSelection" ), currentStyleType() );
292  QDialog::accept();
293 }
294 
295 void QgsMapLayerLoadStyleDialog::deleteStyleFromDB()
296 {
297  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( mLayer );
298  if ( !vl )
299  return;
300 
301  QString msgError;
302  QString opInfo = QObject::tr( "Delete style %1 from %2" ).arg( mSelectedStyleName, mLayer->providerType() );
303 
304  if ( QMessageBox::question( nullptr, QObject::tr( "Delete Style" ),
305  QObject::tr( "Are you sure you want to delete the style %1?" ).arg( mSelectedStyleName ),
306  QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
307  return;
308 
309  vl->deleteStyleFromDatabase( mSelectedStyleId, msgError );
310  if ( !msgError.isNull() )
311  {
312  QgsDebugMsg( opInfo + " failed." );
313  QMessageBox::warning( this, opInfo, tr( "%1: fail. %2" ).arg( opInfo, msgError ) );
314  }
315  else
316  {
317 // QgisApp::instance()->messageBar()->pushMessage( opInfo, tr( "%1: success" ).arg( opInfo ), Qgis::Info, QgisApp::instance()->messageTimeout() );
318 
319  //Delete all rows from the UI table widgets
320  mRelatedTable->setRowCount( 0 );
321  mOthersTable->setRowCount( 0 );
322 
323  //Fill UI widgets again from DB. Other users might have changed the styles meanwhile.
324  QString errorMsg;
325  QStringList ids, names, descriptions;
326  //get the list of styles in the db
327  int sectionLimit = vl->listStylesInDatabase( ids, names, descriptions, errorMsg );
328  if ( !errorMsg.isNull() )
329  {
330  QMessageBox::warning( this, tr( "Error occurred while retrieving styles from database" ), errorMsg );
331  }
332  else
333  {
334  initializeLists( ids, names, descriptions, sectionLimit );
335  }
336  }
337 }
338 
339 void QgsMapLayerLoadStyleDialog::updateLoadButtonState()
340 {
341  QgsVectorLayerProperties::StyleType type = currentStyleType();
342  if ( mLayer->type() == QgsMapLayerType::VectorLayer )
343  {
344  mLoadButton->setEnabled( ( type == QgsVectorLayerProperties::DB
345  && ( mRelatedTable->selectionModel()->hasSelection() || mOthersTable->selectionModel()->hasSelection()
346  ) ) ||
347  ( type != QgsVectorLayerProperties::DB && !mFileWidget->filePath().isEmpty() ) );
348  }
349  else
350  {
351  mLoadButton->setEnabled( !mFileWidget->filePath().isEmpty() );
352  }
353 }
354 
355 void QgsMapLayerLoadStyleDialog::showHelp()
356 {
357  QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#save-and-share-layer-properties" ) );
358 }
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:228
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:626
QgsSettings::setFlagValue
void setFlagValue(const QString &key, const T &value, const Section section=NoSection)
Set the value of a setting based on a flaf.
Definition: qgssettings.h:382
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:174
QgsMapLayerType::MeshLayer
@ MeshLayer
Added in 3.2.
QgsMapLayerLoadStyleDialog::accept
void accept() override
Definition: qgsmaplayerloadstyledialog.cpp:287
QgsMapLayerType::VectorLayer
@ VectorLayer
QgsMapLayerLoadStyleDialog::initializeLists
void initializeLists(const QStringList &ids, const QStringList &names, const QStringList &descriptions, int sectionLimit)
Initialize list of database stored styles.
Definition: qgsmaplayerloadstyledialog.cpp:188
qgsgui.h
QgsMapLayerLoadStyleDialog::filePath
QString filePath() const
Returns the full path to the selected layer style source file.
Definition: qgsmaplayerloadstyledialog.cpp:183
QgsFileWidget::GetFile
@ GetFile
Select a single file.
Definition: qgsfilewidget.h:66
QgsVectorDataProvider::isDeleteStyleFromDatabaseSupported
virtual bool isDeleteStyleFromDatabaseSupported() const
It returns false by default.
Definition: qgsvectordataprovider.cpp:735
QgsVectorLayer::deleteStyleFromDatabase
virtual bool deleteStyleFromDatabase(const QString &styleId, QString &msgError)
Deletes a style from the database.
Definition: qgsvectorlayer.cpp:5197
QgsMapLayerStyleCategoriesModel::setCategories
void setCategories(QgsMapLayer::StyleCategories categories)
Reset the model data.
Definition: qgsmaplayerstylecategoriesmodel.cpp:44
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsMapLayer::providerType
QString providerType() const
Returns the provider type (provider key) for this layer.
Definition: qgsmaplayer.cpp:1617
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:139
QgsMapLayerLoadStyleDialog::currentStyleType
QgsVectorLayerProperties::StyleType currentStyleType() const
Returns the selected vector style type, for vector layers only.
Definition: qgsmaplayerloadstyledialog.cpp:166
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:330
QgsMapLayerType::RasterLayer
@ RasterLayer
qgsmaplayerstylecategoriesmodel.h
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:54
qgsmessagebar.h
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:289
qgsmaplayerloadstyledialog.h
QgsMapLayerLoadStyleDialog::styleCategories
QgsMapLayer::StyleCategories styleCategories() const
Returns the list of selected style categories the user has opted to load.
Definition: qgsmaplayerloadstyledialog.cpp:161
QgsGui::instance
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:63
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:5187
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:387
QgsMapLayer
Base class for all map layer types.
Definition: qgsmaplayer.h:83
qgssettings.h
QgsMapLayerLoadStyleDialog::fileExtension
QString fileExtension() const
Returns the file extension for the selected layer style source file.
Definition: qgsmaplayerloadstyledialog.cpp:178
QgsMapLayerType::VectorTileLayer
@ VectorTileLayer
Added in 3.14.
QgsVectorLayer::dataProvider
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Definition: qgsvectorlayer.cpp:627
qgslogger.h
QgsMapLayer::AllStyleCategories
@ AllStyleCategories
Definition: qgsmaplayer.h:178
QgsMapLayerType::PluginLayer
@ PluginLayer
QgsMapLayer::type
QgsMapLayerType type
Definition: qgsmaplayer.h:90