QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
Loading...
Searching...
No Matches
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
18#include "qgsapplication.h"
20#include "qgsfeatureid.h"
21#include "qgsguiutils.h"
22#include "qgsmaplayercombobox.h"
23#include "qgsmimedatautils.h"
24#include "qgspanelwidget.h"
30#include "qgsproviderregistry.h"
31#include "qgssettings.h"
32#include "qgsvectorlayer.h"
33#include "qgswmsutils.h"
34
35#include <QAction>
36#include <QCheckBox>
37#include <QDragEnterEvent>
38#include <QFileDialog>
39#include <QHBoxLayout>
40#include <QMenu>
41#include <QString>
42#include <QToolButton>
43#include <QUrl>
44#include <QVBoxLayout>
45
46#include "moc_qgsprocessingmaplayercombobox.cpp"
47
48using namespace Qt::StringLiterals;
49
51
52QgsProcessingMapLayerComboBox::QgsProcessingMapLayerComboBox( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
53 : QWidget( parent )
54 , mParameter( parameter->clone() )
55{
56 QHBoxLayout *layout = new QHBoxLayout();
57 layout->setContentsMargins( 0, 0, 0, 0 );
58 layout->setSpacing( 6 );
59
60 mCombo = new QgsMapLayerComboBox();
61 layout->addWidget( mCombo );
62 layout->setAlignment( mCombo, Qt::AlignTop );
63
66 {
67 mIterateButton = new QToolButton();
68 mIterateButton->setIcon( QgsApplication::getThemeIcon( u"mIconIterate.svg"_s ) );
69 mIterateButton->setToolTip( tr( "Iterate over this layer, creating a separate output for every feature in the layer" ) );
70 mIterateButton->setCheckable( true );
71 mIterateButton->setAutoRaise( true );
72
73 // button width is 1.25 * icon size, height 1.1 * icon size. But we round to ensure even pixel sizes for equal margins
74 mIterateButton->setFixedSize( 2 * static_cast<int>( 1.25 * iconSize / 2.0 ), 2 * static_cast<int>( iconSize * 1.1 / 2.0 ) );
75 mIterateButton->setIconSize( QSize( iconSize, iconSize ) );
76
77 layout->addWidget( mIterateButton );
78 layout->setAlignment( mIterateButton, Qt::AlignTop );
79 }
80
81 if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() )
82 {
83 mSettingsButton = new QToolButton();
84 mSettingsButton->setIcon( QgsApplication::getThemeIcon( u"mActionOptions.svg"_s ) );
85 mSettingsButton->setToolTip( tr( "Advanced options" ) );
86
87 // button width is 1.25 * icon size, height 1.1 * icon size. But we round to ensure even pixel sizes for equal margins
88 mSettingsButton->setFixedSize( 2 * static_cast<int>( 1.25 * iconSize / 2.0 ), 2 * static_cast<int>( iconSize * 1.1 / 2.0 ) );
89 mSettingsButton->setIconSize( QSize( iconSize, iconSize ) );
90 mSettingsButton->setAutoRaise( true );
91
92 connect( mSettingsButton, &QToolButton::clicked, this, &QgsProcessingMapLayerComboBox::showSourceOptions );
93 layout->addWidget( mSettingsButton );
94 layout->setAlignment( mSettingsButton, Qt::AlignTop );
95 }
96 else if ( mParameter->type() == QgsProcessingParameterRasterLayer::typeName() )
97 {
98 mSettingsButton = new QToolButton();
99 mSettingsButton->setIcon( QgsApplication::getThemeIcon( u"mActionOptions.svg"_s ) );
100 mSettingsButton->setToolTip( tr( "Advanced options" ) );
101 mSettingsButton->setEnabled( false ); // Only WMS layers will access raster advanced options for now
102
103 // button width is 1.25 * icon size, height 1.1 * icon size. But we round to ensure even pixel sizes for equal margins
104 mSettingsButton->setFixedSize( 2 * static_cast<int>( 1.25 * iconSize / 2.0 ), 2 * static_cast<int>( iconSize * 1.1 / 2.0 ) );
105 mSettingsButton->setIconSize( QSize( iconSize, iconSize ) );
106 mSettingsButton->setAutoRaise( true );
107
108 connect( mSettingsButton, &QToolButton::clicked, this, &QgsProcessingMapLayerComboBox::showRasterSourceOptions );
109 layout->addWidget( mSettingsButton );
110 layout->setAlignment( mSettingsButton, Qt::AlignTop );
111 }
112
113 mSelectButton = new QToolButton();
114 mSelectButton->setText( QString( QChar( 0x2026 ) ) );
115 mSelectButton->setToolTip( tr( "Select input" ) );
116 layout->addWidget( mSelectButton );
117 layout->setAlignment( mSelectButton, Qt::AlignTop );
118 if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() || mParameter->type() == QgsProcessingParameterVectorLayer::typeName() )
119 {
120 mFeatureSourceMenu = new QMenu( this );
121 QAction *selectFromFileAction = new QAction( tr( "Select File…" ), mFeatureSourceMenu );
122 connect( selectFromFileAction, &QAction::triggered, this, &QgsProcessingMapLayerComboBox::selectFromFile );
123 mFeatureSourceMenu->addAction( selectFromFileAction );
124 QAction *browseForLayerAction = new QAction( tr( "Browse for Layer…" ), mFeatureSourceMenu );
125 connect( browseForLayerAction, &QAction::triggered, this, &QgsProcessingMapLayerComboBox::browseForLayer );
126 mFeatureSourceMenu->addAction( browseForLayerAction );
127 mSelectButton->setMenu( mFeatureSourceMenu );
128 mSelectButton->setPopupMode( QToolButton::InstantPopup );
129 }
130 else
131 {
132 connect( mSelectButton, &QToolButton::clicked, this, &QgsProcessingMapLayerComboBox::selectFromFile );
133 }
134
135 QVBoxLayout *vl = new QVBoxLayout();
136 vl->setContentsMargins( 0, 0, 0, 0 );
137 vl->setSpacing( 6 );
138 vl->addLayout( layout );
139
141
143 {
144 mUseSelectionCheckBox = new QCheckBox( tr( "Selected features only" ) );
145 mUseSelectionCheckBox->setChecked( false );
146 mUseSelectionCheckBox->setEnabled( false );
147 vl->addWidget( mUseSelectionCheckBox );
148 }
149
150 bool mayBeRaster { false };
151
152 if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() || mParameter->type() == QgsProcessingParameterVectorLayer::typeName() )
153 {
154 QList<int> dataTypes;
155 if ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() )
156 dataTypes = static_cast<QgsProcessingParameterFeatureSource *>( mParameter.get() )->dataTypes();
157 else if ( mParameter->type() == QgsProcessingParameterVectorLayer::typeName() )
158 dataTypes = static_cast<QgsProcessingParameterVectorLayer *>( mParameter.get() )->dataTypes();
159
160 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) || dataTypes.isEmpty() )
162 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) ) )
164 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) ) )
166 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) ) )
168 if ( !filters )
170 }
171 else if ( mParameter->type() == QgsProcessingParameterRasterLayer::typeName() )
172 {
173 mayBeRaster = true;
175 }
176 else if ( mParameter->type() == QgsProcessingParameterMeshLayer::typeName() )
177 {
179 }
180 else if ( mParameter->type() == QgsProcessingParameterPointCloudLayer::typeName() )
181 {
183 }
184 else if ( mParameter->type() == QgsProcessingParameterMapLayer::typeName() )
185 {
186 QList<int> dataTypes;
187 dataTypes = static_cast<QgsProcessingParameterMapLayer *>( mParameter.get() )->dataTypes();
188
189 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::Vector ) ) )
191 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) )
193 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) ) )
195 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) ) )
197 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) ) )
199 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::Raster ) ) )
200 {
201 mayBeRaster = true;
203 }
204 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::Mesh ) ) )
206 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::PointCloud ) ) )
208 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::Annotation ) ) )
210 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorTile ) ) )
212 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::TiledScene ) ) )
214 if ( !filters )
215 filters = Qgis::LayerFilter::All;
216 }
217
218 QgsSettings settings;
219 if ( settings.value( u"Processing/Configuration/SHOW_CRS_DEF"_s, true ).toBool() )
220 mCombo->setShowCrs( true );
221
222 if ( filters )
223 mCombo->setFilters( filters );
224
225 // Check compatibility with virtualraster data provider
226 // see https://github.com/qgis/QGIS/issues/55890
227 if ( mayBeRaster && ( !mParameter->provider() || !mParameter->provider()->flags().testFlag( Qgis::ProcessingProviderFlag::CompatibleWithVirtualRaster ) ) )
228 {
229 mCombo->setExcludedProviders( mCombo->excludedProviders() << u"virtualraster"_s );
230 }
231
232 if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
233 {
234 mCombo->setAllowEmptyLayer( true );
235 mCombo->setLayer( nullptr );
236 }
237
238 connect( mCombo, &QgsMapLayerComboBox::layerChanged, this, &QgsProcessingMapLayerComboBox::onLayerChanged );
239 if ( mUseSelectionCheckBox )
240 connect( mUseSelectionCheckBox, &QCheckBox::toggled, this, [this] {
241 if ( !mBlockChangedSignal )
242 emit valueChanged();
243 } );
244
245 setLayout( vl );
246
247 setAcceptDrops( true );
248
249 onLayerChanged( mCombo->currentLayer() );
250}
251
252QgsProcessingMapLayerComboBox::~QgsProcessingMapLayerComboBox() = default;
253
254void QgsProcessingMapLayerComboBox::setLayer( QgsMapLayer *layer )
255{
256 if ( layer || mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
257 {
258 mCombo->setLayer( layer );
259 }
260}
261
262QgsMapLayer *QgsProcessingMapLayerComboBox::currentLayer()
263{
264 return mCombo->currentLayer();
265}
266
267QString QgsProcessingMapLayerComboBox::currentText()
268{
269 return mCombo->currentText();
270}
271
272void QgsProcessingMapLayerComboBox::setValue( const QVariant &value, QgsProcessingContext &context )
273{
274 if ( !value.isValid() && mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
275 {
276 setLayer( nullptr );
277 return;
278 }
279
280 QVariant val = value;
281 bool found = false;
282 bool selectedOnly = false;
283 bool iterate = false;
284 if ( val.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
285 {
286 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
287 val = fromVar.source;
288 selectedOnly = fromVar.selectedFeaturesOnly;
290 mFeatureLimit = fromVar.featureLimit;
291 mFilterExpression = fromVar.filterExpression;
292 mIsOverridingDefaultGeometryCheck = fromVar.flags & Qgis::ProcessingFeatureSourceDefinitionFlag::OverrideDefaultGeometryCheck;
293 mGeometryCheck = fromVar.geometryCheck;
294 }
295 else
296 {
297 mFeatureLimit = -1;
298 mFilterExpression.clear();
299 mIsOverridingDefaultGeometryCheck = false;
301 }
302
303 if ( val.userType() == qMetaTypeId<QgsProcessingRasterLayerDefinition>() )
304 {
305 QgsProcessingRasterLayerDefinition fromVar = qvariant_cast<QgsProcessingRasterLayerDefinition>( val );
306 val = fromVar.source;
307 mRasterReferenceScale = fromVar.referenceScale;
308 mRasterDpi = fromVar.dpi;
309 }
310 else
311 {
312 mRasterReferenceScale = 0;
313 mRasterDpi = 96;
314 }
315
316 if ( val.userType() == qMetaTypeId<QgsProperty>() )
317 {
319 {
320 val = val.value<QgsProperty>().staticValue();
321 }
322 else
323 {
324 val = val.value<QgsProperty>().valueAsString( context.expressionContext(), mParameter->defaultValueForGui().toString() );
325 }
326 }
327
328 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( val.value<QObject *>() );
329 if ( !layer && val.userType() == QMetaType::Type::QString )
330 {
331 layer = QgsProcessingUtils::mapLayerFromString( val.toString(), context, false );
332 }
333
334 if ( layer )
335 {
336 mBlockChangedSignal++;
337 QgsMapLayer *prevLayer = currentLayer();
338 setLayer( layer );
339 found = static_cast<bool>( currentLayer() );
340 bool changed = found && ( currentLayer() != prevLayer );
341 if ( found && mUseSelectionCheckBox )
342 {
343 const bool hasSelection = qobject_cast<QgsVectorLayer *>( layer ) && qobject_cast<QgsVectorLayer *>( layer )->selectedFeatureCount() > 0;
344 changed = changed | ( ( hasSelection && selectedOnly ) != mUseSelectionCheckBox->isChecked() );
345 if ( hasSelection )
346 {
347 mUseSelectionCheckBox->setEnabled( true );
348 mUseSelectionCheckBox->setChecked( selectedOnly );
349 }
350 else
351 {
352 mUseSelectionCheckBox->setChecked( false );
353 mUseSelectionCheckBox->setEnabled( false );
354 }
355
356 if ( mIterateButton )
357 {
358 mIterateButton->setChecked( iterate );
359 }
360 }
361 mBlockChangedSignal--;
362 if ( changed )
363 emit valueChanged(); // and ensure we only ever raise one
364 }
365
366 if ( !found )
367 {
368 const QString string = val.toString();
369 if ( mIterateButton )
370 mIterateButton->setChecked( iterate );
371
372 if ( !string.isEmpty() )
373 {
374 mBlockChangedSignal++;
375 if ( mCombo->findText( string ) < 0 )
376 {
377 QStringList additional = mCombo->additionalItems();
378 additional.append( string );
379 mCombo->setAdditionalItems( additional );
380 }
381 mCombo->setCurrentIndex( mCombo->findText( string ) ); // this may or may not throw a signal, so let's block it..
382 if ( mUseSelectionCheckBox )
383 {
384 mUseSelectionCheckBox->setChecked( false );
385 mUseSelectionCheckBox->setEnabled( false );
386 }
387 mBlockChangedSignal--;
388 if ( !mBlockChangedSignal )
389 emit valueChanged(); // and ensure we only ever raise one
390 }
391 else if ( mParameter->flags() & Qgis::ProcessingParameterFlag::Optional )
392 {
393 mCombo->setLayer( nullptr );
394 if ( mUseSelectionCheckBox )
395 {
396 mUseSelectionCheckBox->setChecked( false );
397 mUseSelectionCheckBox->setEnabled( false );
398 }
399 }
400 }
401}
402
403QVariant QgsProcessingMapLayerComboBox::value() const
404{
405 if ( isEditable() && mCombo->currentText() != mCombo->itemText( mCombo->currentIndex() ) )
406 return mCombo->currentText();
407
408 const bool iterate = mIterateButton && mIterateButton->isChecked();
409 const bool selectedOnly = mUseSelectionCheckBox && mUseSelectionCheckBox->isChecked();
410 if ( QgsMapLayer *layer = mCombo->currentLayer() )
411 {
412 if ( selectedOnly || iterate || mFeatureLimit != -1 || mIsOverridingDefaultGeometryCheck || !mFilterExpression.isEmpty() )
414 layer->id(),
415 selectedOnly,
416 mFeatureLimit,
419 mGeometryCheck,
420 mFilterExpression
421 );
422 else if ( mRasterReferenceScale != 0 )
423 return QgsProcessingRasterLayerDefinition( layer->id(), mRasterReferenceScale, mRasterDpi );
424 else
425 return layer->id();
426 }
427 else
428 {
429 if ( !mCombo->currentText().isEmpty() )
430 {
431 if ( selectedOnly || iterate || mFeatureLimit != -1 || mIsOverridingDefaultGeometryCheck || !mFilterExpression.isEmpty() )
433 mCombo->currentText(),
434 selectedOnly,
435 mFeatureLimit,
438 mGeometryCheck,
439 mFilterExpression
440 );
441 else if ( mRasterReferenceScale != 0 )
442 return QgsProcessingRasterLayerDefinition( mCombo->currentText(), mRasterReferenceScale, mRasterDpi );
443 else
444 return mCombo->currentText();
445 }
446 }
447 return QVariant();
448}
449
450void QgsProcessingMapLayerComboBox::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
451{
452 mBrowserModel = context.browserModel();
453 mCombo->setProject( context.project() );
454}
455
456void QgsProcessingMapLayerComboBox::setEditable( bool editable )
457{
458 mCombo->setEditable( editable );
459}
460
461bool QgsProcessingMapLayerComboBox::isEditable() const
462{
463 return mCombo->isEditable();
464}
465
466QgsMapLayer *QgsProcessingMapLayerComboBox::compatibleMapLayerFromMimeData( const QMimeData *data, bool &incompatibleLayerSelected ) const
467{
468 incompatibleLayerSelected = false;
470 for ( const QgsMimeDataUtils::Uri &u : uriList )
471 {
472 // is this uri from the current project?
473 if ( QgsMapLayer *layer = u.mapLayer() )
474 {
475 if ( mCombo->mProxyModel->acceptsLayer( layer ) )
476 return layer;
477 else
478 {
479 incompatibleLayerSelected = true;
480 return nullptr;
481 }
482 }
483 }
484 return nullptr;
485}
486
487
488QString QgsProcessingMapLayerComboBox::compatibleUriFromMimeData( const QMimeData *data ) const
489{
491 for ( const QgsMimeDataUtils::Uri &u : uriList )
492 {
493 if ( ( mParameter->type() == QgsProcessingParameterFeatureSource::typeName() || mParameter->type() == QgsProcessingParameterVectorLayer::typeName() ) && u.layerType == "vector"_L1 )
494 {
495 QList<int> dataTypes = mParameter->type() == QgsProcessingParameterFeatureSource::typeName()
496 ? static_cast<QgsProcessingParameterFeatureSource *>( mParameter.get() )->dataTypes()
497 : ( mParameter->type() == QgsProcessingParameterVectorLayer::typeName() ? static_cast<QgsProcessingParameterVectorLayer *>( mParameter.get() )->dataTypes()
498 : QList<int>() );
499 bool acceptable = false;
500 switch ( QgsWkbTypes::geometryType( u.wkbType ) )
501 {
503 acceptable = true;
504 break;
505
507 if ( dataTypes.isEmpty()
508 || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::Vector ) )
509 || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) )
510 || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) ) )
511 acceptable = true;
512 break;
513
515 if ( dataTypes.isEmpty()
516 || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::Vector ) )
517 || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) )
518 || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) ) )
519 acceptable = true;
520 break;
521
523 if ( dataTypes.isEmpty()
524 || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::Vector ) )
525 || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) )
526 || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) ) )
527 acceptable = true;
528 break;
529
531 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::Vector ) ) )
532 acceptable = true;
533 break;
534 }
535 if ( acceptable )
536 return u.providerKey != "ogr"_L1 ? QgsProcessingUtils::encodeProviderKeyAndUri( u.providerKey, u.uri ) : u.uri;
537 }
538 else if ( mParameter->type() == QgsProcessingParameterRasterLayer::typeName() && u.layerType == "raster"_L1 && u.providerKey == "gdal"_L1 )
539 return u.uri;
540 else if ( mParameter->type() == QgsProcessingParameterMeshLayer::typeName() && u.layerType == "mesh"_L1 && u.providerKey == "mdal"_L1 )
541 return u.uri;
542 else if ( mParameter->type() == QgsProcessingParameterMapLayer::typeName() )
543 {
544 QList<int> dataTypes = static_cast<QgsProcessingParameterMapLayer *>( mParameter.get() )->dataTypes();
545 if ( dataTypes.isEmpty() || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::MapLayer ) ) )
546 {
547 return u.uri;
548 }
549
550 if ( u.layerType == "vector"_L1 && u.providerKey == "ogr"_L1 )
551 {
552 switch ( QgsWkbTypes::geometryType( u.wkbType ) )
553 {
555 return u.uri;
556
558 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) ) )
559 return u.uri;
560 break;
561
563 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) ) )
564 return u.uri;
565 break;
566
568 if ( dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) || dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) ) )
569 return u.uri;
570 break;
571
573 return u.uri;
574 }
575 }
576 else if ( u.layerType == "raster"_L1 && u.providerKey == "gdal"_L1 && dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::Raster ) ) )
577 return u.uri;
578 else if ( u.layerType == "mesh"_L1 && u.providerKey == "mdal"_L1 && dataTypes.contains( static_cast<int>( Qgis::ProcessingSourceType::Mesh ) ) )
579 return u.uri;
580 }
581 }
582 if ( !uriList.isEmpty() )
583 return QString();
584
585 // second chance -- files dragged from file explorer, outside of QGIS
586 QStringList rawPaths;
587 if ( data->hasUrls() )
588 {
589 const QList<QUrl> urls = data->urls();
590 rawPaths.reserve( urls.count() );
591 for ( const QUrl &url : urls )
592 {
593 const QString local = url.toLocalFile();
594 if ( !rawPaths.contains( local ) )
595 rawPaths.append( local );
596 }
597 }
598 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
599 rawPaths.append( data->text() );
600
601 for ( const QString &path : std::as_const( rawPaths ) )
602 {
603 QFileInfo file( path );
604 if ( file.isFile() )
605 {
606 // TODO - we should check to see if it's a valid extension for the parameter, but that's non-trivial
607 return path;
608 }
609 }
610
611 return QString();
612}
613
614void QgsProcessingMapLayerComboBox::dragEnterEvent( QDragEnterEvent *event )
615{
616 if ( !( event->possibleActions() & Qt::CopyAction ) )
617 return;
618
619 bool incompatibleLayerSelected = false;
620 QgsMapLayer *layer = compatibleMapLayerFromMimeData( event->mimeData(), incompatibleLayerSelected );
621 const QString uri = compatibleUriFromMimeData( event->mimeData() );
622 if ( layer || ( !incompatibleLayerSelected && !uri.isEmpty() ) )
623 {
624 // dragged an acceptable layer, phew
625 event->setDropAction( Qt::CopyAction );
626 event->accept();
627 mDragActive = true;
628 mCombo->mHighlight = true;
629 update();
630 }
631}
632
633void QgsProcessingMapLayerComboBox::dragLeaveEvent( QDragLeaveEvent *event )
634{
635 QWidget::dragLeaveEvent( event );
636 if ( mDragActive )
637 {
638 event->accept();
639 mDragActive = false;
640 mCombo->mHighlight = false;
641 update();
642 }
643}
644
645void QgsProcessingMapLayerComboBox::dropEvent( QDropEvent *event )
646{
647 if ( !( event->possibleActions() & Qt::CopyAction ) )
648 return;
649
650 bool incompatibleLayerSelected = false;
651 QgsMapLayer *layer = compatibleMapLayerFromMimeData( event->mimeData(), incompatibleLayerSelected );
652 const QString uri = compatibleUriFromMimeData( event->mimeData() );
653 if ( layer || ( !incompatibleLayerSelected && !uri.isEmpty() ) )
654 {
655 // dropped an acceptable layer, phew
656 setFocus( Qt::MouseFocusReason );
657 event->setDropAction( Qt::CopyAction );
658 event->accept();
659 QgsProcessingContext context;
660 setValue( layer ? QVariant::fromValue( layer ) : QVariant::fromValue( uri ), context );
661 }
662 mDragActive = false;
663 mCombo->mHighlight = false;
664 update();
665}
666
667void QgsProcessingMapLayerComboBox::onLayerChanged( QgsMapLayer *layer )
668{
669 if ( mUseSelectionCheckBox && mParameter->type() == QgsProcessingParameterFeatureSource::typeName() )
670 {
671 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
672 {
673 if ( QgsVectorLayer *prevLayer = qobject_cast<QgsVectorLayer *>( mPrevLayer ) )
674 {
675 disconnect( prevLayer, &QgsVectorLayer::selectionChanged, this, &QgsProcessingMapLayerComboBox::selectionChanged );
676 }
677 if ( vl->selectedFeatureCount() == 0 )
678 mUseSelectionCheckBox->setChecked( false );
679 mUseSelectionCheckBox->setEnabled( vl->selectedFeatureCount() > 0 );
680 connect( vl, &QgsVectorLayer::selectionChanged, this, &QgsProcessingMapLayerComboBox::selectionChanged );
681 }
682 }
683
684 if ( mParameter->type() == QgsProcessingParameterRasterLayer::typeName() )
685 {
686 // Only WMS layers whose parameter supports WmsScale and WmsDpi will access raster advanced options for now.
687 QgsProcessingParameterRasterLayer *rasterParameter = qgis::down_cast<QgsProcessingParameterRasterLayer *>( mParameter.get() );
688 const bool supportsRasterOptions = rasterParameter->parameterCapabilities().testFlag( Qgis::RasterProcessingParameterCapability::WmsScale )
690 mSettingsButton->setEnabled( supportsRasterOptions && QgsWmsUtils::isWmsLayer( layer ) );
691 }
692
693 mPrevLayer = layer;
694 if ( !mBlockChangedSignal )
695 emit valueChanged();
696}
697
698void QgsProcessingMapLayerComboBox::selectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &, bool )
699{
700 if ( selected.isEmpty() )
701 mUseSelectionCheckBox->setChecked( false );
702 mUseSelectionCheckBox->setEnabled( !selected.isEmpty() );
703}
704
705void QgsProcessingMapLayerComboBox::showSourceOptions()
706{
707 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
708 {
709 QgsProcessingFeatureSourceOptionsWidget *widget = new QgsProcessingFeatureSourceOptionsWidget();
710 widget->setPanelTitle( tr( "%1 Options" ).arg( mParameter->description() ) );
711 widget->setLayer( qobject_cast<QgsVectorLayer *>( mCombo->currentLayer() ) );
712
713 widget->setGeometryCheckMethod( mIsOverridingDefaultGeometryCheck, mGeometryCheck );
714 widget->setFeatureLimit( mFeatureLimit );
715 widget->setFilterExpression( mFilterExpression );
716
717 panel->openPanel( widget );
718
719 connect( widget, &QgsPanelWidget::widgetChanged, this, [this, widget] {
720 bool changed = false;
721 changed = changed | ( widget->featureLimit() != mFeatureLimit );
722 changed = changed | ( widget->filterExpression() != mFilterExpression );
723 changed = changed | ( widget->isOverridingInvalidGeometryCheck() != mIsOverridingDefaultGeometryCheck );
724 changed = changed | ( widget->geometryCheckMethod() != mGeometryCheck );
725
726 mFeatureLimit = widget->featureLimit();
727 mFilterExpression = widget->filterExpression();
728 mIsOverridingDefaultGeometryCheck = widget->isOverridingInvalidGeometryCheck();
729 mGeometryCheck = widget->geometryCheckMethod();
730
731 if ( changed )
732 emit valueChanged();
733 } );
734 }
735}
736
737void QgsProcessingMapLayerComboBox::showRasterSourceOptions()
738{
739 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
740 {
741 QgsProcessingRasterSourceOptionsWidget *widget = new QgsProcessingRasterSourceOptionsWidget();
742 widget->setPanelTitle( tr( "%1 Options" ).arg( mParameter->description() ) );
743 widget->setReferenceScale( mRasterReferenceScale );
744 widget->setDpi( mRasterDpi );
745
746 QgsProcessingParameterRasterLayer *rasterParameter = qgis::down_cast<QgsProcessingParameterRasterLayer *>( mParameter.get() );
747 widget->setWidgetParameterCapabilities( rasterParameter->parameterCapabilities() );
748
749 panel->openPanel( widget );
750
751 connect( widget, &QgsPanelWidget::widgetChanged, this, [this, widget] {
752 bool changed = false;
753 changed = changed | ( widget->referenceScale() != mRasterReferenceScale );
754 changed = changed | ( widget->dpi() != mRasterDpi );
755
756 mRasterReferenceScale = widget->referenceScale();
757 mRasterDpi = widget->dpi();
758
759 if ( changed )
760 emit valueChanged();
761 } );
762 }
763}
764
765void QgsProcessingMapLayerComboBox::selectFromFile()
766{
767 QgsSettings settings;
768 const QString initialValue = currentText();
769 QString path;
770
771 if ( QFileInfo( initialValue ).isDir() && QFileInfo::exists( initialValue ) )
772 path = initialValue;
773 else if ( QFileInfo::exists( QFileInfo( initialValue ).path() ) && QFileInfo( initialValue ).path() != '.' )
774 path = QFileInfo( initialValue ).path();
775 else if ( settings.contains( u"/Processing/LastInputPath"_s ) )
776 path = settings.value( u"/Processing/LastInputPath"_s ).toString();
777
778 QString filter;
779 if ( const QgsFileFilterGenerator *generator = dynamic_cast<const QgsFileFilterGenerator *>( mParameter.get() ) )
780 filter = generator->createFileFilter();
781 else
782 filter = QObject::tr( "All files (*.*)" );
783
784 const QString filename = QFileDialog::getOpenFileName( this, tr( "Select File" ), path, filter );
785 if ( filename.isEmpty() )
786 return;
787
788 settings.setValue( u"/Processing/LastInputPath"_s, QFileInfo( filename ).path() );
789 QgsProcessingContext context;
790 setValue( filename, context );
791}
792
793void QgsProcessingMapLayerComboBox::browseForLayer()
794{
795 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ) )
796 {
798 widget->setPanelTitle( tr( "Browse for \"%1\"" ).arg( mParameter->description() ) );
799
800 panel->openPanel( widget );
801
802 connect( widget, &QgsDataSourceSelectWidget::itemTriggered, this, [widget]( const QgsMimeDataUtils::Uri & ) { widget->acceptPanel(); } );
803 connect( widget, &QgsPanelWidget::panelAccepted, this, [this, widget]() {
804 QgsProcessingContext context;
805 if ( widget->uri().uri.isEmpty() )
806 setValue( QVariant(), context );
807 else if ( widget->uri().providerKey == "ogr"_L1 )
808 setValue( widget->uri().uri, context );
809 else
810 setValue( QgsProcessingUtils::encodeProviderKeyAndUri( widget->uri().providerKey, widget->uri().uri ), context );
811 } );
812 }
813}
814
815
@ TiledScene
Tiled scene layers.
Definition qgis.h:3659
@ Annotation
Annotation layers.
Definition qgis.h:3657
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition qgis.h:3653
@ VectorTile
Vector tile layers.
Definition qgis.h:3658
@ MapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer).
Definition qgis.h:3646
@ Mesh
Mesh layers.
Definition qgis.h:3654
@ Raster
Raster layers.
Definition qgis.h:3651
@ VectorAnyGeometry
Any vector layer with geometry.
Definition qgis.h:3647
@ VectorPoint
Vector point layers.
Definition qgis.h:3648
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3650
@ VectorLine
Vector line layers.
Definition qgis.h:3649
@ PointCloud
Point cloud layers.
Definition qgis.h:3656
@ WmsDpi
The parameter supports a server resolution for WMS source layers.
Definition qgis.h:6483
@ WmsScale
The parameter supports a reference scale for WMS source layers.
Definition qgis.h:6482
ProcessingMode
Types of modes which Processing widgets can be created for.
Definition qgis.h:3786
@ Standard
Standard (single-run) algorithm mode.
Definition qgis.h:3787
@ CompatibleWithVirtualRaster
The processing provider's algorithms can work with QGIS virtualraster data provider.
Definition qgis.h:3675
@ Static
Static property.
Definition qgis.h:710
@ PointCloudLayer
QgsPointCloudLayer.
Definition qgis.h:237
@ MeshLayer
QgsMeshLayer.
Definition qgis.h:235
@ VectorTileLayer
QgsVectorTileLayer.
Definition qgis.h:236
@ AnnotationLayer
QgsAnnotationLayer.
Definition qgis.h:238
@ All
All layers.
Definition qgis.h:240
@ TiledSceneLayer
QgsTiledSceneLayer.
Definition qgis.h:239
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
@ Unknown
Unknown types.
Definition qgis.h:383
@ Null
No geometry.
Definition qgis.h:384
@ Vector
Vector layer.
Definition qgis.h:207
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
Definition qgis.h:2322
QFlags< ProcessingFeatureSourceDefinitionFlag > ProcessingFeatureSourceDefinitionFlags
Flags which control behavior for a Processing feature source.
Definition qgis.h:3816
@ CreateIndividualOutputPerInputFeature
If set, every feature processed from this source will be placed into its own individually created out...
Definition qgis.h:3804
@ OverrideDefaultGeometryCheck
If set, the default geometry check method (as dictated by QgsProcessingContext) will be overridden fo...
Definition qgis.h:3802
@ Optional
Parameter is optional.
Definition qgis.h:3882
QFlags< LayerFilter > LayerFilters
Definition qgis.h:243
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
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.
A combobox which displays a dynamic list of layers from a QGIS project.
void layerChanged(QgsMapLayer *layer)
Emitted whenever the currently selected layer changes.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString id
Definition qgsmaplayer.h:86
QList< QgsMimeDataUtils::Uri > UriList
static UriList decodeUriList(const QMimeData *data)
Base class for any widget that can be shown as an 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.
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.
QList< int > dataTypes() const
Returns the geometry types for sources acceptable by the parameter.
A map layer parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
A raster layer parameter for processing algorithms.
Qgis::RasterProcessingParameterCapabilities parameterCapabilities() const
Returns flags containing the supported capabilities of the raster layer parameter.
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.
Encapsulates settings relating to a raster layer input to a processing algorithm.
double referenceScale
If set to a value > 0, sets a scale at which a raster (e.g., a WMS) should be requested or rendered.
int dpi
Indicates the resolution of the raster source (e.g., a WMS server).
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.
Qgis::PropertyType propertyType() const
Returns the property type.
Stores settings for use within QGIS.
Definition qgssettings.h:68
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 dataset.
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...
static bool isWmsLayer(QgsMapLayer *layer)
Returns whether a map layer is an OGC WMS layer or not.
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
QString uri
Identifier of the data source recognized by its providerKey.
QString providerKey
For "vector" / "raster" type: provider id.