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