QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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 "qgshelp.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 const QString myLastUsedDir = settings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
47
48 // load style type combobox
49 connect( mStyleTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [ = ]( int )
50 {
52 mFileLabel->setVisible( type != QgsLayerPropertiesDialog::StyleType::DatasourceDatabase && type != QgsLayerPropertiesDialog::StyleType::UserDatabase );
53 mFileWidget->setVisible( type != QgsLayerPropertiesDialog::StyleType::DatasourceDatabase && type != QgsLayerPropertiesDialog::StyleType::UserDatabase );
54 mFromDbWidget->setVisible( type == QgsLayerPropertiesDialog::StyleType::DatasourceDatabase );
55 mDeleteButton->setVisible( type == QgsLayerPropertiesDialog::StyleType::DatasourceDatabase && mLayer->dataProvider()->styleStorageCapabilities().testFlag( Qgis::ProviderStyleStorageCapability::DeleteFromDatabase ) );
56
57 mStyleCategoriesListView->setEnabled( currentStyleType() != QgsLayerPropertiesDialog::StyleType::SLD );
58 updateLoadButtonState();
59 } );
60 mStyleTypeComboBox->addItem( tr( "From file" ), QgsLayerPropertiesDialog::QML ); // QML is used as entry, but works for SLD too, see currentStyleType()
61 mStyleTypeComboBox->addItem( tr( "Default from local database" ), QgsLayerPropertiesDialog::UserDatabase );
62
64 {
65 mStyleTypeComboBox->addItem( tr( "From datasource database" ), QgsLayerPropertiesDialog::StyleType::DatasourceDatabase );
66 if ( settings.value( QStringLiteral( "style/lastLoadStyleTypeSelection" ) ) == QgsLayerPropertiesDialog::StyleType::DatasourceDatabase )
67 {
68 mStyleTypeComboBox->setCurrentIndex( mStyleTypeComboBox->findData( QgsLayerPropertiesDialog::StyleType::DatasourceDatabase ) );
69 }
70 }
71
72 // fill style categories
73 mModel = new QgsMapLayerStyleCategoriesModel( mLayer->type(), this );
74 const QgsMapLayer::StyleCategories lastStyleCategories = settings.flagValue( QStringLiteral( "style/lastStyleCategories" ), QgsMapLayer::AllStyleCategories );
75 mModel->setCategories( lastStyleCategories );
76 mStyleCategoriesListView->setModel( mModel );
77
78 // load from file setup
79 switch ( mLayer->type() )
80 {
82 mFileWidget->setFilter( tr( "QGIS Layer Style File, SLD File" ) + QStringLiteral( " (*.qml *.sld)" ) );
83 break;
84
86 mFileWidget->setFilter( tr( "All Styles" ) + QStringLiteral( " (*.qml *.json);;" )
87 + tr( "QGIS Layer Style File" ) + QStringLiteral( " (*.qml);;" )
88 + tr( "MapBox GL Style JSON File" ) + QStringLiteral( " (*.json)" ) );
89 break;
90
98 break;
99
100 }
101
102 mFileWidget->setStorageMode( QgsFileWidget::GetFile );
103 mFileWidget->setDefaultRoot( myLastUsedDir );
104 connect( mFileWidget, &QgsFileWidget::fileChanged, this, [ = ]( const QString & path )
105 {
106 mStyleCategoriesListView->setEnabled( currentStyleType() != QgsLayerPropertiesDialog::SLD );
107 QgsSettings settings;
108 const QFileInfo tmplFileInfo( path );
109 settings.setValue( QStringLiteral( "style/lastStyleDir" ), tmplFileInfo.absolutePath() );
110
111 updateLoadButtonState();
112 } );
113
114 // load from DB
115 mLoadButton->setDisabled( true );
116 mDeleteButton->setDisabled( true );
117 mRelatedTable->setEditTriggers( QTableWidget::NoEditTriggers );
118 mRelatedTable->horizontalHeader()->setStretchLastSection( true );
119 mRelatedTable->setSelectionBehavior( QTableWidget::SelectRows );
120 mRelatedTable->verticalHeader()->setVisible( false );
121 mOthersTable->setEditTriggers( QTableWidget::NoEditTriggers );
122 mOthersTable->horizontalHeader()->setStretchLastSection( true );
123 mOthersTable->setSelectionBehavior( QTableWidget::SelectRows );
124 mOthersTable->verticalHeader()->setVisible( false );
125 connect( mRelatedTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged );
126 connect( mOthersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged );
127 connect( mRelatedTable, &QTableWidget::doubleClicked, this, &QDialog::accept );
128 connect( mOthersTable, &QTableWidget::doubleClicked, this, &QDialog::accept );
129 connect( mCancelButton, &QPushButton::clicked, this, &QDialog::reject );
130 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsMapLayerLoadStyleDialog::showHelp );
131 connect( mLoadButton, &QPushButton::clicked, this, &QDialog::accept );
132 connect( mDeleteButton, &QPushButton::clicked, this, &QgsMapLayerLoadStyleDialog::deleteStyleFromDB );
133 connect( this, &QgsMapLayerLoadStyleDialog::rejected, [ = ]
134 {
135 QgsSettings().setValue( QStringLiteral( "style/lastLoadStyleTypeSelection" ), currentStyleType() );
136 } );
137
138 setTabOrder( mRelatedTable, mOthersTable );
139
140 mStyleCategoriesListView->adjustSize();
141
142 // select and deselect all categories
143 connect( mSelectAllButton, &QPushButton::clicked, this, &QgsMapLayerLoadStyleDialog::selectAll );
144 connect( mDeselectAllButton, &QPushButton::clicked, this, &QgsMapLayerLoadStyleDialog::deselectAll );
145 connect( mInvertSelectionButton, &QPushButton::clicked, this, &QgsMapLayerLoadStyleDialog::invertSelection );
146}
147
148void QgsMapLayerLoadStyleDialog::invertSelection()
149{
150 for ( int i = 0; i < mModel->rowCount( QModelIndex() ); i++ )
151 {
152 QModelIndex index = mModel->index( i, 0 );
153 Qt::CheckState currentState = Qt::CheckState( mModel->data( index, Qt::CheckStateRole ).toInt() );
154 Qt::CheckState newState = ( currentState == Qt::Checked ) ? Qt::Unchecked : Qt::Checked;
155 mModel->setData( index, newState, Qt::CheckStateRole );
156 }
157}
158
159void QgsMapLayerLoadStyleDialog::selectAll()
160{
161 for ( int i = 0; i < mModel->rowCount( QModelIndex() ); i++ )
162 {
163 QModelIndex index = mModel->index( i, 0 );
164 mModel->setData( index, Qt::Checked, Qt::CheckStateRole );
165 }
166}
167
168void QgsMapLayerLoadStyleDialog::deselectAll()
169{
170 for ( int i = 0; i < mModel->rowCount( QModelIndex() ); i++ )
171 {
172 QModelIndex index = mModel->index( i, 0 );
173 mModel->setData( index, Qt::Unchecked, Qt::CheckStateRole );
174 }
175}
176
178{
179 return mModel->categories();
180}
181
183{
184 QgsLayerPropertiesDialog::StyleType type = mStyleTypeComboBox->currentData().value<QgsLayerPropertiesDialog::StyleType>();
185 if ( type == QgsLayerPropertiesDialog::QML )
186 {
187 const QFileInfo fi( mFileWidget->filePath() );
188 if ( fi.exists() && fi.suffix().compare( QStringLiteral( "sld" ), Qt::CaseInsensitive ) == 0 )
190 }
191 return type;
192}
193
195{
196 return QFileInfo( mFileWidget->filePath() ).suffix();
197}
198
200{
201 return mFileWidget->filePath();
202}
203
204void QgsMapLayerLoadStyleDialog::initializeLists( const QStringList &ids, const QStringList &names, const QStringList &descriptions, int sectionLimit )
205{
206 // -1 means no ids
207 mSectionLimit = sectionLimit;
208 const int relatedTableNOfCols = sectionLimit > 0 ? 2 : 1;
209 const int othersTableNOfCols = ( sectionLimit >= 0 && ids.count() - sectionLimit > 0 ) ? 2 : 1;
210 const QString twoColsHeader( QStringLiteral( "Name;Description" ) );
211 const QString oneColsHeader( QStringLiteral( "No styles found in the database" ) );
212 const QString relatedTableHeader = relatedTableNOfCols == 1 ? oneColsHeader : twoColsHeader;
213 const QString othersTableHeader = othersTableNOfCols == 1 ? oneColsHeader : twoColsHeader;
214
215 mRelatedTable->setColumnCount( relatedTableNOfCols );
216 mOthersTable->setColumnCount( othersTableNOfCols );
217 mRelatedTable->setHorizontalHeaderLabels( relatedTableHeader.split( ';' ) );
218 mOthersTable->setHorizontalHeaderLabels( othersTableHeader.split( ';' ) );
219 mRelatedTable->setRowCount( sectionLimit );
220 mOthersTable->setRowCount( sectionLimit >= 0 ? ( ids.count() - sectionLimit ) : 0 );
221 mRelatedTable->setDisabled( relatedTableNOfCols == 1 );
222 mOthersTable->setDisabled( othersTableNOfCols == 1 );
223
224 if ( sectionLimit >= 0 )
225 {
226 for ( int i = 0; i < sectionLimit; i++ )
227 {
228 QTableWidgetItem *item = new QTableWidgetItem( names.value( i, QString() ) );
229 item->setData( Qt::UserRole, ids[i] );
230 mRelatedTable->setItem( i, 0, item );
231 mRelatedTable->setItem( i, 1, new QTableWidgetItem( descriptions.value( i, QString() ) ) );
232 }
233 for ( int i = sectionLimit; i < ids.count(); i++ )
234 {
235 const int j = i - sectionLimit;
236 QTableWidgetItem *item = new QTableWidgetItem( names.value( i, QString() ) );
237 item->setData( Qt::UserRole, ids[i] );
238 mOthersTable->setItem( j, 0, item );
239 mOthersTable->setItem( j, 1, new QTableWidgetItem( descriptions.value( i, QString() ) ) );
240 }
241 }
242}
243
245{
246 return mSelectedStyleId;
247}
248
249void QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged()
250{
251 selectionChanged( mRelatedTable );
252 if ( mRelatedTable->selectionModel()->hasSelection() )
253 {
254 if ( mOthersTable->selectionModel()->hasSelection() )
255 {
256 disconnect( mOthersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged );
257 const QTableWidgetSelectionRange range( 0, 0, mOthersTable->rowCount() - 1, mOthersTable->columnCount() - 1 );
258 mOthersTable->setRangeSelected( range, false );
259 connect( mOthersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged );
260 }
261 }
262}
263
264void QgsMapLayerLoadStyleDialog::onOthersTableSelectionChanged()
265{
266 selectionChanged( mOthersTable );
267 if ( mOthersTable->selectionModel()->hasSelection() )
268 {
269 if ( mRelatedTable->selectionModel()->hasSelection() )
270 {
271 disconnect( mRelatedTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged );
272 const QTableWidgetSelectionRange range( 0, 0, mRelatedTable->rowCount() - 1, mRelatedTable->columnCount() - 1 );
273 mRelatedTable->setRangeSelected( range, false );
274 connect( mRelatedTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsMapLayerLoadStyleDialog::onRelatedTableSelectionChanged );
275 }
276 }
277}
278
279void QgsMapLayerLoadStyleDialog::selectionChanged( QTableWidget *styleTable )
280{
281 QTableWidgetItem *item = nullptr;
282 const QList<QTableWidgetItem *> selected = styleTable->selectedItems();
283
284 if ( !selected.isEmpty() )
285 {
286 item = selected.at( 0 );
287 mSelectedStyleName = item->text();
288 mSelectedStyleId = item->data( Qt::UserRole ).toString();
289 mLoadButton->setEnabled( true );
290 mDeleteButton->setEnabled( true );
291 }
292 else
293 {
294 mSelectedStyleName.clear();
295 mSelectedStyleId.clear();
296 mLoadButton->setEnabled( false );
297 mDeleteButton->setEnabled( false );
298 }
299
300 updateLoadButtonState();
301}
302
304{
305 QgsSettings settings;
306 settings.setFlagValue( QStringLiteral( "style/lastStyleCategories" ), styleCategories() );
307 settings.setValue( QStringLiteral( "style/lastLoadStyleTypeSelection" ), currentStyleType() );
308 QDialog::accept();
309}
310
311void QgsMapLayerLoadStyleDialog::deleteStyleFromDB()
312{
313 QString msgError;
314 const QString opInfo = QObject::tr( "Delete style %1 from %2" ).arg( mSelectedStyleName, mLayer->providerType() );
315
316 if ( QMessageBox::question( nullptr, QObject::tr( "Delete Style" ),
317 QObject::tr( "Are you sure you want to delete the style %1?" ).arg( mSelectedStyleName ),
318 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
319 return;
320
321 mLayer->deleteStyleFromDatabase( mSelectedStyleId, msgError );
322 if ( !msgError.isNull() )
323 {
324 QgsDebugError( opInfo + " failed." );
325 QMessageBox::warning( this, opInfo, tr( "%1: fail. %2" ).arg( opInfo, msgError ) );
326 }
327 else
328 {
329// QgisApp::instance()->messageBar()->pushMessage( opInfo, tr( "%1: success" ).arg( opInfo ), Qgis::MessageLevel::Info, QgisApp::instance()->messageTimeout() );
330
331 //Delete all rows from the UI table widgets
332 mRelatedTable->setRowCount( 0 );
333 mOthersTable->setRowCount( 0 );
334
335 //Fill UI widgets again from DB. Other users might have changed the styles meanwhile.
336 QString errorMsg;
337 QStringList ids, names, descriptions;
338 //get the list of styles in the db
339 const int sectionLimit = mLayer->listStylesInDatabase( ids, names, descriptions, errorMsg );
340 if ( !errorMsg.isNull() )
341 {
342 QMessageBox::warning( this, tr( "Error occurred while retrieving styles from database" ), errorMsg );
343 }
344 else
345 {
346 initializeLists( ids, names, descriptions, sectionLimit );
347 }
348 }
349}
350
351void QgsMapLayerLoadStyleDialog::updateLoadButtonState()
352{
354 mLoadButton->setEnabled( ( type == QgsLayerPropertiesDialog::DatasourceDatabase
355 && ( mRelatedTable->selectionModel()->hasSelection() || mOthersTable->selectionModel()->hasSelection()
356 ) ) ||
357 ( type != QgsLayerPropertiesDialog::DatasourceDatabase && !mFileWidget->filePath().isEmpty() ) ||
359}
360
361void QgsMapLayerLoadStyleDialog::showHelp()
362{
363 QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#save-and-share-layer-properties" ) );
364}
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
virtual Qgis::ProviderStyleStorageCapabilities styleStorageCapabilities() const
Returns the style storage capabilities.
@ 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:194
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:39
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.
QgsLayerPropertiesDialog::StyleType currentStyleType() const
Returns the selected style type.
Model for layer style categories.
void setCategories(QgsMapLayer::StyleCategories categories)
Reset the model data.
QVariant data(const QModelIndex &index, int role) const override
bool setData(const QModelIndex &index, const QVariant &value, int role) override
QgsMapLayer::StyleCategories categories() const
Returns the categories as defined in the model.
int rowCount(const QModelIndex &=QModelIndex()) const override
Base class for all map layer types.
Definition: qgsmaplayer.h:75
virtual bool deleteStyleFromDatabase(const QString &styleId, QString &msgError)
Deletes a style from the database.
QString providerType() const
Returns the provider type (provider key) for this layer.
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.
Qgis::LayerType type
Definition: qgsmaplayer.h:82
QFlags< StyleCategory > StyleCategories
Definition: qgsmaplayer.h:188
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
@ AllStyleCategories
Definition: qgsmaplayer.h:184
This class is a composition of two QSettings instances:
Definition: qgssettings.h:64
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:341
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:405
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.
#define QgsDebugError(str)
Definition: qgslogger.h:38