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