QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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() || mParameter->type() == QgsProcessingParameterVectorLayer::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() == QgsProcessingParameterPointCloudLayer::typeName() )
151  {
153  }
154  else if ( mParameter->type() == QgsProcessingParameterMapLayer::typeName() )
155  {
156  QList<int> dataTypes;
157  dataTypes = static_cast< QgsProcessingParameterMapLayer *>( mParameter.get() )->dataTypes();
158 
159  if ( dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) )
161  if ( dataTypes.contains( QgsProcessing::TypeVectorPoint ) )
163  if ( dataTypes.contains( QgsProcessing::TypeVectorLine ) )
165  if ( dataTypes.contains( QgsProcessing::TypeVectorPolygon ) )
167  if ( dataTypes.contains( QgsProcessing::TypeRaster ) )
169  if ( dataTypes.contains( QgsProcessing::TypeMesh ) )
171  if ( dataTypes.contains( QgsProcessing::TypePointCloud ) )
173  if ( !filters )
174  filters = QgsMapLayerProxyModel::All;
175  }
176 
177  QgsSettings settings;
178  if ( settings.value( QStringLiteral( "Processing/Configuration/SHOW_CRS_DEF" ), true ).toBool() )
179  mCombo->setShowCrs( true );
180 
181  if ( filters )
182  mCombo->setFilters( filters );
183  mCombo->setExcludedProviders( QStringList() << QStringLiteral( "grass" ) ); // not sure if this is still required...
184 
185  if ( mParameter->flags() & QgsProcessingParameterDefinition::FlagOptional )
186  {
187  mCombo->setAllowEmptyLayer( true );
188  mCombo->setLayer( nullptr );
189  }
190 
191  connect( mCombo, &QgsMapLayerComboBox::layerChanged, this, &QgsProcessingMapLayerComboBox::onLayerChanged );
192  if ( mUseSelectionCheckBox )
193  connect( mUseSelectionCheckBox, &QCheckBox::toggled, this, [ = ]
194  {
195  if ( !mBlockChangedSignal )
196  emit valueChanged();
197  } );
198 
199  setLayout( vl );
200 
201  setAcceptDrops( true );
202 
203  onLayerChanged( mCombo->currentLayer() );
204 }
205 
206 QgsProcessingMapLayerComboBox::~QgsProcessingMapLayerComboBox() = default;
207 
208 void QgsProcessingMapLayerComboBox::setLayer( QgsMapLayer *layer )
209 {
210  if ( layer || mParameter->flags() & QgsProcessingParameterDefinition::FlagOptional )
211  mCombo->setLayer( layer );
212 }
213 
214 QgsMapLayer *QgsProcessingMapLayerComboBox::currentLayer()
215 {
216  return mCombo->currentLayer();
217 }
218 
219 QString QgsProcessingMapLayerComboBox::currentText()
220 {
221  return mCombo->currentText();
222 }
223 
224 void QgsProcessingMapLayerComboBox::setValue( const QVariant &value, QgsProcessingContext &context )
225 {
226  if ( !value.isValid() && mParameter->flags() & QgsProcessingParameterDefinition::FlagOptional )
227  {
228  setLayer( nullptr );
229  return;
230  }
231 
232  QVariant val = value;
233  bool found = false;
234  bool selectedOnly = false;
235  bool iterate = false;
236  if ( val.canConvert<QgsProcessingFeatureSourceDefinition>() )
237  {
238  QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
239  val = fromVar.source;
240  selectedOnly = fromVar.selectedFeaturesOnly;
241  iterate = fromVar.flags & QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature;
242  mFeatureLimit = fromVar.featureLimit;
243  mIsOverridingDefaultGeometryCheck = fromVar.flags & QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck;
244  mGeometryCheck = fromVar.geometryCheck;
245  }
246  else
247  {
248  mFeatureLimit = -1;
249  mIsOverridingDefaultGeometryCheck = false;
251  }
252 
253  if ( val.canConvert<QgsProperty>() )
254  {
255  if ( val.value< QgsProperty >().propertyType() == QgsProperty::StaticProperty )
256  {
257  val = val.value< QgsProperty >().staticValue();
258  }
259  else
260  {
261  val = val.value< QgsProperty >().valueAsString( context.expressionContext(), mParameter->defaultValueForGui().toString() );
262  }
263  }
264 
265  QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( val.value< QObject * >() );
266  if ( !layer && val.type() == QVariant::String )
267  {
268  layer = QgsProcessingUtils::mapLayerFromString( val.toString(), context, false );
269  }
270 
271  if ( layer )
272  {
273  mBlockChangedSignal++;
274  QgsMapLayer *prevLayer = currentLayer();
275  setLayer( layer );
276  found = static_cast< bool >( currentLayer() );
277  bool changed = found && ( currentLayer() != prevLayer );
278  if ( found && mUseSelectionCheckBox )
279  {
280  const bool hasSelection = qobject_cast< QgsVectorLayer * >( layer ) && qobject_cast< QgsVectorLayer * >( layer )->selectedFeatureCount() > 0;
281  changed = changed | ( ( hasSelection && selectedOnly ) != mUseSelectionCheckBox->isChecked() );
282  if ( hasSelection )
283  {
284  mUseSelectionCheckBox->setEnabled( true );
285  mUseSelectionCheckBox->setChecked( selectedOnly );
286  }
287  else
288  {
289  mUseSelectionCheckBox->setChecked( false );
290  mUseSelectionCheckBox->setEnabled( false );
291  }
292 
293  if ( mIterateButton )
294  {
295  mIterateButton->setChecked( iterate );
296  }
297  }
298  mBlockChangedSignal--;
299  if ( changed )
300  emit valueChanged(); // and ensure we only ever raise one
301  }
302 
303  if ( !found )
304  {
305  const QString string = val.toString();
306  if ( mIterateButton )
307  mIterateButton->setChecked( iterate );
308 
309  if ( !string.isEmpty() )
310  {
311  mBlockChangedSignal++;
312  if ( mCombo->findText( string ) < 0 )
313  {
314  QStringList additional = mCombo->additionalItems();
315  additional.append( string );
316  mCombo->setAdditionalItems( additional );
317  }
318  mCombo->setCurrentIndex( mCombo->findText( string ) ); // this may or may not throw a signal, so let's block it..
319  if ( mUseSelectionCheckBox )
320  {
321  mUseSelectionCheckBox->setChecked( false );
322  mUseSelectionCheckBox->setEnabled( false );
323  }
324  mBlockChangedSignal--;
325  if ( !mBlockChangedSignal )
326  emit valueChanged(); // and ensure we only ever raise one
327  }
328  else if ( mParameter->flags() & QgsProcessingParameterDefinition::FlagOptional )
329  {
330  mCombo->setLayer( nullptr );
331  if ( mUseSelectionCheckBox )
332  {
333  mUseSelectionCheckBox->setChecked( false );
334  mUseSelectionCheckBox->setEnabled( false );
335  }
336  }
337  }
338 }
339 
340 QVariant QgsProcessingMapLayerComboBox::value() const
341 {
342  if ( isEditable() && mCombo->currentText() != mCombo->itemText( mCombo->currentIndex() ) )
343  return mCombo->currentText();
344 
345  const bool iterate = mIterateButton && mIterateButton->isChecked();
346  const bool selectedOnly = mUseSelectionCheckBox && mUseSelectionCheckBox->isChecked();
347  if ( QgsMapLayer *layer = mCombo->currentLayer() )
348  {
349  if ( selectedOnly || iterate || mFeatureLimit != -1 || mIsOverridingDefaultGeometryCheck )
350  return QgsProcessingFeatureSourceDefinition( layer->id(), selectedOnly, mFeatureLimit,
351  ( iterate ? QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature : QgsProcessingFeatureSourceDefinition::Flags() )
352  | ( mIsOverridingDefaultGeometryCheck ? QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck : QgsProcessingFeatureSourceDefinition::Flags() ),
353  mGeometryCheck );
354  else
355  return layer->id();
356  }
357  else
358  {
359  if ( !mCombo->currentText().isEmpty() )
360  {
361  if ( selectedOnly || iterate || mFeatureLimit != -1 || mIsOverridingDefaultGeometryCheck )
362  return QgsProcessingFeatureSourceDefinition( mCombo->currentText(), selectedOnly, mFeatureLimit,
363  ( iterate ? QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature : QgsProcessingFeatureSourceDefinition::Flags() )
364  | ( mIsOverridingDefaultGeometryCheck ? QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck : QgsProcessingFeatureSourceDefinition::Flags() ),
365  mGeometryCheck );
366  else
367  return mCombo->currentText();
368  }
369  }
370  return QVariant();
371 }
372 
373 void QgsProcessingMapLayerComboBox::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
374 {
375  mBrowserModel = context.browserModel();
376  mCombo->setProject( context.project() );
377 }
378 
379 void QgsProcessingMapLayerComboBox::setEditable( bool editable )
380 {
381  mCombo->setEditable( editable );
382 }
383 
384 bool QgsProcessingMapLayerComboBox::isEditable() const
385 {
386  return mCombo->isEditable();
387 }
388 
389 QgsMapLayer *QgsProcessingMapLayerComboBox::compatibleMapLayerFromMimeData( const QMimeData *data, bool &incompatibleLayerSelected ) const
390 {
391  incompatibleLayerSelected = false;
393  for ( const QgsMimeDataUtils::Uri &u : uriList )
394  {
395  // is this uri from the current project?
396  if ( QgsMapLayer *layer = u.mapLayer() )
397  {
398  if ( mCombo->mProxyModel->acceptsLayer( layer ) )
399  return layer;
400  else
401  {
402  incompatibleLayerSelected = true;
403  return nullptr;
404  }
405  }
406  }
407  return nullptr;
408 }
409 
410 
411 QString QgsProcessingMapLayerComboBox::compatibleUriFromMimeData( const QMimeData *data ) const
412 {
414  for ( const QgsMimeDataUtils::Uri &u : uriList )
415  {
416  if ( ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName()
417  || mParameter->type() == QgsProcessingParameterVectorLayer::typeName() )
418  && u.layerType == QLatin1String( "vector" ) )
419  {
420  QList< int > dataTypes = mParameter->type() == QgsProcessingParameterFeatureSource::typeName() ? static_cast< QgsProcessingParameterFeatureSource * >( mParameter.get() )->dataTypes()
421  : ( mParameter->type() == QgsProcessingParameterVectorLayer::typeName() ? static_cast<QgsProcessingParameterVectorLayer *>( mParameter.get() )->dataTypes()
422  : QList< int >() );
423  bool acceptable = false;
424  switch ( QgsWkbTypes::geometryType( u.wkbType ) )
425  {
427  acceptable = true;
428  break;
429 
431  if ( dataTypes.isEmpty() || dataTypes.contains( QgsProcessing::TypeVector ) || dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.contains( QgsProcessing::TypeVectorPoint ) )
432  acceptable = true;
433  break;
434 
436  if ( dataTypes.isEmpty() || dataTypes.contains( QgsProcessing::TypeVector ) || dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.contains( QgsProcessing::TypeVectorLine ) )
437  acceptable = true;
438  break;
439 
441  if ( dataTypes.isEmpty() || dataTypes.contains( QgsProcessing::TypeVector ) || dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.contains( QgsProcessing::TypeVectorPolygon ) )
442  acceptable = true;
443  break;
444 
446  if ( dataTypes.contains( QgsProcessing::TypeVector ) )
447  acceptable = true;
448  break;
449  }
450  if ( acceptable )
451  return u.providerKey != QLatin1String( "ogr" ) ? QgsProcessingUtils::encodeProviderKeyAndUri( u.providerKey, u.uri ) : u.uri;
452  }
453  else if ( mParameter->type() == QgsProcessingParameterRasterLayer::typeName()
454  && u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" ) )
455  return u.uri;
456  else if ( mParameter->type() == QgsProcessingParameterMeshLayer::typeName()
457  && u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" ) )
458  return u.uri;
459  else if ( mParameter->type() == QgsProcessingParameterMapLayer::typeName() )
460  {
461  QList< int > dataTypes = static_cast< QgsProcessingParameterMapLayer * >( mParameter.get() )->dataTypes();
462  if ( dataTypes.isEmpty() || dataTypes.contains( QgsProcessing::TypeMapLayer ) )
463  {
464  return u.uri;
465  }
466 
467  if ( u.layerType == QLatin1String( "vector" ) && u.providerKey == QLatin1String( "ogr" ) )
468  {
469  switch ( QgsWkbTypes::geometryType( u.wkbType ) )
470  {
472  return u.uri;
473 
475  if ( dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.contains( QgsProcessing::TypeVectorPoint ) )
476  return u.uri;
477  break;
478 
480  if ( dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.contains( QgsProcessing::TypeVectorLine ) )
481  return u.uri;
482  break;
483 
485  if ( dataTypes.contains( QgsProcessing::TypeVectorAnyGeometry ) || dataTypes.contains( QgsProcessing::TypeVectorPolygon ) )
486  return u.uri;
487  break;
488 
490  return u.uri;
491  }
492  }
493  else if ( u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" )
494  && dataTypes.contains( QgsProcessing::TypeRaster ) )
495  return u.uri;
496  else if ( u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" )
497  && dataTypes.contains( QgsProcessing::TypeMesh ) )
498  return u.uri;
499  }
500  }
501  if ( !uriList.isEmpty() )
502  return QString();
503 
504  // second chance -- files dragged from file explorer, outside of QGIS
505  QStringList rawPaths;
506  if ( data->hasUrls() )
507  {
508  const QList< QUrl > urls = data->urls();
509  rawPaths.reserve( urls.count() );
510  for ( const QUrl &url : urls )
511  {
512  const QString local = url.toLocalFile();
513  if ( !rawPaths.contains( local ) )
514  rawPaths.append( local );
515  }
516  }
517  if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
518  rawPaths.append( data->text() );
519 
520  for ( const QString &path : std::as_const( rawPaths ) )
521  {
522  QFileInfo file( path );
523  if ( file.isFile() )
524  {
525  // TODO - we should check to see if it's a valid extension for the parameter, but that's non-trivial
526  return path;
527  }
528  }
529 
530  return QString();
531 }
532 
533 void QgsProcessingMapLayerComboBox::dragEnterEvent( QDragEnterEvent *event )
534 {
535  if ( !( event->possibleActions() & Qt::CopyAction ) )
536  return;
537 
538  bool incompatibleLayerSelected = false;
539  QgsMapLayer *layer = compatibleMapLayerFromMimeData( event->mimeData(), incompatibleLayerSelected );
540  const QString uri = compatibleUriFromMimeData( event->mimeData() );
541  if ( layer || ( !incompatibleLayerSelected && !uri.isEmpty() ) )
542  {
543  // dragged an acceptable layer, phew
544  event->setDropAction( Qt::CopyAction );
545  event->accept();
546  mDragActive = true;
547  mCombo->mHighlight = true;
548  update();
549  }
550 }
551 
552 void QgsProcessingMapLayerComboBox::dragLeaveEvent( QDragLeaveEvent *event )
553 {
554  QWidget::dragLeaveEvent( event );
555  if ( mDragActive )
556  {
557  event->accept();
558  mDragActive = false;
559  mCombo->mHighlight = false;
560  update();
561  }
562 }
563 
564 void QgsProcessingMapLayerComboBox::dropEvent( QDropEvent *event )
565 {
566  if ( !( event->possibleActions() & Qt::CopyAction ) )
567  return;
568 
569  bool incompatibleLayerSelected = false;
570  QgsMapLayer *layer = compatibleMapLayerFromMimeData( event->mimeData(), incompatibleLayerSelected );
571  const QString uri = compatibleUriFromMimeData( event->mimeData() );
572  if ( layer || ( !incompatibleLayerSelected && !uri.isEmpty() ) )
573  {
574  // dropped an acceptable layer, phew
575  setFocus( Qt::MouseFocusReason );
576  event->setDropAction( Qt::CopyAction );
577  event->accept();
578  QgsProcessingContext context;
579  setValue( layer ? QVariant::fromValue( layer ) : QVariant::fromValue( uri ), context );
580  }
581  mDragActive = false;
582  mCombo->mHighlight = false;
583  update();
584 }
585 
586 void QgsProcessingMapLayerComboBox::onLayerChanged( QgsMapLayer *layer )
587 {
588  if ( mUseSelectionCheckBox && mParameter->type() == QgsProcessingParameterFeatureSource::typeName() )
589  {
590  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
591  {
592  if ( QgsVectorLayer *prevLayer = qobject_cast< QgsVectorLayer * >( mPrevLayer ) )
593  {
594  disconnect( prevLayer, &QgsVectorLayer::selectionChanged, this, &QgsProcessingMapLayerComboBox::selectionChanged );
595  }
596  if ( vl->selectedFeatureCount() == 0 )
597  mUseSelectionCheckBox->setChecked( false );
598  mUseSelectionCheckBox->setEnabled( vl->selectedFeatureCount() > 0 );
599  connect( vl, &QgsVectorLayer::selectionChanged, this, &QgsProcessingMapLayerComboBox::selectionChanged );
600  }
601  }
602 
603  mPrevLayer = layer;
604  if ( !mBlockChangedSignal )
605  emit valueChanged();
606 }
607 
608 void QgsProcessingMapLayerComboBox::selectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &, bool )
609 {
610  if ( selected.isEmpty() )
611  mUseSelectionCheckBox->setChecked( false );
612  mUseSelectionCheckBox->setEnabled( !selected.isEmpty() );
613 }
614 
615 void QgsProcessingMapLayerComboBox::showSourceOptions()
616 {
617  if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
618  {
619  QgsProcessingFeatureSourceOptionsWidget *widget = new QgsProcessingFeatureSourceOptionsWidget();
620  widget->setPanelTitle( tr( "%1 Options" ).arg( mParameter->description() ) );
621 
622  widget->setGeometryCheckMethod( mIsOverridingDefaultGeometryCheck, mGeometryCheck );
623  widget->setFeatureLimit( mFeatureLimit );
624 
625  panel->openPanel( widget );
626 
627  connect( widget, &QgsPanelWidget::widgetChanged, this, [ = ]
628  {
629  bool changed = false;
630  changed = changed | ( widget->featureLimit() != mFeatureLimit );
631  changed = changed | ( widget->isOverridingInvalidGeometryCheck() != mIsOverridingDefaultGeometryCheck );
632  changed = changed | ( widget->geometryCheckMethod() != mGeometryCheck );
633 
634  mFeatureLimit = widget->featureLimit();
635  mIsOverridingDefaultGeometryCheck = widget->isOverridingInvalidGeometryCheck();
636  mGeometryCheck = widget->geometryCheckMethod();
637 
638  if ( changed )
639  emit valueChanged();
640  } );
641  }
642 }
643 
644 void QgsProcessingMapLayerComboBox::selectFromFile()
645 {
646  QgsSettings settings;
647  const QString initialValue = currentText();
648  QString path;
649 
650  if ( QFileInfo( initialValue ).isDir() && QFileInfo::exists( initialValue ) )
651  path = initialValue;
652  else if ( QFileInfo::exists( QFileInfo( initialValue ).path() ) && QFileInfo( initialValue ).path() != '.' )
653  path = QFileInfo( initialValue ).path();
654  else if ( settings.contains( QStringLiteral( "/Processing/LastInputPath" ) ) )
655  path = settings.value( QStringLiteral( "/Processing/LastInputPath" ) ).toString();
656 
657  QString filter;
658  if ( const QgsFileFilterGenerator *generator = dynamic_cast< const QgsFileFilterGenerator * >( mParameter.get() ) )
659  filter = generator->createFileFilter();
660  else
661  filter = QObject::tr( "All files (*.*)" );
662 
663  const QString filename = QFileDialog::getOpenFileName( this, tr( "Select File" ), path, filter );
664  if ( filename.isEmpty() )
665  return;
666 
667  settings.setValue( QStringLiteral( "/Processing/LastInputPath" ), QFileInfo( filename ).path() );
668  QgsProcessingContext context;
669  setValue( filename, context );
670 }
671 
672 void QgsProcessingMapLayerComboBox::browseForLayer()
673 {
674  if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
675  {
677  widget->setPanelTitle( tr( "Browse for \"%1\"" ).arg( mParameter->description() ) );
678 
679  panel->openPanel( widget );
680 
681  connect( widget, &QgsDataSourceSelectWidget::itemTriggered, this, [ = ]( const QgsMimeDataUtils::Uri & )
682  {
683  widget->acceptPanel();
684  } );
685  connect( widget, &QgsPanelWidget::panelAccepted, this, [ = ]()
686  {
687  QgsProcessingContext context;
688  if ( widget->uri().uri.isEmpty() )
689  setValue( QVariant(), context );
690  else if ( widget->uri().providerKey == QLatin1String( "ogr" ) )
691  setValue( widget->uri().uri, context );
692  else
693  setValue( QgsProcessingUtils::encodeProviderKeyAndUri( widget->uri().providerKey, widget->uri().uri ), context );
694  } );
695  }
696 }
697 
698 
699 
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.
@ PointCloudLayer
QgsPointCloudLayer.
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.
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,...
QgsProject * project() const
Returns the project associated with the widget.
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
@ TypePointCloud
Point cloud layers.
Definition: qgsprocessing.h:57
@ 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:231
@ StaticProperty
Static property (QgsStaticProperty)
Definition: qgsproperty.h:238
Type propertyType() const
Returns the property type.
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.
bool contains(const QString &key, QgsSettings::Section section=QgsSettings::NoSection) const
Returns true if there exists a setting called key; returns false otherwise.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
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:968
@ VectorLayer
Vector layer.
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.