QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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"
30#include <QHBoxLayout>
31#include <QVBoxLayout>
32#include <QToolButton>
33#include <QCheckBox>
34#include <QDragEnterEvent>
35#include <QMenu>
36#include <QAction>
37#include <QFileDialog>
38#include <QUrl>
39
41
42QgsProcessingMapLayerComboBox::QgsProcessingMapLayerComboBox( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
43 : QWidget( parent )
44 , mParameter( parameter->clone() )
45{
46 QHBoxLayout *layout = new QHBoxLayout();
47 layout->setContentsMargins( 0, 0, 0, 0 );
48 layout->setSpacing( 6 );
49
50 mCombo = new QgsMapLayerComboBox();
51 layout->addWidget( mCombo );
52 layout->setAlignment( mCombo, Qt::AlignTop );
53
56 {
57 mIterateButton = new QToolButton();
58 mIterateButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconIterate.svg" ) ) );
59 mIterateButton->setToolTip( tr( "Iterate over this layer, creating a separate output for every feature in the layer" ) );
60 mIterateButton->setCheckable( true );
61 mIterateButton->setAutoRaise( true );
62
63 // button width is 1.25 * icon size, height 1.1 * icon size. But we round to ensure even pixel sizes for equal margins
64 mIterateButton->setFixedSize( 2 * static_cast< int >( 1.25 * iconSize / 2.0 ), 2 * static_cast< int >( iconSize * 1.1 / 2.0 ) );
65 mIterateButton->setIconSize( QSize( iconSize, iconSize ) );
66
67 layout->addWidget( mIterateButton );
68 layout->setAlignment( mIterateButton, Qt::AlignTop );
69 }
70
71 if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() )
72 {
73 mSettingsButton = new QToolButton();
74 mSettingsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionOptions.svg" ) ) );
75 mSettingsButton->setToolTip( tr( "Advanced options" ) );
76
77 // button width is 1.25 * icon size, height 1.1 * icon size. But we round to ensure even pixel sizes for equal margins
78 mSettingsButton->setFixedSize( 2 * static_cast< int >( 1.25 * iconSize / 2.0 ), 2 * static_cast< int >( iconSize * 1.1 / 2.0 ) );
79 mSettingsButton->setIconSize( QSize( iconSize, iconSize ) );
80 mSettingsButton->setAutoRaise( true );
81
82 connect( mSettingsButton, &QToolButton::clicked, this, &QgsProcessingMapLayerComboBox::showSourceOptions );
83 layout->addWidget( mSettingsButton );
84 layout->setAlignment( mSettingsButton, Qt::AlignTop );
85 }
86
87 mSelectButton = new QToolButton();
88 mSelectButton->setText( QString( QChar( 0x2026 ) ) );
89 mSelectButton->setToolTip( tr( "Select input" ) );
90 layout->addWidget( mSelectButton );
91 layout->setAlignment( mSelectButton, Qt::AlignTop );
92 if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() || mParameter->type() == QgsProcessingParameterVectorLayer::typeName() )
93 {
94 mFeatureSourceMenu = new QMenu( this );
95 QAction *selectFromFileAction = new QAction( tr( "Select File…" ), mFeatureSourceMenu );
96 connect( selectFromFileAction, &QAction::triggered, this, &QgsProcessingMapLayerComboBox::selectFromFile );
97 mFeatureSourceMenu->addAction( selectFromFileAction );
98 QAction *browseForLayerAction = new QAction( tr( "Browse for Layer…" ), mFeatureSourceMenu );
99 connect( browseForLayerAction, &QAction::triggered, this, &QgsProcessingMapLayerComboBox::browseForLayer );
100 mFeatureSourceMenu->addAction( browseForLayerAction );
101 mSelectButton->setMenu( mFeatureSourceMenu );
102 mSelectButton->setPopupMode( QToolButton::InstantPopup );
103 }
104 else
105 {
106 connect( mSelectButton, &QToolButton::clicked, this, &QgsProcessingMapLayerComboBox::selectFromFile );
107 }
108
109 QVBoxLayout *vl = new QVBoxLayout();
110 vl->setContentsMargins( 0, 0, 0, 0 );
111 vl->setSpacing( 6 );
112 vl->addLayout( layout );
113
115
116 if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() && type == QgsProcessingGui::Standard )
117 {
118 mUseSelectionCheckBox = new QCheckBox( tr( "Selected features only" ) );
119 mUseSelectionCheckBox->setChecked( false );
120 mUseSelectionCheckBox->setEnabled( false );
121 vl->addWidget( mUseSelectionCheckBox );
122 }
123
124 bool mayBeRaster { false };
125
126 if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() || mParameter->type() == QgsProcessingParameterVectorLayer::typeName() )
127 {
128 QList<int> dataTypes;
129 if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() )
130 dataTypes = static_cast< QgsProcessingParameterFeatureSource *>( mParameter.get() )->dataTypes();
131 else if ( mParameter->type() == QgsProcessingParameterVectorLayer::typeName() )
132 dataTypes = static_cast< QgsProcessingParameterVectorLayer *>( mParameter.get() )->dataTypes();
133
134 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) || dataTypes.isEmpty() )
136 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorPoint ) ) )
138 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorLine ) ) )
140 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorPolygon ) ) )
142 if ( !filters )
144 }
145 else if ( mParameter->type() == QgsProcessingParameterRasterLayer::typeName() )
146 {
147 mayBeRaster = true;
149 }
150 else if ( mParameter->type() == QgsProcessingParameterMeshLayer::typeName() )
151 {
153 }
154 else if ( mParameter->type() == QgsProcessingParameterPointCloudLayer::typeName() )
155 {
157 }
158 else if ( mParameter->type() == QgsProcessingParameterMapLayer::typeName() )
159 {
160 QList<int> dataTypes;
161 dataTypes = static_cast< QgsProcessingParameterMapLayer *>( mParameter.get() )->dataTypes();
162
163 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) )
165 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorPoint ) ) )
167 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorLine ) ) )
169 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorPolygon ) ) )
171 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Raster ) ) )
172 {
173 mayBeRaster = true;
175 }
176 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Mesh ) ) )
178 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::PointCloud ) ) )
180 if ( !filters )
181 filters = Qgis::LayerFilter::All;
182 }
183
184 QgsSettings settings;
185 if ( settings.value( QStringLiteral( "Processing/Configuration/SHOW_CRS_DEF" ), true ).toBool() )
186 mCombo->setShowCrs( true );
187
188 if ( filters )
189 mCombo->setFilters( filters );
190
191 mCombo->setExcludedProviders( QStringList() << QStringLiteral( "grass" ) ); // not sure if this is still required...
192
193 // Check compatibility with virtualraster data provider
194 // see https://github.com/qgis/QGIS/issues/55890
195 if ( mayBeRaster &&
196 ( ! mParameter->provider() || ! mParameter->provider()->flags().testFlag( Qgis::ProcessingProviderFlag::CompatibleWithVirtualRaster ) ) )
197 {
198 mCombo->setExcludedProviders( mCombo->excludedProviders() << QStringLiteral( "virtualraster" ) );
199 }
200
201 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
202 {
203 mCombo->setAllowEmptyLayer( true );
204 mCombo->setLayer( nullptr );
205 }
206
207 connect( mCombo, &QgsMapLayerComboBox::layerChanged, this, &QgsProcessingMapLayerComboBox::onLayerChanged );
208 if ( mUseSelectionCheckBox )
209 connect( mUseSelectionCheckBox, &QCheckBox::toggled, this, [ = ]
210 {
211 if ( !mBlockChangedSignal )
212 emit valueChanged();
213 } );
214
215 setLayout( vl );
216
217 setAcceptDrops( true );
218
219 onLayerChanged( mCombo->currentLayer() );
220}
221
222QgsProcessingMapLayerComboBox::~QgsProcessingMapLayerComboBox() = default;
223
224void QgsProcessingMapLayerComboBox::setLayer( QgsMapLayer *layer )
225{
226 if ( layer || mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
227 mCombo->setLayer( layer );
228}
229
230QgsMapLayer *QgsProcessingMapLayerComboBox::currentLayer()
231{
232 return mCombo->currentLayer();
233}
234
235QString QgsProcessingMapLayerComboBox::currentText()
236{
237 return mCombo->currentText();
238}
239
240void QgsProcessingMapLayerComboBox::setValue( const QVariant &value, QgsProcessingContext &context )
241{
242 if ( !value.isValid() && mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
243 {
244 setLayer( nullptr );
245 return;
246 }
247
248 QVariant val = value;
249 bool found = false;
250 bool selectedOnly = false;
251 bool iterate = false;
252 if ( val.userType() == QMetaType::type( "QgsProcessingFeatureSourceDefinition" ) )
253 {
254 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
255 val = fromVar.source;
256 selectedOnly = fromVar.selectedFeaturesOnly;
258 mFeatureLimit = fromVar.featureLimit;
259 mFilterExpression = fromVar.filterExpression;
260 mIsOverridingDefaultGeometryCheck = fromVar.flags & Qgis::ProcessingFeatureSourceDefinitionFlag::OverrideDefaultGeometryCheck;
261 mGeometryCheck = fromVar.geometryCheck;
262 }
263 else
264 {
265 mFeatureLimit = -1;
266 mFilterExpression.clear();
267 mIsOverridingDefaultGeometryCheck = false;
269 }
270
271 if ( val.userType() == QMetaType::type( "QgsProperty" ) )
272 {
273 if ( val.value< QgsProperty >().propertyType() == Qgis::PropertyType::Static )
274 {
275 val = val.value< QgsProperty >().staticValue();
276 }
277 else
278 {
279 val = val.value< QgsProperty >().valueAsString( context.expressionContext(), mParameter->defaultValueForGui().toString() );
280 }
281 }
282
283 QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( val.value< QObject * >() );
284 if ( !layer && val.type() == QVariant::String )
285 {
286 layer = QgsProcessingUtils::mapLayerFromString( val.toString(), context, false );
287 }
288
289 if ( layer )
290 {
291 mBlockChangedSignal++;
292 QgsMapLayer *prevLayer = currentLayer();
293 setLayer( layer );
294 found = static_cast< bool >( currentLayer() );
295 bool changed = found && ( currentLayer() != prevLayer );
296 if ( found && mUseSelectionCheckBox )
297 {
298 const bool hasSelection = qobject_cast< QgsVectorLayer * >( layer ) && qobject_cast< QgsVectorLayer * >( layer )->selectedFeatureCount() > 0;
299 changed = changed | ( ( hasSelection && selectedOnly ) != mUseSelectionCheckBox->isChecked() );
300 if ( hasSelection )
301 {
302 mUseSelectionCheckBox->setEnabled( true );
303 mUseSelectionCheckBox->setChecked( selectedOnly );
304 }
305 else
306 {
307 mUseSelectionCheckBox->setChecked( false );
308 mUseSelectionCheckBox->setEnabled( false );
309 }
310
311 if ( mIterateButton )
312 {
313 mIterateButton->setChecked( iterate );
314 }
315 }
316 mBlockChangedSignal--;
317 if ( changed )
318 emit valueChanged(); // and ensure we only ever raise one
319 }
320
321 if ( !found )
322 {
323 const QString string = val.toString();
324 if ( mIterateButton )
325 mIterateButton->setChecked( iterate );
326
327 if ( !string.isEmpty() )
328 {
329 mBlockChangedSignal++;
330 if ( mCombo->findText( string ) < 0 )
331 {
332 QStringList additional = mCombo->additionalItems();
333 additional.append( string );
334 mCombo->setAdditionalItems( additional );
335 }
336 mCombo->setCurrentIndex( mCombo->findText( string ) ); // this may or may not throw a signal, so let's block it..
337 if ( mUseSelectionCheckBox )
338 {
339 mUseSelectionCheckBox->setChecked( false );
340 mUseSelectionCheckBox->setEnabled( false );
341 }
342 mBlockChangedSignal--;
343 if ( !mBlockChangedSignal )
344 emit valueChanged(); // and ensure we only ever raise one
345 }
346 else if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
347 {
348 mCombo->setLayer( nullptr );
349 if ( mUseSelectionCheckBox )
350 {
351 mUseSelectionCheckBox->setChecked( false );
352 mUseSelectionCheckBox->setEnabled( false );
353 }
354 }
355 }
356}
357
358QVariant QgsProcessingMapLayerComboBox::value() const
359{
360 if ( isEditable() && mCombo->currentText() != mCombo->itemText( mCombo->currentIndex() ) )
361 return mCombo->currentText();
362
363 const bool iterate = mIterateButton && mIterateButton->isChecked();
364 const bool selectedOnly = mUseSelectionCheckBox && mUseSelectionCheckBox->isChecked();
365 if ( QgsMapLayer *layer = mCombo->currentLayer() )
366 {
367 if ( selectedOnly || iterate || mFeatureLimit != -1 || mIsOverridingDefaultGeometryCheck || !mFilterExpression.isEmpty() )
368 return QgsProcessingFeatureSourceDefinition( layer->id(), selectedOnly, mFeatureLimit,
371 mGeometryCheck, mFilterExpression );
372 else
373 return layer->id();
374 }
375 else
376 {
377 if ( !mCombo->currentText().isEmpty() )
378 {
379 if ( selectedOnly || iterate || mFeatureLimit != -1 || mIsOverridingDefaultGeometryCheck || !mFilterExpression.isEmpty() )
380 return QgsProcessingFeatureSourceDefinition( mCombo->currentText(), selectedOnly, mFeatureLimit,
383 mGeometryCheck, mFilterExpression );
384 else
385 return mCombo->currentText();
386 }
387 }
388 return QVariant();
389}
390
391void QgsProcessingMapLayerComboBox::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
392{
393 mBrowserModel = context.browserModel();
394 mCombo->setProject( context.project() );
395}
396
397void QgsProcessingMapLayerComboBox::setEditable( bool editable )
398{
399 mCombo->setEditable( editable );
400}
401
402bool QgsProcessingMapLayerComboBox::isEditable() const
403{
404 return mCombo->isEditable();
405}
406
407QgsMapLayer *QgsProcessingMapLayerComboBox::compatibleMapLayerFromMimeData( const QMimeData *data, bool &incompatibleLayerSelected ) const
408{
409 incompatibleLayerSelected = false;
411 for ( const QgsMimeDataUtils::Uri &u : uriList )
412 {
413 // is this uri from the current project?
414 if ( QgsMapLayer *layer = u.mapLayer() )
415 {
416 if ( mCombo->mProxyModel->acceptsLayer( layer ) )
417 return layer;
418 else
419 {
420 incompatibleLayerSelected = true;
421 return nullptr;
422 }
423 }
424 }
425 return nullptr;
426}
427
428
429QString QgsProcessingMapLayerComboBox::compatibleUriFromMimeData( const QMimeData *data ) const
430{
432 for ( const QgsMimeDataUtils::Uri &u : uriList )
433 {
434 if ( ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName()
435 || mParameter->type() == QgsProcessingParameterVectorLayer::typeName() )
436 && u.layerType == QLatin1String( "vector" ) )
437 {
438 QList< int > dataTypes = mParameter->type() == QgsProcessingParameterFeatureSource::typeName() ? static_cast< QgsProcessingParameterFeatureSource * >( mParameter.get() )->dataTypes()
439 : ( mParameter->type() == QgsProcessingParameterVectorLayer::typeName() ? static_cast<QgsProcessingParameterVectorLayer *>( mParameter.get() )->dataTypes()
440 : QList< int >() );
441 bool acceptable = false;
442 switch ( QgsWkbTypes::geometryType( u.wkbType ) )
443 {
445 acceptable = true;
446 break;
447
449 if ( dataTypes.isEmpty() || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) ) || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorPoint ) ) )
450 acceptable = true;
451 break;
452
454 if ( dataTypes.isEmpty() || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) ) || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorLine ) ) )
455 acceptable = true;
456 break;
457
459 if ( dataTypes.isEmpty() || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) ) || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorPolygon ) ) )
460 acceptable = true;
461 break;
462
464 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Vector ) ) )
465 acceptable = true;
466 break;
467 }
468 if ( acceptable )
469 return u.providerKey != QLatin1String( "ogr" ) ? QgsProcessingUtils::encodeProviderKeyAndUri( u.providerKey, u.uri ) : u.uri;
470 }
471 else if ( mParameter->type() == QgsProcessingParameterRasterLayer::typeName()
472 && u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" ) )
473 return u.uri;
474 else if ( mParameter->type() == QgsProcessingParameterMeshLayer::typeName()
475 && u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" ) )
476 return u.uri;
477 else if ( mParameter->type() == QgsProcessingParameterMapLayer::typeName() )
478 {
479 QList< int > dataTypes = static_cast< QgsProcessingParameterMapLayer * >( mParameter.get() )->dataTypes();
480 if ( dataTypes.isEmpty() || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::MapLayer ) ) )
481 {
482 return u.uri;
483 }
484
485 if ( u.layerType == QLatin1String( "vector" ) && u.providerKey == QLatin1String( "ogr" ) )
486 {
487 switch ( QgsWkbTypes::geometryType( u.wkbType ) )
488 {
490 return u.uri;
491
493 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorPoint ) ) )
494 return u.uri;
495 break;
496
498 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorLine ) ) )
499 return u.uri;
500 break;
501
503 if ( dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) || dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::VectorPolygon ) ) )
504 return u.uri;
505 break;
506
508 return u.uri;
509 }
510 }
511 else if ( u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" )
512 && dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Raster ) ) )
513 return u.uri;
514 else if ( u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" )
515 && dataTypes.contains( static_cast< int >( Qgis::ProcessingSourceType::Mesh ) ) )
516 return u.uri;
517 }
518 }
519 if ( !uriList.isEmpty() )
520 return QString();
521
522 // second chance -- files dragged from file explorer, outside of QGIS
523 QStringList rawPaths;
524 if ( data->hasUrls() )
525 {
526 const QList< QUrl > urls = data->urls();
527 rawPaths.reserve( urls.count() );
528 for ( const QUrl &url : urls )
529 {
530 const QString local = url.toLocalFile();
531 if ( !rawPaths.contains( local ) )
532 rawPaths.append( local );
533 }
534 }
535 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
536 rawPaths.append( data->text() );
537
538 for ( const QString &path : std::as_const( rawPaths ) )
539 {
540 QFileInfo file( path );
541 if ( file.isFile() )
542 {
543 // TODO - we should check to see if it's a valid extension for the parameter, but that's non-trivial
544 return path;
545 }
546 }
547
548 return QString();
549}
550
551void QgsProcessingMapLayerComboBox::dragEnterEvent( QDragEnterEvent *event )
552{
553 if ( !( event->possibleActions() & Qt::CopyAction ) )
554 return;
555
556 bool incompatibleLayerSelected = false;
557 QgsMapLayer *layer = compatibleMapLayerFromMimeData( event->mimeData(), incompatibleLayerSelected );
558 const QString uri = compatibleUriFromMimeData( event->mimeData() );
559 if ( layer || ( !incompatibleLayerSelected && !uri.isEmpty() ) )
560 {
561 // dragged an acceptable layer, phew
562 event->setDropAction( Qt::CopyAction );
563 event->accept();
564 mDragActive = true;
565 mCombo->mHighlight = true;
566 update();
567 }
568}
569
570void QgsProcessingMapLayerComboBox::dragLeaveEvent( QDragLeaveEvent *event )
571{
572 QWidget::dragLeaveEvent( event );
573 if ( mDragActive )
574 {
575 event->accept();
576 mDragActive = false;
577 mCombo->mHighlight = false;
578 update();
579 }
580}
581
582void QgsProcessingMapLayerComboBox::dropEvent( QDropEvent *event )
583{
584 if ( !( event->possibleActions() & Qt::CopyAction ) )
585 return;
586
587 bool incompatibleLayerSelected = false;
588 QgsMapLayer *layer = compatibleMapLayerFromMimeData( event->mimeData(), incompatibleLayerSelected );
589 const QString uri = compatibleUriFromMimeData( event->mimeData() );
590 if ( layer || ( !incompatibleLayerSelected && !uri.isEmpty() ) )
591 {
592 // dropped an acceptable layer, phew
593 setFocus( Qt::MouseFocusReason );
594 event->setDropAction( Qt::CopyAction );
595 event->accept();
596 QgsProcessingContext context;
597 setValue( layer ? QVariant::fromValue( layer ) : QVariant::fromValue( uri ), context );
598 }
599 mDragActive = false;
600 mCombo->mHighlight = false;
601 update();
602}
603
604void QgsProcessingMapLayerComboBox::onLayerChanged( QgsMapLayer *layer )
605{
606 if ( mUseSelectionCheckBox && mParameter->type() == QgsProcessingParameterFeatureSource::typeName() )
607 {
608 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
609 {
610 if ( QgsVectorLayer *prevLayer = qobject_cast< QgsVectorLayer * >( mPrevLayer ) )
611 {
612 disconnect( prevLayer, &QgsVectorLayer::selectionChanged, this, &QgsProcessingMapLayerComboBox::selectionChanged );
613 }
614 if ( vl->selectedFeatureCount() == 0 )
615 mUseSelectionCheckBox->setChecked( false );
616 mUseSelectionCheckBox->setEnabled( vl->selectedFeatureCount() > 0 );
617 connect( vl, &QgsVectorLayer::selectionChanged, this, &QgsProcessingMapLayerComboBox::selectionChanged );
618 }
619 }
620
621 mPrevLayer = layer;
622 if ( !mBlockChangedSignal )
623 emit valueChanged();
624}
625
626void QgsProcessingMapLayerComboBox::selectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &, bool )
627{
628 if ( selected.isEmpty() )
629 mUseSelectionCheckBox->setChecked( false );
630 mUseSelectionCheckBox->setEnabled( !selected.isEmpty() );
631}
632
633void QgsProcessingMapLayerComboBox::showSourceOptions()
634{
635 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
636 {
637 QgsProcessingFeatureSourceOptionsWidget *widget = new QgsProcessingFeatureSourceOptionsWidget();
638 widget->setPanelTitle( tr( "%1 Options" ).arg( mParameter->description() ) );
639 widget->setLayer( qobject_cast< QgsVectorLayer * >( mCombo->currentLayer() ) );
640
641 widget->setGeometryCheckMethod( mIsOverridingDefaultGeometryCheck, mGeometryCheck );
642 widget->setFeatureLimit( mFeatureLimit );
643 widget->setFilterExpression( mFilterExpression );
644
645 panel->openPanel( widget );
646
647 connect( widget, &QgsPanelWidget::widgetChanged, this, [ = ]
648 {
649 bool changed = false;
650 changed = changed | ( widget->featureLimit() != mFeatureLimit );
651 changed = changed | ( widget->filterExpression() != mFilterExpression );
652 changed = changed | ( widget->isOverridingInvalidGeometryCheck() != mIsOverridingDefaultGeometryCheck );
653 changed = changed | ( widget->geometryCheckMethod() != mGeometryCheck );
654
655 mFeatureLimit = widget->featureLimit();
656 mFilterExpression = widget->filterExpression();
657 mIsOverridingDefaultGeometryCheck = widget->isOverridingInvalidGeometryCheck();
658 mGeometryCheck = widget->geometryCheckMethod();
659
660 if ( changed )
661 emit valueChanged();
662 } );
663 }
664}
665
666void QgsProcessingMapLayerComboBox::selectFromFile()
667{
668 QgsSettings settings;
669 const QString initialValue = currentText();
670 QString path;
671
672 if ( QFileInfo( initialValue ).isDir() && QFileInfo::exists( initialValue ) )
673 path = initialValue;
674 else if ( QFileInfo::exists( QFileInfo( initialValue ).path() ) && QFileInfo( initialValue ).path() != '.' )
675 path = QFileInfo( initialValue ).path();
676 else if ( settings.contains( QStringLiteral( "/Processing/LastInputPath" ) ) )
677 path = settings.value( QStringLiteral( "/Processing/LastInputPath" ) ).toString();
678
679 QString filter;
680 if ( const QgsFileFilterGenerator *generator = dynamic_cast< const QgsFileFilterGenerator * >( mParameter.get() ) )
681 filter = generator->createFileFilter();
682 else
683 filter = QObject::tr( "All files (*.*)" );
684
685 const QString filename = QFileDialog::getOpenFileName( this, tr( "Select File" ), path, filter );
686 if ( filename.isEmpty() )
687 return;
688
689 settings.setValue( QStringLiteral( "/Processing/LastInputPath" ), QFileInfo( filename ).path() );
690 QgsProcessingContext context;
691 setValue( filename, context );
692}
693
694void QgsProcessingMapLayerComboBox::browseForLayer()
695{
696 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
697 {
699 widget->setPanelTitle( tr( "Browse for \"%1\"" ).arg( mParameter->description() ) );
700
701 panel->openPanel( widget );
702
703 connect( widget, &QgsDataSourceSelectWidget::itemTriggered, this, [ = ]( const QgsMimeDataUtils::Uri & )
704 {
705 widget->acceptPanel();
706 } );
707 connect( widget, &QgsPanelWidget::panelAccepted, this, [ = ]()
708 {
709 QgsProcessingContext context;
710 if ( widget->uri().uri.isEmpty() )
711 setValue( QVariant(), context );
712 else if ( widget->uri().providerKey == QLatin1String( "ogr" ) )
713 setValue( widget->uri().uri, context );
714 else
715 setValue( QgsProcessingUtils::encodeProviderKeyAndUri( widget->uri().providerKey, widget->uri().uri ), context );
716 } );
717 }
718}
719
720
721
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
@ MapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer)
@ VectorAnyGeometry
Any vector layer with geometry.
@ VectorPoint
Vector point layers.
@ VectorPolygon
Vector polygon layers.
@ VectorLine
Vector line layers.
@ PointCloud
Point cloud layers.
@ CompatibleWithVirtualRaster
The processing provider's algorithms can work with QGIS virtualraster data provider....
@ Static
Static property.
@ PointCloudLayer
QgsPointCloudLayer.
@ MeshLayer
QgsMeshLayer.
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
@ Vector
Vector layer.
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
QFlags< ProcessingFeatureSourceDefinitionFlag > ProcessingFeatureSourceDefinitionFlags
Flags which control behavior for a Processing feature source.
Definition: qgis.h:2988
@ CreateIndividualOutputPerInputFeature
If set, every feature processed from this source will be placed into its own individually created out...
@ OverrideDefaultGeometryCheck
If set, the default geometry check method (as dictated by QgsProcessingContext) will be overridden fo...
@ Optional
Parameter is optional.
QFlags< LayerFilter > LayerFilters
Definition: qgis.h:151
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.
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:75
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.
bool selectedFeaturesOnly
true if only selected features in the source should be used by algorithms.
Qgis::InvalidGeometryCheck geometryCheck
Geometry check method to apply to this source.
Qgis::ProcessingFeatureSourceDefinitionFlags flags
Flags which dictate source behavior.
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...
QString filterExpression
Optional expression filter to use for filtering features which will be read from the source.
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, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Interprets a string as a map layer within the supplied context.
A store for object properties.
Definition: qgsproperty.h:228
Qgis::PropertyType propertyType() const
Returns the property type.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:64
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 Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:862
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.