QGIS API Documentation  3.25.0-Master (10b47c2603)
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 }
SublayerPromptMode
Specifies how to handle layer sources with multiple sublayers.
Definition: qgis.h:629
@ AlwaysAsk
Always ask users to select from available sublayers, if sublayers are present.
@ NeverAskLoadAll
Never ask users to select sublayers, instead automatically load all available sublayers.
@ NeverAskSkip
Never ask users to select sublayers, instead don't load anything.
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
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
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.
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
bool mShowType
Whether to show type in the table.
QList< QgsSublayersDialog::LayerDefinition > LayerDefinitionList
List of layer definitions for the purpose of this dialog.
Q_DECL_DEPRECATED QgsSublayersDialog(ProviderType providerType, const QString &name, QWidget *parent=nullptr, Qt::WindowFlags fl=Qt::WindowFlags(), const QString &dataSourceUri=QString())
Constructor for QgsSublayersDialog.
LayerDefinitionList selection()
Returns list of selected layers.
bool mShowCount
Whether to show number of features in the table.
bool mShowDescription
Whether to show description in the table.
void populateLayerTable(const LayerDefinitionList &list)
Populate the table with layers.
int countColumn() const
Returns column with count or -1.
QString mName
Provider type name.
@ Uncounted
Feature count not yet computed.
@ UnknownCount
Provider returned an unknown feature count.
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:2441
A structure that defines layers for the purpose of this dialog.
QString layerName
Name of the layer (not necessarily unique)
QString type
Extra type depending on the use (e.g. geometry type for vector sublayers)
int layerId
Identifier of the layer (one unique layer id may have multiple types though)