QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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
41QgsProcessingMapLayerComboBox::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
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 )
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
206QgsProcessingMapLayerComboBox::~QgsProcessingMapLayerComboBox() = default;
207
208void QgsProcessingMapLayerComboBox::setLayer( QgsMapLayer *layer )
209{
210 if ( layer || mParameter->flags() & QgsProcessingParameterDefinition::FlagOptional )
211 mCombo->setLayer( layer );
212}
213
214QgsMapLayer *QgsProcessingMapLayerComboBox::currentLayer()
215{
216 return mCombo->currentLayer();
217}
218
219QString QgsProcessingMapLayerComboBox::currentText()
220{
221 return mCombo->currentText();
222}
223
224void 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.userType() == QMetaType::type( "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.userType() == QMetaType::type( "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
340QVariant 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
373void QgsProcessingMapLayerComboBox::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
374{
375 mBrowserModel = context.browserModel();
376 mCombo->setProject( context.project() );
377}
378
379void QgsProcessingMapLayerComboBox::setEditable( bool editable )
380{
381 mCombo->setEditable( editable );
382}
383
384bool QgsProcessingMapLayerComboBox::isEditable() const
385{
386 return mCombo->isEditable();
387}
388
389QgsMapLayer *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
411QString 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
533void 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
552void 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
564void 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
586void 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
608void QgsProcessingMapLayerComboBox::selectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &, bool )
609{
610 if ( selected.isEmpty() )
611 mUseSelectionCheckBox->setChecked( false );
612 mUseSelectionCheckBox->setEnabled( !selected.isEmpty() );
613}
614
615void 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
644void 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
672void 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:230
@ StaticProperty
Static property (QgsStaticProperty)
Definition: qgsproperty.h:237
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.