QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgssublayersdialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssublayersdialog.cpp - dialog for selecting sublayers
3  ---------------------
4  begin : January 2009
5  copyright : (C) 2009 by Florian El Ahdab
6  email : felahdab at gmail dot com
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 "qgssublayersdialog.h"
17 #include "qgslogger.h"
18 #include "qgssettings.h"
19 #include "qgsgui.h"
20 #include "qgsproviderregistry.h"
21 
22 #include <QTableWidgetItem>
23 #include <QPushButton>
24 
26 class SubLayerItem : public QTreeWidgetItem
27 {
28  public:
29  SubLayerItem( const QStringList &strings, int type = QTreeWidgetItem::Type )
30  : QTreeWidgetItem( strings, type )
31  {}
32 
33  bool operator <( const QTreeWidgetItem &other ) const override
34  {
35  QgsSublayersDialog *d = qobject_cast<QgsSublayersDialog *>( treeWidget()->parent() );
36  const int col = treeWidget()->sortColumn();
37 
38  if ( col == 0 || ( col > 0 && d->countColumn() == col ) )
39  return text( col ).toInt() < other.text( col ).toInt();
40  else
41  return text( col ) < other.text( col );
42  }
43 };
45 
47  const QString &name,
48  QWidget *parent,
49  Qt::WindowFlags fl,
50  const QString &dataSourceUri )
51  : QDialog( parent, fl )
52  , mName( name )
53 {
54  setupUi( this );
56 
57  QString title;
58  switch ( providerType )
59  {
61  title = tr( "Select Vector Layers to Add…" );
62  layersTable->setHeaderLabels( QStringList() << tr( "Layer ID" ) << tr( "Layer name" )
63  << tr( "Number of features" ) << tr( "Geometry type" ) << tr( "Description" ) );
64  mShowCount = true;
65  mShowType = true;
66  mShowDescription = true;
67  break;
69  title = tr( "Select Raster Layers to Add…" );
70  layersTable->setHeaderLabels( QStringList() << tr( "Layer ID" ) << tr( "Layer name" ) );
71  break;
73  title = tr( "Select Mesh Layers to Add…" );
74  layersTable->setHeaderLabels( QStringList() << tr( "Layer ID" ) << tr( "Mesh name" ) );
75  break;
76  default:
77  title = tr( "Select Layers to Add…" );
78  layersTable->setHeaderLabels( QStringList() << tr( "Layer ID" ) << tr( "Layer name" )
79  << tr( "Type" ) );
80  mShowType = true;
81  }
82 
83  const QVariantMap dataSourceUriParsed = QgsProviderRegistry::instance()->decodeUri( name, dataSourceUri );
84  const QString dataSourceFilePath = dataSourceUriParsed.value( QStringLiteral( "path" ) ).toString();
85  const QString filePath = dataSourceFilePath.isEmpty() ? dataSourceUri : dataSourceFilePath;
86  const QString fileName = QFileInfo( filePath ).fileName();
87 
88  setWindowTitle( fileName.isEmpty() ? title : QStringLiteral( "%1 | %2" ).arg( title, fileName ) );
89  mLblFilePath->setText( QDir::toNativeSeparators( QFileInfo( filePath ).canonicalFilePath() ) );
90  mLblFilePath->setVisible( ! fileName.isEmpty() );
91 
92  // add a "Select All" button - would be nicer with an icon
93  connect( mBtnSelectAll, &QAbstractButton::pressed, layersTable, &QTreeView::selectAll );
94  connect( mBtnDeselectAll, &QAbstractButton::pressed, this, &QgsSublayersDialog::mBtnDeselectAll_pressed );
95  connect( layersTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsSublayersDialog::layersTable_selectionChanged );
96 
97  mCbxAddToGroup->setVisible( false );
98 }
99 
101 {
102  QgsSettings settings;
103  settings.setValue( "/Windows/" + mName + "SubLayers/headerColumnCount",
104  layersTable->columnCount() );
105  settings.setValue( "/Windows/" + mName + "SubLayers/headerState",
106  layersTable->header()->saveState() );
107 }
108 
109 static bool _isLayerIdUnique( int layerId, QTreeWidget *layersTable )
110 {
111  int count = 0;
112  for ( int j = 0; j < layersTable->topLevelItemCount(); j++ )
113  {
114  if ( layersTable->topLevelItem( j )->text( 0 ).toInt() == layerId )
115  {
116  count++;
117  }
118  }
119  return count == 1;
120 }
121 
123 {
124  LayerDefinitionList list;
125  for ( int i = 0; i < layersTable->selectedItems().size(); i++ )
126  {
127  QTreeWidgetItem *item = layersTable->selectedItems().at( i );
128 
129  LayerDefinition def;
130  def.layerId = item->text( 0 ).toInt();
131  def.layerName = item->text( 1 );
132  if ( mShowType )
133  {
134  // If there are more sub layers of the same name (virtual for geometry types),
135  // add geometry type
136  if ( !_isLayerIdUnique( def.layerId, layersTable ) )
137  def.type = item->text( mShowCount ? 3 : 2 );
138  }
139 
140  list << def;
141  }
142  return list;
143 }
144 
145 
147 {
148  const auto constList = list;
149  for ( const LayerDefinition &item : constList )
150  {
151  QStringList elements;
152  elements << QString::number( item.layerId ) << item.layerName;
153  if ( mShowCount )
154  elements << ( item.count == static_cast< int >( Qgis::FeatureCountState::Uncounted ) ||
155  item.count == static_cast< int >( Qgis::FeatureCountState::UnknownCount )
156  ? tr( "Unknown" ) : QString::number( item.count ) );
157  if ( mShowType )
158  elements << item.type;
159  if ( mShowDescription )
160  elements << item.description;
161  layersTable->addTopLevelItem( new SubLayerItem( elements ) );
162  }
163 
164  // resize columns
165  const QgsSettings settings;
166  const QByteArray ba = settings.value( "/Windows/" + mName + "SubLayers/headerState" ).toByteArray();
167  const int savedColumnCount = settings.value( "/Windows/" + mName + "SubLayers/headerColumnCount" ).toInt();
168  if ( ! ba.isNull() && savedColumnCount == layersTable->columnCount() )
169  {
170  layersTable->header()->restoreState( ba );
171  }
172  else
173  {
174  for ( int i = 0; i < layersTable->columnCount(); i++ )
175  layersTable->resizeColumnToContents( i );
176  layersTable->setColumnWidth( 1, layersTable->columnWidth( 1 ) + 10 );
177  }
178 }
179 
180 // override exec() instead of using showEvent()
181 // because in some case we don't want the dialog to appear (depending on user settings)
182 // TODO alert the user when dialog is not opened
184 {
185  QgsSettings settings;
186  const Qgis::SublayerPromptMode promptLayers = settings.enumValue( QStringLiteral( "qgis/promptForSublayers" ), Qgis::SublayerPromptMode::AlwaysAsk );
187 
188  // make sure three are sublayers to choose
189  if ( layersTable->topLevelItemCount() == 0 )
190  return QDialog::Rejected;
191 
192  layersTable->selectAll();
193 
194  // check promptForSublayers settings - perhaps this should be in QgsDataSource instead?
195  if ( promptLayers == Qgis::SublayerPromptMode::NeverAskSkip )
196  return QDialog::Rejected;
197  else if ( promptLayers == Qgis::SublayerPromptMode::NeverAskLoadAll )
198  return QDialog::Accepted;
199 
200  // if there is only 1 sublayer (probably the main layer), just select that one and return
201  if ( layersTable->topLevelItemCount() == 1 )
202  return QDialog::Accepted;
203 
204  layersTable->sortByColumn( 1, Qt::AscendingOrder );
205  layersTable->setSortingEnabled( true );
206 
207  // if we got here, disable override cursor, open dialog and return result
208  // TODO add override cursor where it is missing (e.g. when opening via "Add Raster")
209  QCursor cursor;
210  const bool overrideCursor = nullptr != QApplication::overrideCursor();
211  if ( overrideCursor )
212  {
213  cursor = QCursor( * QApplication::overrideCursor() );
214  QApplication::restoreOverrideCursor();
215  }
216 
217  // Checkbox about adding sublayers to a group
218  if ( mShowAddToGroupCheckbox )
219  {
220  mCbxAddToGroup->setVisible( true );
221  const bool addToGroup = settings.value( QStringLiteral( "/qgis/openSublayersInGroup" ), false ).toBool();
222  mCbxAddToGroup->setChecked( addToGroup );
223  }
224 
225  const int ret = QDialog::exec();
226  if ( overrideCursor )
227  QApplication::setOverrideCursor( cursor );
228 
229  if ( mShowAddToGroupCheckbox )
230  settings.setValue( QStringLiteral( "/qgis/openSublayersInGroup" ), mCbxAddToGroup->isChecked() );
231  return ret;
232 }
233 
234 void QgsSublayersDialog::layersTable_selectionChanged( const QItemSelection &, const QItemSelection & )
235 {
236  buttonBox->button( QDialogButtonBox::Ok )->setEnabled( layersTable->selectedItems().length() > 0 );
237 }
238 
239 void QgsSublayersDialog::mBtnDeselectAll_pressed()
240 {
241  layersTable->selectionModel()->clear();
242 }
QgsSublayersDialog::mShowType
bool mShowType
Whether to show type in the table.
Definition: qgssublayersdialog.h:180
Qgis::FeatureCountState::UnknownCount
@ UnknownCount
Provider returned an unknown feature count.
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
QgsSublayersDialog::mShowCount
bool mShowCount
Whether to show number of features in the table.
Definition: qgssublayersdialog.h:178
qgsgui.h
Qgis::SublayerPromptMode::AlwaysAsk
@ AlwaysAsk
Always ask users to select from available sublayers, if sublayers are present.
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
Qgis::SublayerPromptMode
SublayerPromptMode
Specifies how to handle layer sources with multiple sublayers.
Definition: qgis.h:763
QgsSublayersDialog::exec
int exec() override
Definition: qgssublayersdialog.cpp:183
QgsSublayersDialog::ProviderType
ProviderType
Definition: qgssublayersdialog.h:65
QgsSublayersDialog::LayerDefinition::layerName
QString layerName
Name of the layer (not necessarily unique)
Definition: qgssublayersdialog.h:83
QgsSublayersDialog::populateLayerTable
void populateLayerTable(const LayerDefinitionList &list)
Populate the table with layers.
Definition: qgssublayersdialog.cpp:146
QgsSublayersDialog::mName
QString mName
Provider type name.
Definition: qgssublayersdialog.h:174
QgsSublayersDialog::Ogr
@ Ogr
Definition: qgssublayersdialog.h:67
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
qgsproviderregistry.h
Qgis::SublayerPromptMode::NeverAskLoadAll
@ NeverAskLoadAll
Never ask users to select sublayers, instead automatically load all available sublayers.
QgsSublayersDialog::mShowDescription
bool mShowDescription
Whether to show description in the table.
Definition: qgssublayersdialog.h:182
QgsSublayersDialog::countColumn
int countColumn() const
Returns column with count or -1.
Definition: qgssublayersdialog.h:160
QgsProviderRegistry::decodeUri
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
Definition: qgsproviderregistry.cpp:555
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
QgsSublayersDialog::selection
LayerDefinitionList selection()
Returns list of selected layers.
Definition: qgssublayersdialog.cpp:122
QgsSublayersDialog::Mdal
@ Mdal
Definition: qgssublayersdialog.h:70
QgsSublayersDialog
Definition: qgssublayersdialog.h:31
operator<
bool operator<(const QVariant &v1, const QVariant &v2)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.h:2644
Qgis::SublayerPromptMode::NeverAskSkip
@ NeverAskSkip
Never ask users to select sublayers, instead don't load anything.
QgsSublayersDialog::LayerDefinitionList
QList< QgsSublayersDialog::LayerDefinition > LayerDefinitionList
List of layer definitions for the purpose of this dialog.
Definition: qgssublayersdialog.h:103
qgssublayersdialog.h
qgssettings.h
QgsSublayersDialog::LayerDefinition::type
QString type
Extra type depending on the use (e.g. geometry type for vector sublayers)
Definition: qgssublayersdialog.h:89
QgsSublayersDialog::QgsSublayersDialog
Q_DECL_DEPRECATED QgsSublayersDialog(ProviderType providerType, const QString &name, QWidget *parent=nullptr, Qt::WindowFlags fl=Qt::WindowFlags(), const QString &dataSourceUri=QString())
Constructor for QgsSublayersDialog.
Definition: qgssublayersdialog.cpp:46
QgsSublayersDialog::LayerDefinition::layerId
int layerId
Identifier of the layer (one unique layer id may have multiple types though)
Definition: qgssublayersdialog.h:80
Qgis::FeatureCountState::Uncounted
@ Uncounted
Feature count not yet computed.
QgsSublayersDialog::Gdal
@ Gdal
Definition: qgssublayersdialog.h:68
QgsSettings::enumValue
T enumValue(const QString &key, const T &defaultValue, const Section section=NoSection)
Returns the setting value for a setting based on an enum.
Definition: qgssettings.h:282
qgslogger.h
QgsProviderRegistry::instance
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Definition: qgsproviderregistry.cpp:73
QgsSublayersDialog::~QgsSublayersDialog
~QgsSublayersDialog() override
Definition: qgssublayersdialog.cpp:100
QgsSublayersDialog::LayerDefinition
A structure that defines layers for the purpose of this dialog.
Definition: qgssublayersdialog.h:77