QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsprocessingmultipleselectiondialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsprocessingmultipleselectiondialog.cpp
3  ------------------------------------
4  Date : February 2019
5  Copyright : (C) 2019 Nyall Dawson
6  Email : nyall dot dawson 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 
17 #include "qgsgui.h"
18 #include "qgssettings.h"
19 #include "qgsfileutils.h"
20 #include "qgsvectorlayer.h"
21 #include "qgsmeshlayer.h"
22 #include "qgsrasterlayer.h"
23 #include "processing/models/qgsprocessingmodelchildparametersource.h"
24 #include <QStandardItemModel>
25 #include <QStandardItem>
26 #include <QPushButton>
27 #include <QLineEdit>
28 #include <QToolButton>
29 #include <QFileDialog>
30 
32 
33 QgsProcessingMultipleSelectionPanelWidget::QgsProcessingMultipleSelectionPanelWidget( const QVariantList &availableOptions,
34  const QVariantList &selectedOptions,
35  QWidget *parent )
36  : QgsPanelWidget( parent )
37  , mValueFormatter( []( const QVariant & v )->QString
38 {
39  if ( v.canConvert< QgsProcessingModelChildParameterSource >() )
40  {
41  return v.value< QgsProcessingModelChildParameterSource >().staticValue().toString();
42  }
43  else
44  {
45  return v.toString();
46  }
47  return QString();
48 } )
49 {
50  setupUi( this );
51 
53 
54  mSelectionList->setSelectionBehavior( QAbstractItemView::SelectRows );
55  mSelectionList->setSelectionMode( QAbstractItemView::ExtendedSelection );
56  mSelectionList->setDragDropMode( QAbstractItemView::InternalMove );
57 
58  mButtonSelectAll = new QPushButton( tr( "Select All" ) );
59  mButtonBox->addButton( mButtonSelectAll, QDialogButtonBox::ActionRole );
60 
61  mButtonClearSelection = new QPushButton( tr( "Clear Selection" ) );
62  mButtonBox->addButton( mButtonClearSelection, QDialogButtonBox::ActionRole );
63 
64  mButtonToggleSelection = new QPushButton( tr( "Toggle Selection" ) );
65  mButtonBox->addButton( mButtonToggleSelection, QDialogButtonBox::ActionRole );
66 
67  connect( mButtonSelectAll, &QPushButton::clicked, this, [ = ] { selectAll( true ); } );
68  connect( mButtonClearSelection, &QPushButton::clicked, this, [ = ] { selectAll( false ); } );
69  connect( mButtonToggleSelection, &QPushButton::clicked, this, &QgsProcessingMultipleSelectionPanelWidget::toggleSelection );
70 
71  connect( mButtonBox, &QDialogButtonBox::accepted, this, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked );
72  populateList( availableOptions, selectedOptions );
73 
74  connect( mModel, &QStandardItemModel::itemChanged, this, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged );
75 }
76 
77 void QgsProcessingMultipleSelectionPanelWidget::setValueFormatter( const std::function<QString( const QVariant & )> &formatter )
78 {
79  mValueFormatter = formatter;
80  // update item text using new formatter
81  for ( int i = 0; i < mModel->rowCount(); ++i )
82  {
83  mModel->item( i )->setText( mValueFormatter( mModel->item( i )->data( Qt::UserRole ) ) );
84  }
85 }
86 
87 QVariantList QgsProcessingMultipleSelectionPanelWidget::selectedOptions() const
88 {
89  QVariantList options;
90  options.reserve( mModel->rowCount() );
91  for ( int i = 0; i < mModel->rowCount(); ++i )
92  {
93  if ( mModel->item( i )->checkState() == Qt::Checked )
94  options << mModel->item( i )->data( Qt::UserRole );
95  }
96  return options;
97 }
98 
99 
100 void QgsProcessingMultipleSelectionPanelWidget::selectAll( const bool checked )
101 {
102  const QList<QStandardItem *> items = currentItems();
103  for ( QStandardItem *item : items )
104  {
105  item->setCheckState( checked ? Qt::Checked : Qt::Unchecked );
106  }
107 }
108 
109 void QgsProcessingMultipleSelectionPanelWidget::toggleSelection()
110 {
111  const QList<QStandardItem *> items = currentItems();
112  for ( QStandardItem *item : items )
113  {
114  item->setCheckState( item->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked );
115  }
116 }
117 
118 QList<QStandardItem *> QgsProcessingMultipleSelectionPanelWidget::currentItems()
119 {
120  QList<QStandardItem *> items;
121  const QModelIndexList selection = mSelectionList->selectionModel()->selectedIndexes();
122  if ( selection.size() > 1 )
123  {
124  items.reserve( selection.size() );
125  for ( const QModelIndex &index : selection )
126  {
127  items << mModel->itemFromIndex( index );
128  }
129  }
130  else
131  {
132  items.reserve( mModel->rowCount() );
133  for ( int i = 0; i < mModel->rowCount(); ++i )
134  {
135  items << mModel->item( i );
136  }
137  }
138  return items;
139 }
140 
141 void QgsProcessingMultipleSelectionPanelWidget::populateList( const QVariantList &availableOptions, const QVariantList &selectedOptions )
142 {
143  mModel = new QStandardItemModel( this );
144 
145  QVariantList remainingOptions = availableOptions;
146 
147  // we add selected options first, keeping the existing order of options
148  for ( const QVariant &option : selectedOptions )
149  {
150 // if isinstance(t, QgsProcessingModelChildParameterSource):
151 // item = QStandardItem(t.staticValue())
152  // else:
153 
154  addOption( option, mValueFormatter( option ), true );
155  remainingOptions.removeAll( option );
156  }
157 
158  for ( const QVariant &option : qgis::as_const( remainingOptions ) )
159  {
160  addOption( option, mValueFormatter( option ), false );
161  }
162 
163  mSelectionList->setModel( mModel );
164 }
165 
166 
167 void QgsProcessingMultipleSelectionPanelWidget::addOption( const QVariant &value, const QString &title, bool selected, bool updateExistingTitle )
168 {
169  // don't add duplicate options
170  for ( int i = 0; i < mModel->rowCount(); ++i )
171  {
172  if ( mModel->item( i )->data( Qt::UserRole ) == value ||
173  ( mModel->item( i )->data( Qt::UserRole ).canConvert< QgsProcessingModelChildParameterSource >() &&
174  value.canConvert< QgsProcessingModelChildParameterSource >() &&
175  mModel->item( i )->data( Qt::UserRole ).value< QgsProcessingModelChildParameterSource >() ==
176  value.value< QgsProcessingModelChildParameterSource >() )
177  )
178  {
179  if ( updateExistingTitle )
180  mModel->item( i )->setText( title );
181  return;
182  }
183  }
184 
185  std::unique_ptr< QStandardItem > item = qgis::make_unique< QStandardItem >( title );
186  item->setData( value, Qt::UserRole );
187  item->setCheckState( selected ? Qt::Checked : Qt::Unchecked );
188  item->setCheckable( true );
189  item->setDropEnabled( false );
190  mModel->appendRow( item.release() );
191 }
192 
193 //
194 // QgsProcessingMultipleSelectionDialog
195 //
196 
197 
198 
199 QgsProcessingMultipleSelectionDialog::QgsProcessingMultipleSelectionDialog( const QVariantList &availableOptions, const QVariantList &selectedOptions, QWidget *parent, Qt::WindowFlags flags )
200  : QDialog( parent, flags )
201 {
202  setWindowTitle( tr( "Multiple Selection" ) );
203  QVBoxLayout *vLayout = new QVBoxLayout();
204  mWidget = new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
205  vLayout->addWidget( mWidget );
206  mWidget->buttonBox()->addButton( QDialogButtonBox::Cancel );
207  connect( mWidget->buttonBox(), &QDialogButtonBox::accepted, this, &QDialog::accept );
208  connect( mWidget->buttonBox(), &QDialogButtonBox::rejected, this, &QDialog::reject );
209  setLayout( vLayout );
210 }
211 
212 void QgsProcessingMultipleSelectionDialog::setValueFormatter( const std::function<QString( const QVariant & )> &formatter )
213 {
214  mWidget->setValueFormatter( formatter );
215 }
216 
217 QVariantList QgsProcessingMultipleSelectionDialog::selectedOptions() const
218 {
219  return mWidget->selectedOptions();
220 }
221 
222 
223 //
224 // QgsProcessingMultipleInputPanelWidget
225 //
226 
227 QgsProcessingMultipleInputPanelWidget::QgsProcessingMultipleInputPanelWidget( const QgsProcessingParameterMultipleLayers *parameter, const QVariantList &selectedOptions,
228  const QList<QgsProcessingModelChildParameterSource> &modelSources,
229  QgsProcessingModelAlgorithm *model, QWidget *parent )
230  : QgsProcessingMultipleSelectionPanelWidget( QVariantList(), selectedOptions, parent )
231  , mParameter( parameter )
232 {
233  QPushButton *addFileButton = new QPushButton( tr( "Add File(s)…" ) );
234  connect( addFileButton, &QPushButton::clicked, this, &QgsProcessingMultipleInputPanelWidget::addFiles );
235  buttonBox()->addButton( addFileButton, QDialogButtonBox::ActionRole );
236 
237  QPushButton *addDirButton = new QPushButton( tr( "Add Directory…" ) );
238  connect( addDirButton, &QPushButton::clicked, this, &QgsProcessingMultipleInputPanelWidget::addDirectory );
239  buttonBox()->addButton( addDirButton, QDialogButtonBox::ActionRole );
240 
241  for ( const QgsProcessingModelChildParameterSource &source : modelSources )
242  {
243  addOption( QVariant::fromValue( source ), source.friendlyIdentifier( model ), false, true );
244  }
245 }
246 
247 void QgsProcessingMultipleInputPanelWidget::setProject( QgsProject *project )
248 {
249  if ( mParameter->layerType() != QgsProcessing::TypeFile )
250  populateFromProject( project );
251 }
252 
253 void QgsProcessingMultipleInputPanelWidget::addFiles()
254 {
255  QgsSettings settings;
256  QString path = settings.value( QStringLiteral( "/Processing/LastInputPath" ), QDir::homePath() ).toString();
257 
258  QString filter;
259  if ( const QgsFileFilterGenerator *generator = dynamic_cast< const QgsFileFilterGenerator * >( mParameter ) )
260  filter = generator->createFileFilter();
261  else
262  filter = QObject::tr( "All files (*.*)" );
263 
264  const QStringList filenames = QFileDialog::getOpenFileNames( this, tr( "Select File(s)" ), path, filter );
265  if ( filenames.empty() )
266  return;
267 
268  settings.setValue( QStringLiteral( "/Processing/LastInputPath" ), QFileInfo( filenames.at( 0 ) ).path() );
269 
270  for ( const QString &file : filenames )
271  {
272  addOption( file, file, true );
273  }
274 
275  emit selectionChanged();
276 }
277 
278 void QgsProcessingMultipleInputPanelWidget::addDirectory()
279 {
280  QgsSettings settings;
281  QString path = settings.value( QStringLiteral( "/Processing/LastInputPath" ), QDir::homePath() ).toString();
282 
283  const QString dir = QFileDialog::getExistingDirectory( this, tr( "Select Directory" ), path );
284  if ( dir.isEmpty() )
285  return;
286 
287  settings.setValue( QStringLiteral( "/Processing/LastInputPath" ), dir );
288 
289  QStringList nameFilters;
290  if ( const QgsFileFilterGenerator *generator = dynamic_cast< const QgsFileFilterGenerator * >( mParameter ) )
291  {
292  const QStringList extensions = QgsFileUtils::extensionsFromFilter( generator->createFileFilter() );
293  for ( const QString &extension : extensions )
294  {
295  nameFilters << QStringLiteral( "*.%1" ).arg( extension );
296  nameFilters << QStringLiteral( "*.%1" ).arg( extension.toUpper() );
297  nameFilters << QStringLiteral( "*.%1" ).arg( extension.toLower() );
298  }
299  }
300 
301  QDirIterator it( path, nameFilters, QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::Subdirectories );
302  QStringList files;
303  while ( it.hasNext() )
304  {
305  const QString fullPath = it.next();
306  addOption( fullPath, fullPath, true );
307  }
308  emit selectionChanged();
309 }
310 
311 void QgsProcessingMultipleInputPanelWidget::populateFromProject( QgsProject *project )
312 {
313  QgsSettings settings;
314  auto addLayer = [&]( const QgsMapLayer * layer )
315  {
316  const QString authid = layer->crs().authid();
317  QString title;
318  if ( settings.value( QStringLiteral( "Processing/Configuration/SHOW_CRS_DEF" ), true ).toBool() && !authid.isEmpty() )
319  title = QStringLiteral( "%1 [%2]" ).arg( layer->name(), authid );
320  else
321  title = layer->name();
322 
323 
324  QString id = layer->id();
325  for ( int i = 0; i < mModel->rowCount(); ++i )
326  {
327  // try to match project layers to current layers
328  if ( mModel->item( i )->data( Qt::UserRole ) == layer->id() )
329  {
330  id = layer->id();
331  break;
332  }
333  else if ( mModel->item( i )->data( Qt::UserRole ) == layer->source() )
334  {
335  id = layer->source();
336  break;
337  }
338  }
339 
340  addOption( id, title, false, true );
341  };
342 
343  switch ( mParameter->layerType() )
344  {
346  break;
347 
349  {
350  const QList<QgsRasterLayer *> options = QgsProcessingUtils::compatibleRasterLayers( project, false );
351  for ( const QgsRasterLayer *layer : options )
352  {
353  addLayer( layer );
354  }
355  break;
356  }
357 
359  {
360  const QList<QgsMeshLayer *> options = QgsProcessingUtils::compatibleMeshLayers( project, false );
361  for ( const QgsMeshLayer *layer : options )
362  {
363  addLayer( layer );
364  }
365 
366  break;
367  }
368 
371  {
372  const QList<QgsVectorLayer *> options = QgsProcessingUtils::compatibleVectorLayers( project, QList< int >() );
373  for ( const QgsVectorLayer *layer : options )
374  {
375  addLayer( layer );
376  }
377 
378  break;
379  }
380 
382  {
383  const QList<QgsVectorLayer *> vectors = QgsProcessingUtils::compatibleVectorLayers( project, QList< int >() );
384  for ( const QgsVectorLayer *layer : vectors )
385  {
386  addLayer( layer );
387  }
388  const QList<QgsRasterLayer *> rasters = QgsProcessingUtils::compatibleRasterLayers( project );
389  for ( const QgsRasterLayer *layer : rasters )
390  {
391  addLayer( layer );
392  }
393  const QList<QgsMeshLayer *> meshes = QgsProcessingUtils::compatibleMeshLayers( project );
394  for ( const QgsMeshLayer *layer : meshes )
395  {
396  addLayer( layer );
397  }
398 
399  break;
400  }
401 
405  {
406  const QList<QgsVectorLayer *> vectors = QgsProcessingUtils::compatibleVectorLayers( project, QList< int >() << mParameter->layerType() );
407  for ( const QgsVectorLayer *layer : vectors )
408  {
409  addLayer( layer );
410  }
411  break;
412  }
413  }
414 }
415 
416 //
417 // QgsProcessingMultipleInputDialog
418 //
419 
420 QgsProcessingMultipleInputDialog::QgsProcessingMultipleInputDialog( const QgsProcessingParameterMultipleLayers *parameter, const QVariantList &selectedOptions,
421  const QList< QgsProcessingModelChildParameterSource > &modelSources, QgsProcessingModelAlgorithm *model, QWidget *parent, Qt::WindowFlags flags )
422  : QDialog( parent, flags )
423 {
424  setWindowTitle( tr( "Multiple Selection" ) );
425  QVBoxLayout *vLayout = new QVBoxLayout();
426  mWidget = new QgsProcessingMultipleInputPanelWidget( parameter, selectedOptions, modelSources, model );
427  vLayout->addWidget( mWidget );
428  mWidget->buttonBox()->addButton( QDialogButtonBox::Cancel );
429  connect( mWidget->buttonBox(), &QDialogButtonBox::accepted, this, &QDialog::accept );
430  connect( mWidget->buttonBox(), &QDialogButtonBox::rejected, this, &QDialog::reject );
431  setLayout( vLayout );
432 }
433 
434 QVariantList QgsProcessingMultipleInputDialog::selectedOptions() const
435 {
436  return mWidget->selectedOptions();
437 }
438 
439 void QgsProcessingMultipleInputDialog::setProject( QgsProject *project )
440 {
441  mWidget->setProject( project );
442 }
443 
444 
formatter
Definition: qgsbasicnumericformat.cpp:24
QgsProcessingUtils::compatibleMeshLayers
static QList< QgsMeshLayer * > compatibleMeshLayers(QgsProject *project, bool sort=true)
Returns a list of mesh layers from a project which are compatible with the processing framework.
Definition: qgsprocessingutils.cpp:85
QgsFileUtils::extensionsFromFilter
static QStringList extensionsFromFilter(const QString &filter)
Returns a list of the extensions contained within a file filter string.
Definition: qgsfileutils.cpp:40
qgsrasterlayer.h
QgsProcessingUtils::compatibleRasterLayers
static QList< QgsRasterLayer * > compatibleRasterLayers(QgsProject *project, bool sort=true)
Returns a list of raster layers from a project which are compatible with the processing framework.
Definition: qgsprocessingutils.cpp:38
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:174
qgsgui.h
QgsProcessing::TypeVectorPolygon
@ TypeVectorPolygon
Vector polygon layers.
Definition: qgsprocessing.h:50
QgsProcessing::TypeVectorLine
@ TypeVectorLine
Vector line layers.
Definition: qgsprocessing.h:49
QgsSettings
Definition: qgssettings.h:61
QgsProcessing::TypeVectorPoint
@ TypeVectorPoint
Vector point layers.
Definition: qgsprocessing.h:48
QgsProcessingParameterMultipleLayers
Definition: qgsprocessingparameters.h:1756
QgsProject
Definition: qgsproject.h:92
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:133
QgsProcessing::TypeMapLayer
@ TypeMapLayer
Any map layer type (raster or vector or mesh)
Definition: qgsprocessing.h:46
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:29
QgsProcessing::TypeVector
@ TypeVector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:53
QgsFileFilterGenerator
Definition: qgsfilefiltergenerator.h:33
QgsProcessing::TypeMesh
@ TypeMesh
Mesh layers.
Definition: qgsprocessing.h:54
QgsMeshLayer
Definition: qgsmeshlayer.h:94
QgsProcessing::TypeRaster
@ TypeRaster
Raster layers.
Definition: qgsprocessing.h:51
QgsProcessing::TypeVectorAnyGeometry
@ TypeVectorAnyGeometry
Any vector layer with geometry.
Definition: qgsprocessing.h:47
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:289
QgsRasterLayer
Definition: qgsrasterlayer.h:72
qgsfileutils.h
qgsmeshlayer.h
qgsvectorlayer.h
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsProcessingUtils::compatibleVectorLayers
static QList< QgsVectorLayer * > compatibleVectorLayers(QgsProject *project, const QList< int > &sourceTypes=QList< int >(), bool sort=true)
Returns a list of vector layers from a project which are compatible with the processing framework.
Definition: qgsprocessingutils.cpp:62
QgsMapLayer
Definition: qgsmaplayer.h:81
qgssettings.h
QgsProcessing::TypeFile
@ TypeFile
Files (i.e. non map layer sources, such as text files)
Definition: qgsprocessing.h:52
qgsprocessingmultipleselectiondialog.h