QGIS API Documentation  3.21.0-Master (5b68dc587e)
qgsprocessingmaplayercombobox.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsprocessingmaplayercombobox.cpp
3  -------------------------------
4  begin : June 2019
5  copyright : (C) 2019 by 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 "qgsmaplayercombobox.h"
18 #include "qgsmimedatautils.h"
20 #include "qgssettings.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsfeatureid.h"
23 #include "qgsapplication.h"
24 #include "qgsguiutils.h"
25 #include "qgspanelwidget.h"
29 #include <QHBoxLayout>
30 #include <QVBoxLayout>
31 #include <QToolButton>
32 #include <QCheckBox>
33 #include <QDragEnterEvent>
34 #include <QMenu>
35 #include <QAction>
36 #include <QFileDialog>
37 #include <QUrl>
38 
40 
41 QgsProcessingMapLayerComboBox::QgsProcessingMapLayerComboBox( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
42  : QWidget( parent )
43  , mParameter( parameter->clone() )
44 {
45  QHBoxLayout *layout = new QHBoxLayout();
46  layout->setContentsMargins( 0, 0, 0, 0 );
47  layout->setSpacing( 6 );
48 
49  mCombo = new QgsMapLayerComboBox();
50  layout->addWidget( mCombo );
51  layout->setAlignment( mCombo, Qt::AlignTop );
52 
54  if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() && type == QgsProcessingGui::Standard )
55  {
56  mIterateButton = new QToolButton();
57  mIterateButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconIterate.svg" ) ) );
58  mIterateButton->setToolTip( tr( "Iterate over this layer, creating a separate output for every feature in the layer" ) );
59  mIterateButton->setCheckable( true );
60  mIterateButton->setAutoRaise( true );
61 
62  // button width is 1.25 * icon size, height 1.1 * icon size. But we round to ensure even pixel sizes for equal margins
63  mIterateButton->setFixedSize( 2 * static_cast< int >( 1.25 * iconSize / 2.0 ), 2 * static_cast< int >( iconSize * 1.1 / 2.0 ) );
64  mIterateButton->setIconSize( QSize( iconSize, iconSize ) );
65 
66  layout->addWidget( mIterateButton );
67  layout->setAlignment( mIterateButton, Qt::AlignTop );
68  }
69 
70  if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() )
71  {
72  mSettingsButton = new QToolButton();
73  mSettingsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionOptions.svg" ) ) );
74  mSettingsButton->setToolTip( tr( "Advanced options" ) );
75 
76  // button width is 1.25 * icon size, height 1.1 * icon size. But we round to ensure even pixel sizes for equal margins
77  mSettingsButton->setFixedSize( 2 * static_cast< int >( 1.25 * iconSize / 2.0 ), 2 * static_cast< int >( iconSize * 1.1 / 2.0 ) );
78  mSettingsButton->setIconSize( QSize( iconSize, iconSize ) );
79  mSettingsButton->setAutoRaise( true );
80 
81  connect( mSettingsButton, &QToolButton::clicked, this, &QgsProcessingMapLayerComboBox::showSourceOptions );
82  layout->addWidget( mSettingsButton );
83  layout->setAlignment( mSettingsButton, Qt::AlignTop );
84  }
85 
86  mSelectButton = new QToolButton();
87  mSelectButton->setText( QString( QChar( 0x2026 ) ) );
88  mSelectButton->setToolTip( tr( "Select input" ) );
89  layout->addWidget( mSelectButton );
90  layout->setAlignment( mSelectButton, Qt::AlignTop );
91  if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() )
92  {
93  mFeatureSourceMenu = new QMenu( this );
94  QAction *selectFromFileAction = new QAction( tr( "Select File…" ), mFeatureSourceMenu );
95  connect( selectFromFileAction, &QAction::triggered, this, &QgsProcessingMapLayerComboBox::selectFromFile );
96  mFeatureSourceMenu->addAction( selectFromFileAction );
97  QAction *browseForLayerAction = new QAction( tr( "Browse for Layer…" ), mFeatureSourceMenu );
98  connect( browseForLayerAction, &QAction::triggered, this, &QgsProcessingMapLayerComboBox::browseForLayer );
99  mFeatureSourceMenu->addAction( browseForLayerAction );
100  mSelectButton->setMenu( mFeatureSourceMenu );
101  mSelectButton->setPopupMode( QToolButton::InstantPopup );
102  }
103  else
104  {
105  connect( mSelectButton, &QToolButton::clicked, this, &QgsProcessingMapLayerComboBox::selectFromFile );
106  }
107 
108  QVBoxLayout *vl = new QVBoxLayout();
109  vl->setContentsMargins( 0, 0, 0, 0 );
110  vl->setSpacing( 6 );
111  vl->addLayout( layout );
112 
113  QgsMapLayerProxyModel::Filters filters = QgsMapLayerProxyModel::Filters();
114 
115  if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() && type == QgsProcessingGui::Standard )
116  {
117  mUseSelectionCheckBox = new QCheckBox( tr( "Selected features only" ) );
118  mUseSelectionCheckBox->setChecked( false );
119  mUseSelectionCheckBox->setEnabled( false );
120  vl->addWidget( mUseSelectionCheckBox );
121  }
122 
123  if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() || mParameter->type() == QgsProcessingParameterVectorLayer::typeName() )
124  {
125  QList<int> dataTypes;
126  if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() )
127  dataTypes = static_cast< QgsProcessingParameterFeatureSource *>( mParameter.get() )->dataTypes();
128  else if ( mParameter->type() == QgsProcessingParameterVectorLayer::typeName() )
129  dataTypes = static_cast< QgsProcessingParameterVectorLayer *>( mParameter.get() )->dataTypes();
130 
131  if ( dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.isEmpty() )
133  if ( dataTypes.contains( QgsProcessing::TypeVectorPoint ) )
135  if ( dataTypes.contains( QgsProcessing::TypeVectorLine ) )
137  if ( dataTypes.contains( QgsProcessing::TypeVectorPolygon ) )
139  if ( !filters )
141  }
142  else if ( mParameter->type() == QgsProcessingParameterRasterLayer::typeName() )
143  {
145  }
146  else if ( mParameter->type() == QgsProcessingParameterMeshLayer::typeName() )
147  {
149  }
150  else if ( mParameter->type() == QgsProcessingParameterMapLayer::typeName() )
151  {
152  QList<int> dataTypes;
153  dataTypes = static_cast< QgsProcessingParameterMapLayer *>( mParameter.get() )->dataTypes();
154 
155  if ( dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) )
157  if ( dataTypes.contains( QgsProcessing::TypeVectorPoint ) )
159  if ( dataTypes.contains( QgsProcessing::TypeVectorLine ) )
161  if ( dataTypes.contains( QgsProcessing::TypeVectorPolygon ) )
163  if ( dataTypes.contains( QgsProcessing::TypeRaster ) )
165  if ( dataTypes.contains( QgsProcessing::TypeMesh ) )
167  if ( !filters )
168  filters = QgsMapLayerProxyModel::All;
169  }
170 
171  QgsSettings settings;
172  if ( settings.value( QStringLiteral( "Processing/Configuration/SHOW_CRS_DEF" ), true ).toBool() )
173  mCombo->setShowCrs( true );
174 
175  if ( filters )
176  mCombo->setFilters( filters );
177  mCombo->setExcludedProviders( QStringList() << QStringLiteral( "grass" ) ); // not sure if this is still required...
178 
179  if ( mParameter->flags() & QgsProcessingParameterDefinition::FlagOptional )
180  {
181  mCombo->setAllowEmptyLayer( true );
182  mCombo->setLayer( nullptr );
183  }
184 
185  connect( mCombo, &QgsMapLayerComboBox::layerChanged, this, &QgsProcessingMapLayerComboBox::onLayerChanged );
186  if ( mUseSelectionCheckBox )
187  connect( mUseSelectionCheckBox, &QCheckBox::toggled, this, [ = ]
188  {
189  if ( !mBlockChangedSignal )
190  emit valueChanged();
191  } );
192 
193  setLayout( vl );
194 
195  setAcceptDrops( true );
196 
197  onLayerChanged( mCombo->currentLayer() );
198 }
199 
200 QgsProcessingMapLayerComboBox::~QgsProcessingMapLayerComboBox() = default;
201 
202 void QgsProcessingMapLayerComboBox::setLayer( QgsMapLayer *layer )
203 {
204  if ( layer || mParameter->flags() & QgsProcessingParameterDefinition::FlagOptional )
205  mCombo->setLayer( layer );
206 }
207 
208 QgsMapLayer *QgsProcessingMapLayerComboBox::currentLayer()
209 {
210  return mCombo->currentLayer();
211 }
212 
213 QString QgsProcessingMapLayerComboBox::currentText()
214 {
215  return mCombo->currentText();
216 }
217 
218 void QgsProcessingMapLayerComboBox::setValue( const QVariant &value, QgsProcessingContext &context )
219 {
220  if ( !value.isValid() && mParameter->flags() & QgsProcessingParameterDefinition::FlagOptional )
221  {
222  setLayer( nullptr );
223  return;
224  }
225 
226  QVariant val = value;
227  bool found = false;
228  bool selectedOnly = false;
229  bool iterate = false;
230  if ( val.canConvert<QgsProcessingFeatureSourceDefinition>() )
231  {
232  QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
233  val = fromVar.source;
234  selectedOnly = fromVar.selectedFeaturesOnly;
235  iterate = fromVar.flags & QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature;
236  mFeatureLimit = fromVar.featureLimit;
237  mIsOverridingDefaultGeometryCheck = fromVar.flags & QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck;
238  mGeometryCheck = fromVar.geometryCheck;
239  }
240  else
241  {
242  mFeatureLimit = -1;
243  mIsOverridingDefaultGeometryCheck = false;
245  }
246 
247  if ( val.canConvert<QgsProperty>() )
248  {
249  if ( val.value< QgsProperty >().propertyType() == QgsProperty::StaticProperty )
250  {
251  val = val.value< QgsProperty >().staticValue();
252  }
253  else
254  {
255  val = val.value< QgsProperty >().valueAsString( context.expressionContext(), mParameter->defaultValueForGui().toString() );
256  }
257  }
258 
259  QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( val.value< QObject * >() );
260  if ( !layer && val.type() == QVariant::String )
261  {
262  layer = QgsProcessingUtils::mapLayerFromString( val.toString(), context, false );
263  }
264 
265  if ( layer )
266  {
267  mBlockChangedSignal++;
268  QgsMapLayer *prevLayer = currentLayer();
269  setLayer( layer );
270  found = static_cast< bool >( currentLayer() );
271  bool changed = found && ( currentLayer() != prevLayer );
272  if ( found && mUseSelectionCheckBox )
273  {
274  const bool hasSelection = qobject_cast< QgsVectorLayer * >( layer ) && qobject_cast< QgsVectorLayer * >( layer )->selectedFeatureCount() > 0;
275  changed = changed | ( ( hasSelection && selectedOnly ) != mUseSelectionCheckBox->isChecked() );
276  if ( hasSelection )
277  {
278  mUseSelectionCheckBox->setEnabled( true );
279  mUseSelectionCheckBox->setChecked( selectedOnly );
280  }
281  else
282  {
283  mUseSelectionCheckBox->setChecked( false );
284  mUseSelectionCheckBox->setEnabled( false );
285  }
286 
287  if ( mIterateButton )
288  {
289  mIterateButton->setChecked( iterate );
290  }
291  }
292  mBlockChangedSignal--;
293  if ( changed )
294  emit valueChanged(); // and ensure we only ever raise one
295  }
296 
297  if ( !found )
298  {
299  const QString string = val.toString();
300  if ( mIterateButton )
301  mIterateButton->setChecked( iterate );
302 
303  if ( !string.isEmpty() )
304  {
305  mBlockChangedSignal++;
306  if ( mCombo->findText( string ) < 0 )
307  {
308  QStringList additional = mCombo->additionalItems();
309  additional.append( string );
310  mCombo->setAdditionalItems( additional );
311  }
312  mCombo->setCurrentIndex( mCombo->findText( string ) ); // this may or may not throw a signal, so let's block it..
313  if ( mUseSelectionCheckBox )
314  {
315  mUseSelectionCheckBox->setChecked( false );
316  mUseSelectionCheckBox->setEnabled( false );
317  }
318  mBlockChangedSignal--;
319  if ( !mBlockChangedSignal )
320  emit valueChanged(); // and ensure we only ever raise one
321  }
322  else if ( mParameter->flags() & QgsProcessingParameterDefinition::FlagOptional )
323  {
324  mCombo->setLayer( nullptr );
325  if ( mUseSelectionCheckBox )
326  {
327  mUseSelectionCheckBox->setChecked( false );
328  mUseSelectionCheckBox->setEnabled( false );
329  }
330  }
331  }
332 }
333 
334 QVariant QgsProcessingMapLayerComboBox::value() const
335 {
336  if ( isEditable() && mCombo->currentText() != mCombo->itemText( mCombo->currentIndex() ) )
337  return mCombo->currentText();
338 
339  const bool iterate = mIterateButton && mIterateButton->isChecked();
340  const bool selectedOnly = mUseSelectionCheckBox && mUseSelectionCheckBox->isChecked();
341  if ( QgsMapLayer *layer = mCombo->currentLayer() )
342  {
343  if ( selectedOnly || iterate || mFeatureLimit != -1 || mIsOverridingDefaultGeometryCheck )
344  return QgsProcessingFeatureSourceDefinition( layer->id(), selectedOnly, mFeatureLimit,
345  ( iterate ? QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature : QgsProcessingFeatureSourceDefinition::Flags() )
346  | ( mIsOverridingDefaultGeometryCheck ? QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck : QgsProcessingFeatureSourceDefinition::Flags() ),
347  mGeometryCheck );
348  else
349  return layer->id();
350  }
351  else
352  {
353  if ( !mCombo->currentText().isEmpty() )
354  {
355  if ( selectedOnly || iterate || mFeatureLimit != -1 || mIsOverridingDefaultGeometryCheck )
356  return QgsProcessingFeatureSourceDefinition( mCombo->currentText(), selectedOnly, mFeatureLimit,
357  ( iterate ? QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature : QgsProcessingFeatureSourceDefinition::Flags() )
358  | ( mIsOverridingDefaultGeometryCheck ? QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck : QgsProcessingFeatureSourceDefinition::Flags() ),
359  mGeometryCheck );
360  else
361  return mCombo->currentText();
362  }
363  }
364  return QVariant();
365 }
366 
367 void QgsProcessingMapLayerComboBox::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
368 {
369  mBrowserModel = context.browserModel();
370 }
371 
372 void QgsProcessingMapLayerComboBox::setEditable( bool editable )
373 {
374  mCombo->setEditable( editable );
375 }
376 
377 bool QgsProcessingMapLayerComboBox::isEditable() const
378 {
379  return mCombo->isEditable();
380 }
381 
382 QgsMapLayer *QgsProcessingMapLayerComboBox::compatibleMapLayerFromMimeData( const QMimeData *data, bool &incompatibleLayerSelected ) const
383 {
384  incompatibleLayerSelected = false;
386  for ( const QgsMimeDataUtils::Uri &u : uriList )
387  {
388  // is this uri from the current project?
389  if ( QgsMapLayer *layer = u.mapLayer() )
390  {
391  if ( mCombo->mProxyModel->acceptsLayer( layer ) )
392  return layer;
393  else
394  {
395  incompatibleLayerSelected = true;
396  return nullptr;
397  }
398  }
399  }
400  return nullptr;
401 }
402 
403 
404 QString QgsProcessingMapLayerComboBox::compatibleUriFromMimeData( const QMimeData *data ) const
405 {
407  for ( const QgsMimeDataUtils::Uri &u : uriList )
408  {
409  if ( ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName()
410  || mParameter->type() == QgsProcessingParameterVectorLayer::typeName() )
411  && u.layerType == QLatin1String( "vector" ) )
412  {
413  QList< int > dataTypes = mParameter->type() == QgsProcessingParameterFeatureSource::typeName() ? static_cast< QgsProcessingParameterFeatureSource * >( mParameter.get() )->dataTypes()
414  : ( mParameter->type() == QgsProcessingParameterVectorLayer::typeName() ? static_cast<QgsProcessingParameterVectorLayer *>( mParameter.get() )->dataTypes()
415  : QList< int >() );
416  bool acceptable = false;
417  switch ( QgsWkbTypes::geometryType( u.wkbType ) )
418  {
420  acceptable = true;
421  break;
422 
424  if ( dataTypes.isEmpty() || dataTypes.contains( QgsProcessing::TypeVector ) || dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.contains( QgsProcessing::TypeVectorPoint ) )
425  acceptable = true;
426  break;
427 
429  if ( dataTypes.isEmpty() || dataTypes.contains( QgsProcessing::TypeVector ) || dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.contains( QgsProcessing::TypeVectorLine ) )
430  acceptable = true;
431  break;
432 
434  if ( dataTypes.isEmpty() || dataTypes.contains( QgsProcessing::TypeVector ) || dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.contains( QgsProcessing::TypeVectorPolygon ) )
435  acceptable = true;
436  break;
437 
439  if ( dataTypes.contains( QgsProcessing::TypeVector ) )
440  acceptable = true;
441  break;
442  }
443  if ( acceptable )
444  return u.providerKey != QLatin1String( "ogr" ) ? QgsProcessingUtils::encodeProviderKeyAndUri( u.providerKey, u.uri ) : u.uri;
445  }
446  else if ( mParameter->type() == QgsProcessingParameterRasterLayer::typeName()
447  && u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" ) )
448  return u.uri;
449  else if ( mParameter->type() == QgsProcessingParameterMeshLayer::typeName()
450  && u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" ) )
451  return u.uri;
452  else if ( mParameter->type() == QgsProcessingParameterMapLayer::typeName() )
453  {
454  QList< int > dataTypes = static_cast< QgsProcessingParameterMapLayer * >( mParameter.get() )->dataTypes();
455  if ( dataTypes.isEmpty() || dataTypes.contains( QgsProcessing::TypeMapLayer ) )
456  {
457  return u.uri;
458  }
459 
460  if ( u.layerType == QLatin1String( "vector" ) && u.providerKey == QLatin1String( "ogr" ) )
461  {
462  switch ( QgsWkbTypes::geometryType( u.wkbType ) )
463  {
465  return u.uri;
466 
468  if ( dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.contains( QgsProcessing::TypeVectorPoint ) )
469  return u.uri;
470  break;
471 
473  if ( dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.contains( QgsProcessing::TypeVectorLine ) )
474  return u.uri;
475  break;
476 
478  if ( dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.contains( QgsProcessing::TypeVectorPolygon ) )
479  return u.uri;
480  break;
481 
483  return u.uri;
484  }
485  }
486  else if ( u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" )
487  && dataTypes.contains( QgsProcessing::TypeRaster ) )
488  return u.uri;
489  else if ( u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" )
490  && dataTypes.contains( QgsProcessing::TypeMesh ) )
491  return u.uri;
492  }
493  }
494  if ( !uriList.isEmpty() )
495  return QString();
496 
497  // second chance -- files dragged from file explorer, outside of QGIS
498  QStringList rawPaths;
499  if ( data->hasUrls() )
500  {
501  const QList< QUrl > urls = data->urls();
502  rawPaths.reserve( urls.count() );
503  for ( const QUrl &url : urls )
504  {
505  const QString local = url.toLocalFile();
506  if ( !rawPaths.contains( local ) )
507  rawPaths.append( local );
508  }
509  }
510  if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
511  rawPaths.append( data->text() );
512 
513  for ( const QString &path : std::as_const( rawPaths ) )
514  {
515  QFileInfo file( path );
516  if ( file.isFile() )
517  {
518  // TODO - we should check to see if it's a valid extension for the parameter, but that's non-trivial
519  return path;
520  }
521  }
522 
523  return QString();
524 }
525 
526 void QgsProcessingMapLayerComboBox::dragEnterEvent( QDragEnterEvent *event )
527 {
528  if ( !( event->possibleActions() & Qt::CopyAction ) )
529  return;
530 
531  bool incompatibleLayerSelected = false;
532  QgsMapLayer *layer = compatibleMapLayerFromMimeData( event->mimeData(), incompatibleLayerSelected );
533  const QString uri = compatibleUriFromMimeData( event->mimeData() );
534  if ( layer || ( !incompatibleLayerSelected && !uri.isEmpty() ) )
535  {
536  // dragged an acceptable layer, phew
537  event->setDropAction( Qt::CopyAction );
538  event->accept();
539  mDragActive = true;
540  mCombo->mHighlight = true;
541  update();
542  }
543 }
544 
545 void QgsProcessingMapLayerComboBox::dragLeaveEvent( QDragLeaveEvent *event )
546 {
547  QWidget::dragLeaveEvent( event );
548  if ( mDragActive )
549  {
550  event->accept();
551  mDragActive = false;
552  mCombo->mHighlight = false;
553  update();
554  }
555 }
556 
557 void QgsProcessingMapLayerComboBox::dropEvent( QDropEvent *event )
558 {
559  if ( !( event->possibleActions() & Qt::CopyAction ) )
560  return;
561 
562  bool incompatibleLayerSelected = false;
563  QgsMapLayer *layer = compatibleMapLayerFromMimeData( event->mimeData(), incompatibleLayerSelected );
564  const QString uri = compatibleUriFromMimeData( event->mimeData() );
565  if ( layer || ( !incompatibleLayerSelected && !uri.isEmpty() ) )
566  {
567  // dropped an acceptable layer, phew
568  setFocus( Qt::MouseFocusReason );
569  event->setDropAction( Qt::CopyAction );
570  event->accept();
571  QgsProcessingContext context;
572  setValue( layer ? QVariant::fromValue( layer ) : QVariant::fromValue( uri ), context );
573  }
574  mDragActive = false;
575  mCombo->mHighlight = false;
576  update();
577 }
578 
579 void QgsProcessingMapLayerComboBox::onLayerChanged( QgsMapLayer *layer )
580 {
581  if ( mUseSelectionCheckBox && mParameter->type() == QgsProcessingParameterFeatureSource::typeName() )
582  {
583  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
584  {
585  if ( QgsVectorLayer *prevLayer = qobject_cast< QgsVectorLayer * >( mPrevLayer ) )
586  {
587  disconnect( prevLayer, &QgsVectorLayer::selectionChanged, this, &QgsProcessingMapLayerComboBox::selectionChanged );
588  }
589  if ( vl->selectedFeatureCount() == 0 )
590  mUseSelectionCheckBox->setChecked( false );
591  mUseSelectionCheckBox->setEnabled( vl->selectedFeatureCount() > 0 );
592  connect( vl, &QgsVectorLayer::selectionChanged, this, &QgsProcessingMapLayerComboBox::selectionChanged );
593  }
594  }
595 
596  mPrevLayer = layer;
597  if ( !mBlockChangedSignal )
598  emit valueChanged();
599 }
600 
601 void QgsProcessingMapLayerComboBox::selectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &, bool )
602 {
603  if ( selected.isEmpty() )
604  mUseSelectionCheckBox->setChecked( false );
605  mUseSelectionCheckBox->setEnabled( !selected.isEmpty() );
606 }
607 
608 void QgsProcessingMapLayerComboBox::showSourceOptions()
609 {
610  if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
611  {
612  QgsProcessingFeatureSourceOptionsWidget *widget = new QgsProcessingFeatureSourceOptionsWidget();
613  widget->setPanelTitle( tr( "%1 Options" ).arg( mParameter->description() ) );
614 
615  widget->setGeometryCheckMethod( mIsOverridingDefaultGeometryCheck, mGeometryCheck );
616  widget->setFeatureLimit( mFeatureLimit );
617 
618  panel->openPanel( widget );
619 
620  connect( widget, &QgsPanelWidget::widgetChanged, this, [ = ]
621  {
622  bool changed = false;
623  changed = changed | ( widget->featureLimit() != mFeatureLimit );
624  changed = changed | ( widget->isOverridingInvalidGeometryCheck() != mIsOverridingDefaultGeometryCheck );
625  changed = changed | ( widget->geometryCheckMethod() != mGeometryCheck );
626 
627  mFeatureLimit = widget->featureLimit();
628  mIsOverridingDefaultGeometryCheck = widget->isOverridingInvalidGeometryCheck();
629  mGeometryCheck = widget->geometryCheckMethod();
630 
631  if ( changed )
632  emit valueChanged();
633  } );
634  }
635 }
636 
637 void QgsProcessingMapLayerComboBox::selectFromFile()
638 {
639  QgsSettings settings;
640  const QString initialValue = currentText();
641  QString path;
642 
643  if ( QFileInfo( initialValue ).isDir() && QFileInfo::exists( initialValue ) )
644  path = initialValue;
645  else if ( QFileInfo::exists( QFileInfo( initialValue ).path() ) && QFileInfo( initialValue ).path() != '.' )
646  path = QFileInfo( initialValue ).path();
647  else if ( settings.contains( QStringLiteral( "/Processing/LastInputPath" ) ) )
648  path = settings.value( QStringLiteral( "/Processing/LastInputPath" ) ).toString();
649 
650  QString filter;
651  if ( const QgsFileFilterGenerator *generator = dynamic_cast< const QgsFileFilterGenerator * >( mParameter.get() ) )
652  filter = generator->createFileFilter();
653  else
654  filter = QObject::tr( "All files (*.*)" );
655 
656  const QString filename = QFileDialog::getOpenFileName( this, tr( "Select File" ), path, filter );
657  if ( filename.isEmpty() )
658  return;
659 
660  settings.setValue( QStringLiteral( "/Processing/LastInputPath" ), QFileInfo( filename ).path() );
661  QgsProcessingContext context;
662  setValue( filename, context );
663 }
664 
665 void QgsProcessingMapLayerComboBox::browseForLayer()
666 {
667  if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
668  {
670  widget->setPanelTitle( tr( "Browse for \"%1\"" ).arg( mParameter->description() ) );
671 
672  panel->openPanel( widget );
673 
674  connect( widget, &QgsDataSourceSelectWidget::itemTriggered, this, [ = ]( const QgsMimeDataUtils::Uri & )
675  {
676  widget->acceptPanel();
677  } );
678  connect( widget, &QgsPanelWidget::panelAccepted, this, [ = ]()
679  {
680  QgsProcessingContext context;
681  if ( widget->uri().uri.isEmpty() )
682  setValue( QVariant(), context );
683  else if ( widget->uri().providerKey == QLatin1String( "ogr" ) )
684  setValue( widget->uri().uri, context );
685  else
686  setValue( QgsProcessingUtils::encodeProviderKeyAndUri( widget->uri().providerKey, widget->uri().uri ), context );
687  } );
688  }
689 }
690 
691 
692 
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
The QgsDataSourceSelectWidget class embeds the browser view to select an existing data source.
void itemTriggered(const QgsMimeDataUtils::Uri &uri)
Emitted when an item is triggered, e.g.
QgsMimeDataUtils::Uri uri() const
Returns the (possibly invalid) uri of the selected data source.
@ GeometryAbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
Abstract interface for classes which generate a file filter string.
The QgsMapLayerComboBox class is a combo box which displays the list of layers.
void layerChanged(QgsMapLayer *layer)
Emitted whenever the currently selected layer changes.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QList< QgsMimeDataUtils::Uri > UriList
static UriList decodeUriList(const QMimeData *data)
Base class for any widget that can be shown as a inline panel.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
void widgetChanged()
Emitted when the widget state changes.
void acceptPanel()
Accept the panel.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
Contains information about the context in which a processing algorithm is executed.
QgsExpressionContext & expressionContext()
Returns the expression context.
Encapsulates settings relating to a feature source input to a processing algorithm.
Flags flags
Flags which dictate source behavior.
bool selectedFeaturesOnly
true if only selected features in the source should be used by algorithms.
QgsFeatureRequest::InvalidGeometryCheck geometryCheck
Geometry check method to apply to this source.
long long featureLimit
If set to a value > 0, places a limit on the maximum number of features which will be read from the s...
WidgetType
Types of dialogs which Processing widgets can be created for.
@ Standard
Standard algorithm dialog.
Base class for the definition of processing parameters.
An input feature source (such as vector layers) parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A map layer parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
A vector layer (with or without geometry) parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
Contains settings which reflect the context in which a Processing parameter widget is shown,...
QgsBrowserGuiModel * browserModel() const
Returns the browser model associated with the widget.
static QString encodeProviderKeyAndUri(const QString &providerKey, const QString &uri)
Encodes a provider key and layer uri to a single string, for use with decodeProviderKeyAndUri()
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true, QgsProcessingUtils::LayerHint typeHint=QgsProcessingUtils::LayerHint::UnknownType)
Interprets a string as a map layer within the supplied context.
@ TypeVectorLine
Vector line layers.
Definition: qgsprocessing.h:50
@ TypeMapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer)
Definition: qgsprocessing.h:47
@ TypeVectorPolygon
Vector polygon layers.
Definition: qgsprocessing.h:51
@ TypeMesh
Mesh layers.
Definition: qgsprocessing.h:55
@ TypeVector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:54
@ TypeRaster
Raster layers.
Definition: qgsprocessing.h:52
@ TypeVectorPoint
Vector point layers.
Definition: qgsprocessing.h:49
@ TypeVectorAnyGeometry
Any vector layer with geometry.
Definition: qgsprocessing.h:48
A store for object properties.
Definition: qgsproperty.h:232
@ StaticProperty
Static property (QgsStaticProperty)
Definition: qgsproperty.h:239
Type propertyType() const
Returns the property type.
Represents a vector layer which manages a vector based data sets.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:938
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
QString uri
Identifier of the data source recognized by its providerKey.
QString providerKey
For "vector" / "raster" type: provider id.