QGIS API Documentation 3.31.0-Master (d8a37248f1)
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 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 {
102 case Qgis::LayerType::Vector:
103 mFileWidget->setFilter( tr( "QGIS Layer Style File, SLD File" ) + QStringLiteral( " (*.qml *.sld)" ) );
104 break;
105
106 case Qgis::LayerType::VectorTile:
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
112 case Qgis::LayerType::Raster:
113 case Qgis::LayerType::Mesh:
114 case Qgis::LayerType::Annotation:
115 case Qgis::LayerType::Plugin:
116 case Qgis::LayerType::PointCloud:
117 case Qgis::LayerType::Group:
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
164QgsMapLayer::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
191void 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
236void 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
251void 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
266void 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
298void 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 QgsDebugError( 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
342void QgsMapLayerLoadStyleDialog::updateLoadButtonState()
343{
345 if ( mLayer->type() == Qgis::LayerType::Vector )
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
359void QgsMapLayerLoadStyleDialog::showHelp()
360{
361 QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#save-and-share-layer-properties" ) );
362}
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 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:193
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.
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.
Qgis::LayerType type
Definition: qgsmaplayer.h:80
@ AllStyleCategories
Definition: qgsmaplayer.h:180
This class is a composition of two QSettings instances:
Definition: qgssettings.h:63
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:340
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:404
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.
#define QgsDebugError(str)
Definition: qgslogger.h:38