QGIS API Documentation 4.3.0-Master (bf28115e945)
Loading...
Searching...
No Matches
qgsprocessingwidgetwrapperimpl.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingwidgetwrapperimpl.cpp
3 ---------------------
4 begin : August 2018
5 copyright : (C) 2018 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include <memory>
21
23#include "qgsannotationlayer.h"
24#include "qgsapplication.h"
25#include "qgsauthconfigselect.h"
27#include "qgscolorbutton.h"
31#include "qgsdatetimeedit.h"
33#include "qgsdoublespinbox.h"
34#include "qgsdoublevalidator.h"
38#include "qgsextentwidget.h"
39#include "qgsfieldcombobox.h"
41#include "qgsfileutils.h"
42#include "qgsfilewidget.h"
43#include "qgsfilterlineedit.h"
44#include "qgsgeometrywidget.h"
45#include "qgslayoutcombobox.h"
47#include "qgslayoutmanager.h"
48#include "qgsmapcanvas.h"
49#include "qgsmaplayercombobox.h"
50#include "qgsmapmouseevent.h"
52#include "qgsmessagebar.h"
54#include "qgspointcloudlayer.h"
55#include "qgsprintlayout.h"
70#include "qgsproject.h"
74#include "qgsscalewidget.h"
75#include "qgssettings.h"
76#include "qgssnapindicator.h"
77#include "qgsspinbox.h"
78#include "qgsunittypes.h"
79
80#include <QButtonGroup>
81#include <QCheckBox>
82#include <QComboBox>
83#include <QFileDialog>
84#include <QHBoxLayout>
85#include <QInputDialog>
86#include <QLabel>
87#include <QLineEdit>
88#include <QMenu>
89#include <QMessageBox>
90#include <QPlainTextEdit>
91#include <QRadioButton>
92#include <QString>
93#include <QToolButton>
94#include <QVBoxLayout>
95
96#include "moc_qgsprocessingwidgetwrapperimpl.cpp"
97
98using namespace Qt::StringLiterals;
99
101
102//
103// QgsProcessingBooleanWidgetWrapper
104//
105
106
107QgsProcessingBooleanParameterDefinitionWidget::QgsProcessingBooleanParameterDefinitionWidget(
108 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
109)
110 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
111{
112 QVBoxLayout *vlayout = new QVBoxLayout();
113 vlayout->setContentsMargins( 0, 0, 0, 0 );
114
115 mDefaultCheckBox = new QCheckBox( tr( "Checked" ) );
116 if ( const QgsProcessingParameterBoolean *boolParam = dynamic_cast<const QgsProcessingParameterBoolean *>( definition ) )
117 mDefaultCheckBox->setChecked( QgsProcessingParameters::parameterAsBool( boolParam, boolParam->defaultValueForGui(), context ) );
118 else
119 mDefaultCheckBox->setChecked( false );
120 vlayout->addWidget( mDefaultCheckBox );
121 connect( mDefaultCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
122
123 setLayout( vlayout );
124}
125
126QgsProcessingParameterDefinition *QgsProcessingBooleanParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
127{
128 auto param = std::make_unique<QgsProcessingParameterBoolean>( name, description, mDefaultCheckBox->isChecked() );
129 param->setFlags( flags );
130 return param.release();
131}
132
133
134QgsProcessingBooleanWidgetWrapper::QgsProcessingBooleanWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
135 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
136{}
137
138QWidget *QgsProcessingBooleanWidgetWrapper::createWidget()
139{
140 switch ( type() )
141 {
143 {
144 QString description = parameterDefinition()->description();
145 if ( parameterDefinition()->flags() & Qgis::ProcessingParameterFlag::Optional )
146 description = QObject::tr( "%1 [optional]" ).arg( description );
147
148 mCheckBox = new QCheckBox( description );
149 mCheckBox->setToolTip( parameterDefinition()->toolTip() );
150
151 connect( mCheckBox, &QCheckBox::toggled, this, [this] { emit widgetValueHasChanged( this ); } );
152 return mCheckBox;
153 }
154
157 {
158 mComboBox = new QComboBox();
159 mComboBox->addItem( tr( "Yes" ), true );
160 mComboBox->addItem( tr( "No" ), false );
161 mComboBox->setToolTip( parameterDefinition()->toolTip() );
162
163 connect( mComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this] { emit widgetValueHasChanged( this ); } );
164
165 return mComboBox;
166 }
167 }
168 return nullptr;
169}
170
171QLabel *QgsProcessingBooleanWidgetWrapper::createLabel()
172{
173 // avoid creating labels in standard dialogs
174 if ( type() == Qgis::ProcessingMode::Standard )
175 return nullptr;
176 else
178}
179
180void QgsProcessingBooleanWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
181{
182 switch ( type() )
183 {
185 {
186 const bool v = QgsProcessingParameters::parameterAsBool( parameterDefinition(), value, context );
187 mCheckBox->setChecked( v );
188 break;
189 }
190
193 {
194 const bool v = QgsProcessingParameters::parameterAsBool( parameterDefinition(), value, context );
195 mComboBox->setCurrentIndex( mComboBox->findData( v ) );
196 break;
197 }
198 }
199}
200
201QVariant QgsProcessingBooleanWidgetWrapper::widgetValue() const
202{
203 switch ( type() )
204 {
206 return mCheckBox->isChecked();
207
210 return mComboBox->currentData();
211 }
212 return QVariant();
213}
214
215QString QgsProcessingBooleanWidgetWrapper::parameterType() const
216{
218}
219
220QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingBooleanWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
221{
222 return new QgsProcessingBooleanWidgetWrapper( parameter, type );
223}
224
225QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingBooleanWidgetWrapper::createParameterDefinitionWidget(
227)
228{
229 return new QgsProcessingBooleanParameterDefinitionWidget( context, widgetContext, definition, algorithm );
230}
231
232
233//
234// QgsProcessingCrsWidgetWrapper
235//
236
237QgsProcessingCrsParameterDefinitionWidget::QgsProcessingCrsParameterDefinitionWidget(
238 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
239)
240 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
241{
242 QVBoxLayout *vlayout = new QVBoxLayout();
243 vlayout->setContentsMargins( 0, 0, 0, 0 );
244
245 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
246
247 mCrsSelector = new QgsProjectionSelectionWidget();
248
249 // possibly we should expose this for parameter by parameter control
250 mCrsSelector->setShowAccuracyWarnings( true );
251
252 if ( const QgsProcessingParameterCrs *crsParam = dynamic_cast<const QgsProcessingParameterCrs *>( definition ) )
253 mCrsSelector->setCrs( QgsProcessingParameters::parameterAsCrs( crsParam, crsParam->defaultValueForGui(), context ) );
254 else
255 mCrsSelector->setCrs( QgsCoordinateReferenceSystem( u"EPSG:4326"_s ) );
256
257 vlayout->addWidget( mCrsSelector );
258 setLayout( vlayout );
259}
260
261QgsProcessingParameterDefinition *QgsProcessingCrsParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
262{
263 auto param = std::make_unique<QgsProcessingParameterCrs>( name, description, mCrsSelector->crs().authid() );
264 param->setFlags( flags );
265 return param.release();
266}
267
268QgsProcessingCrsWidgetWrapper::QgsProcessingCrsWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
269 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
270{}
271
272QWidget *QgsProcessingCrsWidgetWrapper::createWidget()
273{
274 Q_ASSERT( mProjectionSelectionWidget == nullptr );
275 mProjectionSelectionWidget = new QgsProjectionSelectionWidget();
276 mProjectionSelectionWidget->setToolTip( parameterDefinition()->toolTip() );
277
278 if ( parameterDefinition()->flags() & Qgis::ProcessingParameterFlag::Optional )
279 mProjectionSelectionWidget->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, true );
280 else
281 mProjectionSelectionWidget->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, false );
282
283 connect( mProjectionSelectionWidget, &QgsProjectionSelectionWidget::crsChanged, this, [this] { emit widgetValueHasChanged( this ); } );
284
285 switch ( type() )
286 {
289 {
290 return mProjectionSelectionWidget;
291 }
292
294 {
295 QWidget *w = new QWidget();
296 w->setToolTip( parameterDefinition()->toolTip() );
297
298 QVBoxLayout *vl = new QVBoxLayout();
299 vl->setContentsMargins( 0, 0, 0, 0 );
300 w->setLayout( vl );
301
302 mUseProjectCrsCheckBox = new QCheckBox( tr( "Use project CRS" ) );
303 mUseProjectCrsCheckBox->setToolTip( tr( "Always use the current project CRS when running the model" ) );
304 vl->addWidget( mUseProjectCrsCheckBox );
305 connect( mUseProjectCrsCheckBox, &QCheckBox::toggled, mProjectionSelectionWidget, &QgsProjectionSelectionWidget::setDisabled );
306 connect( mUseProjectCrsCheckBox, &QCheckBox::toggled, this, [this] { emit widgetValueHasChanged( this ); } );
307
308 vl->addWidget( mProjectionSelectionWidget );
309
310 return w;
311 }
312 }
313 return nullptr;
314}
315
316void QgsProcessingCrsWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
317{
318 if ( mUseProjectCrsCheckBox )
319 {
320 if ( value.toString().compare( "ProjectCrs"_L1, Qt::CaseInsensitive ) == 0 )
321 {
322 mUseProjectCrsCheckBox->setChecked( true );
323 return;
324 }
325 else
326 {
327 mUseProjectCrsCheckBox->setChecked( false );
328 }
329 }
330
331 const QgsCoordinateReferenceSystem v = QgsProcessingParameters::parameterAsCrs( parameterDefinition(), value, context );
332 if ( mProjectionSelectionWidget )
333 mProjectionSelectionWidget->setCrs( v );
334}
335
336QVariant QgsProcessingCrsWidgetWrapper::widgetValue() const
337{
338 if ( mUseProjectCrsCheckBox && mUseProjectCrsCheckBox->isChecked() )
339 return u"ProjectCrs"_s;
340 else if ( mProjectionSelectionWidget )
341 return mProjectionSelectionWidget->crs().isValid() ? mProjectionSelectionWidget->crs() : QVariant();
342 else
343 return QVariant();
344}
345
346QString QgsProcessingCrsWidgetWrapper::modelerExpressionFormatString() const
347{
348 return tr( "string as EPSG code, WKT or PROJ format, or a string identifying a map layer" );
349}
350
351QString QgsProcessingCrsWidgetWrapper::parameterType() const
352{
354}
355
356QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingCrsWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
357{
358 return new QgsProcessingCrsWidgetWrapper( parameter, type );
359}
360
361QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingCrsWidgetWrapper::createParameterDefinitionWidget(
363)
364{
365 return new QgsProcessingCrsParameterDefinitionWidget( context, widgetContext, definition, algorithm );
366}
367
368
369//
370// QgsProcessingStringWidgetWrapper
371//
372
373
374QgsProcessingStringParameterDefinitionWidget::QgsProcessingStringParameterDefinitionWidget(
375 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
376)
377 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
378{
379 QVBoxLayout *vlayout = new QVBoxLayout();
380 vlayout->setContentsMargins( 0, 0, 0, 0 );
381
382 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
383
384 mDefaultLineEdit = new QLineEdit();
385 if ( const QgsProcessingParameterString *stringParam = dynamic_cast<const QgsProcessingParameterString *>( definition ) )
386 mDefaultLineEdit->setText( QgsProcessingParameters::parameterAsString( stringParam, stringParam->defaultValueForGui(), context ) );
387 vlayout->addWidget( mDefaultLineEdit );
388
389 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
390
391 mMultiLineCheckBox = new QCheckBox( tr( "Multiline input" ) );
392 if ( const QgsProcessingParameterString *stringParam = dynamic_cast<const QgsProcessingParameterString *>( definition ) )
393 mMultiLineCheckBox->setChecked( stringParam->multiLine() );
394 vlayout->addWidget( mMultiLineCheckBox );
395
396 connect( mMultiLineCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
397
398 setLayout( vlayout );
399}
400
401QgsProcessingParameterDefinition *QgsProcessingStringParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
402{
403 auto param = std::make_unique<QgsProcessingParameterString>( name, description, mDefaultLineEdit->text(), mMultiLineCheckBox->isChecked() );
404 param->setFlags( flags );
405 return param.release();
406}
407
408
409QgsProcessingStringWidgetWrapper::QgsProcessingStringWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
410 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
411{}
412
413QWidget *QgsProcessingStringWidgetWrapper::createWidget()
414{
415 const QVariantMap metadata = parameterDefinition()->metadata();
416 const QVariant valueHintsVariant = metadata.value( u"widget_wrapper"_s ).toMap().value( u"value_hints"_s );
417
418 if ( valueHintsVariant.isValid() )
419 {
420 const QVariantList valueList = valueHintsVariant.toList();
421 mComboBox = new QComboBox();
422 mComboBox->setToolTip( parameterDefinition()->toolTip() );
423
424 if ( parameterDefinition()->flags() & Qgis::ProcessingParameterFlag::Optional )
425 {
426 mComboBox->addItem( QString() );
427 }
428 for ( const QVariant &entry : valueList )
429 {
430 mComboBox->addItem( entry.toString(), entry.toString() );
431 }
432 mComboBox->setCurrentIndex( 0 );
433
434 connect( mComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this]( int ) { emit widgetValueHasChanged( this ); } );
435 return mComboBox;
436 }
437 else
438 {
439 switch ( type() )
440 {
443 {
444 if ( static_cast<const QgsProcessingParameterString *>( parameterDefinition() )->multiLine() )
445 {
446 mPlainTextEdit = new QPlainTextEdit();
447 mPlainTextEdit->setToolTip( parameterDefinition()->toolTip() );
448
449 connect( mPlainTextEdit, &QPlainTextEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
450 return mPlainTextEdit;
451 }
452 else
453 {
454 mLineEdit = new QLineEdit();
455 mLineEdit->setToolTip( parameterDefinition()->toolTip() );
456
457 connect( mLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
458 return mLineEdit;
459 }
460 }
461
463 {
464 mLineEdit = new QLineEdit();
465 mLineEdit->setToolTip( parameterDefinition()->toolTip() );
466
467 connect( mLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
468 return mLineEdit;
469 }
470 }
471 }
472
473 return nullptr;
474}
475
476void QgsProcessingStringWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
477{
478 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
479 if ( mLineEdit )
480 mLineEdit->setText( v );
481 if ( mPlainTextEdit )
482 mPlainTextEdit->setPlainText( v );
483 if ( mComboBox )
484 {
485 int index = -1;
486 if ( !value.isValid() )
487 index = mComboBox->findData( QVariant() );
488 else
489 index = mComboBox->findData( v );
490
491 if ( index >= 0 )
492 mComboBox->setCurrentIndex( index );
493 else
494 mComboBox->setCurrentIndex( 0 );
495 }
496}
497
498QVariant QgsProcessingStringWidgetWrapper::widgetValue() const
499{
500 if ( mLineEdit )
501 return mLineEdit->text();
502 else if ( mPlainTextEdit )
503 return mPlainTextEdit->toPlainText();
504 else if ( mComboBox )
505 return mComboBox->currentData();
506 else
507 return QVariant();
508}
509
510QString QgsProcessingStringWidgetWrapper::parameterType() const
511{
513}
514
515QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingStringWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
516{
517 return new QgsProcessingStringWidgetWrapper( parameter, type );
518}
519
520QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingStringWidgetWrapper::createParameterDefinitionWidget(
522)
523{
524 return new QgsProcessingStringParameterDefinitionWidget( context, widgetContext, definition, algorithm );
525}
526
527
528//
529// QgsProcessingAuthConfigWidgetWrapper
530//
531
532QgsProcessingAuthConfigWidgetWrapper::QgsProcessingAuthConfigWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
533 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
534{}
535
536QWidget *QgsProcessingAuthConfigWidgetWrapper::createWidget()
537{
538 switch ( type() )
539 {
543 {
544 mAuthConfigSelect = new QgsAuthConfigSelect();
545 mAuthConfigSelect->setToolTip( parameterDefinition()->toolTip() );
546
547 connect( mAuthConfigSelect, &QgsAuthConfigSelect::selectedConfigIdChanged, this, [this] { emit widgetValueHasChanged( this ); } );
548 return mAuthConfigSelect;
549 }
550 }
551 return nullptr;
552}
553
554void QgsProcessingAuthConfigWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
555{
556 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
557 if ( mAuthConfigSelect )
558 mAuthConfigSelect->setConfigId( v );
559}
560
561QVariant QgsProcessingAuthConfigWidgetWrapper::widgetValue() const
562{
563 if ( mAuthConfigSelect )
564 return mAuthConfigSelect->configId();
565 else
566 return QVariant();
567}
568
569QString QgsProcessingAuthConfigWidgetWrapper::parameterType() const
570{
572}
573
574QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingAuthConfigWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
575{
576 return new QgsProcessingAuthConfigWidgetWrapper( parameter, type );
577}
578
579//
580// QgsProcessingNumericWidgetWrapper
581//
582
583QgsProcessingNumberParameterDefinitionWidget::QgsProcessingNumberParameterDefinitionWidget(
584 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
585)
586 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
587{
588 QVBoxLayout *vlayout = new QVBoxLayout();
589 vlayout->setContentsMargins( 0, 0, 0, 0 );
590
591 vlayout->addWidget( new QLabel( tr( "Number type" ) ) );
592
593 mTypeComboBox = new QComboBox();
594 mTypeComboBox->addItem( tr( "Float" ), static_cast<int>( Qgis::ProcessingNumberParameterType::Double ) );
595 mTypeComboBox->addItem( tr( "Integer" ), static_cast<int>( Qgis::ProcessingNumberParameterType::Integer ) );
596 vlayout->addWidget( mTypeComboBox );
597
598 vlayout->addWidget( new QLabel( tr( "Minimum value" ) ) );
599 mMinLineEdit = new QLineEdit();
600 vlayout->addWidget( mMinLineEdit );
601
602 vlayout->addWidget( new QLabel( tr( "Maximum value" ) ) );
603 mMaxLineEdit = new QLineEdit();
604 vlayout->addWidget( mMaxLineEdit );
605
606 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
607 mDefaultLineEdit = new QLineEdit();
608 vlayout->addWidget( mDefaultLineEdit );
609
610 if ( const QgsProcessingParameterNumber *numberParam = dynamic_cast<const QgsProcessingParameterNumber *>( definition ) )
611 {
612 mTypeComboBox->setCurrentIndex( mTypeComboBox->findData( static_cast<int>( numberParam->dataType() ) ) );
613
614 if ( !qgsDoubleNear( numberParam->maximum(), std::numeric_limits<double>::max() ) )
615 {
616 mMaxLineEdit->setText( QLocale().toString( numberParam->maximum() ) );
617 }
618 else
619 {
620 mMaxLineEdit->clear();
621 }
622
623 if ( !qgsDoubleNear( numberParam->minimum(), std::numeric_limits<double>::lowest() ) )
624 {
625 mMinLineEdit->setText( QLocale().toString( numberParam->minimum() ) );
626 }
627 else
628 {
629 mMinLineEdit->clear();
630 }
631
632 mDefaultLineEdit->setText( numberParam->defaultValueForGui().toString() );
633 }
634
635 connect( mTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
636 connect( mMinLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
637 connect( mMaxLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
638 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
639
640 setLayout( vlayout );
641}
642
643QgsProcessingParameterDefinition *QgsProcessingNumberParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
644{
645 bool ok;
646 double val = QgsDoubleValidator::toDouble( mDefaultLineEdit->text(), &ok );
647
648 Qgis::ProcessingNumberParameterType dataType = static_cast<Qgis::ProcessingNumberParameterType>( mTypeComboBox->currentData().toInt() );
649 auto param = std::make_unique<QgsProcessingParameterNumber>( name, description, dataType, ok ? val : QVariant() );
650
651 if ( !mMinLineEdit->text().trimmed().isEmpty() )
652 {
653 val = QgsDoubleValidator::toDouble( mMinLineEdit->text(), &ok );
654 if ( ok )
655 {
656 param->setMinimum( val );
657 }
658 }
659
660 if ( !mMaxLineEdit->text().trimmed().isEmpty() )
661 {
662 val = QgsDoubleValidator::toDouble( mMaxLineEdit->text(), &ok );
663 if ( ok )
664 {
665 param->setMaximum( val );
666 }
667 }
668
669 param->setFlags( flags );
670 return param.release();
671}
672
673QgsProcessingNumericWidgetWrapper::QgsProcessingNumericWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
674 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
675{}
676
677QWidget *QgsProcessingNumericWidgetWrapper::createWidget()
678{
679 const QgsProcessingParameterNumber *numberDef = static_cast<const QgsProcessingParameterNumber *>( parameterDefinition() );
680 const QVariantMap metadata = numberDef->metadata();
681 const int decimals = metadata.value( u"widget_wrapper"_s ).toMap().value( u"decimals"_s, 6 ).toInt();
682 switch ( type() )
683 {
687 {
688 // lots of duplicate code here -- but there's no common interface between QSpinBox/QDoubleSpinBox which would allow us to avoid this
689 QAbstractSpinBox *spinBox = nullptr;
690 switch ( numberDef->dataType() )
691 {
693 mDoubleSpinBox = new QgsDoubleSpinBox();
694 mDoubleSpinBox->setExpressionsEnabled( true );
695 mDoubleSpinBox->setDecimals( decimals );
696
697 // guess reasonable step value for double spin boxes
698 if ( !qgsDoubleNear( numberDef->maximum(), std::numeric_limits<double>::max() ) && !qgsDoubleNear( numberDef->minimum(), std::numeric_limits<double>::lowest() + 1 ) )
699 {
700 double singleStep = calculateStep( numberDef->minimum(), numberDef->maximum() );
701 singleStep = std::max( singleStep, std::pow( 10, -decimals ) );
702 mDoubleSpinBox->setSingleStep( singleStep );
703 }
704
705 spinBox = mDoubleSpinBox;
706 break;
707
709 mSpinBox = new QgsSpinBox();
710 mSpinBox->setExpressionsEnabled( true );
711 spinBox = mSpinBox;
712 break;
713 }
714 spinBox->setToolTip( parameterDefinition()->toolTip() );
715
716 double max = 999999999;
717 if ( !qgsDoubleNear( numberDef->maximum(), std::numeric_limits<double>::max() ) )
718 {
719 max = numberDef->maximum();
720 }
721 double min = -999999999;
722 if ( !qgsDoubleNear( numberDef->minimum(), std::numeric_limits<double>::lowest() ) )
723 {
724 min = numberDef->minimum();
725 }
726 if ( mDoubleSpinBox )
727 {
728 mDoubleSpinBox->setMinimum( min );
729 mDoubleSpinBox->setMaximum( max );
730 }
731 else
732 {
733 mSpinBox->setMinimum( static_cast<int>( min ) );
734 mSpinBox->setMaximum( static_cast<int>( max ) );
735 }
736
738 {
739 mAllowingNull = true;
740 if ( mDoubleSpinBox )
741 {
742 mDoubleSpinBox->setShowClearButton( true );
743 const double min = mDoubleSpinBox->minimum() - mDoubleSpinBox->singleStep();
744 mDoubleSpinBox->setMinimum( min );
745 mDoubleSpinBox->setValue( min );
746 }
747 else
748 {
749 mSpinBox->setShowClearButton( true );
750 const int min = mSpinBox->minimum() - 1;
751 mSpinBox->setMinimum( min );
752 mSpinBox->setValue( min );
753 }
754 spinBox->setSpecialValueText( tr( "Not set" ) );
755 }
756 else
757 {
758 if ( numberDef->defaultValueForGui().isValid() )
759 {
760 // if default value for parameter, we clear to that
761 bool ok = false;
762 if ( mDoubleSpinBox )
763 {
764 double defaultVal = numberDef->defaultValueForGui().toDouble( &ok );
765 if ( ok )
766 mDoubleSpinBox->setClearValue( defaultVal );
767 }
768 else
769 {
770 int intVal = numberDef->defaultValueForGui().toInt( &ok );
771 if ( ok )
772 mSpinBox->setClearValue( intVal );
773 }
774 }
775 else if ( !qgsDoubleNear( numberDef->minimum(), std::numeric_limits<double>::lowest() ) )
776 {
777 // otherwise we clear to the minimum, if it's set
778 if ( mDoubleSpinBox )
779 mDoubleSpinBox->setClearValue( numberDef->minimum() );
780 else
781 mSpinBox->setClearValue( static_cast<int>( numberDef->minimum() ) );
782 }
783 else
784 {
785 // last resort, we clear to 0
786 if ( mDoubleSpinBox )
787 {
788 mDoubleSpinBox->setValue( 0 );
789 mDoubleSpinBox->setClearValue( 0 );
790 }
791 else
792 {
793 mSpinBox->setValue( 0 );
794 mSpinBox->setClearValue( 0 );
795 }
796 }
797 }
798
799 if ( mDoubleSpinBox )
800 connect( mDoubleSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, [this] { emit widgetValueHasChanged( this ); } );
801 else if ( mSpinBox )
802 connect( mSpinBox, qOverload<int>( &QgsSpinBox::valueChanged ), this, [this] { emit widgetValueHasChanged( this ); } );
803
804 return spinBox;
805 }
806 }
807 return nullptr;
808}
809
810void QgsProcessingNumericWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
811{
812 if ( mDoubleSpinBox )
813 {
814 if ( mAllowingNull && !value.isValid() )
815 mDoubleSpinBox->clear();
816 else
817 {
818 const double v = QgsProcessingParameters::parameterAsDouble( parameterDefinition(), value, context );
819 mDoubleSpinBox->setValue( v );
820 }
821 }
822 else if ( mSpinBox )
823 {
824 if ( mAllowingNull && !value.isValid() )
825 mSpinBox->clear();
826 else
827 {
828 const int v = QgsProcessingParameters::parameterAsInt( parameterDefinition(), value, context );
829 mSpinBox->setValue( v );
830 }
831 }
832}
833
834QVariant QgsProcessingNumericWidgetWrapper::widgetValue() const
835{
836 if ( mDoubleSpinBox )
837 {
838 if ( mAllowingNull && qgsDoubleNear( mDoubleSpinBox->value(), mDoubleSpinBox->minimum() ) )
839 return QVariant();
840 else
841 return mDoubleSpinBox->value();
842 }
843 else if ( mSpinBox )
844 {
845 if ( mAllowingNull && mSpinBox->value() == mSpinBox->minimum() )
846 return QVariant();
847 else
848 return mSpinBox->value();
849 }
850 else
851 return QVariant();
852}
853
854double QgsProcessingNumericWidgetWrapper::calculateStep( const double minimum, const double maximum )
855{
856 const double valueRange = maximum - minimum;
857 if ( valueRange <= 1.0 )
858 {
859 const double step = valueRange / 10.0;
860 // round to 1 significant figure
861 return qgsRound( step, -std::floor( std::log( step ) ) );
862 }
863 else
864 {
865 return 1.0;
866 }
867}
868
869QString QgsProcessingNumericWidgetWrapper::parameterType() const
870{
872}
873
874QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingNumericWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
875{
876 return new QgsProcessingNumericWidgetWrapper( parameter, type );
877}
878
879QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingNumericWidgetWrapper::createParameterDefinitionWidget(
881)
882{
883 return new QgsProcessingNumberParameterDefinitionWidget( context, widgetContext, definition, algorithm );
884}
885
886//
887// QgsProcessingDistanceWidgetWrapper
888//
889
890QgsProcessingDistanceParameterDefinitionWidget::QgsProcessingDistanceParameterDefinitionWidget(
891 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
892)
893 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
894{
895 QVBoxLayout *vlayout = new QVBoxLayout();
896 vlayout->setContentsMargins( 0, 0, 0, 0 );
897
898 vlayout->addWidget( new QLabel( tr( "Linked input" ) ) );
899
900 mParentLayerComboBox = new QComboBox();
901
902 QString initialParent;
903 if ( const QgsProcessingParameterDistance *distParam = dynamic_cast<const QgsProcessingParameterDistance *>( definition ) )
904 initialParent = distParam->parentParameterName();
905
906 if ( auto *lModel = widgetContext.model() )
907 {
908 // populate combo box with other model input choices
909 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
910 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
911 {
912 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
913 {
914 mParentLayerComboBox->addItem( definition->description(), definition->name() );
915 if ( !initialParent.isEmpty() && initialParent == definition->name() )
916 {
917 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
918 }
919 }
920 else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast<const QgsProcessingParameterVectorLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
921 {
922 mParentLayerComboBox->addItem( definition->description(), definition->name() );
923 if ( !initialParent.isEmpty() && initialParent == definition->name() )
924 {
925 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
926 }
927 }
928 else if ( const QgsProcessingParameterMapLayer *definition = dynamic_cast<const QgsProcessingParameterMapLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
929 {
930 mParentLayerComboBox->addItem( definition->description(), definition->name() );
931 if ( !initialParent.isEmpty() && initialParent == definition->name() )
932 {
933 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
934 }
935 }
936 else if ( const QgsProcessingParameterCrs *definition = dynamic_cast<const QgsProcessingParameterCrs *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
937 {
938 mParentLayerComboBox->addItem( definition->description(), definition->name() );
939 if ( !initialParent.isEmpty() && initialParent == definition->name() )
940 {
941 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
942 }
943 }
944 }
945 }
946
947 if ( mParentLayerComboBox->count() == 0 && !initialParent.isEmpty() )
948 {
949 // if no parent candidates found, we just add the existing one as a placeholder
950 mParentLayerComboBox->addItem( initialParent, initialParent );
951 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
952 }
953
954 vlayout->addWidget( mParentLayerComboBox );
955 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
956
957 vlayout->addWidget( new QLabel( tr( "Minimum value" ) ) );
958 mMinLineEdit = new QLineEdit();
959 vlayout->addWidget( mMinLineEdit );
960
961 vlayout->addWidget( new QLabel( tr( "Maximum value" ) ) );
962 mMaxLineEdit = new QLineEdit();
963 vlayout->addWidget( mMaxLineEdit );
964
965 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
966 mDefaultLineEdit = new QLineEdit();
967 vlayout->addWidget( mDefaultLineEdit );
968
969 if ( const QgsProcessingParameterDistance *distParam = dynamic_cast<const QgsProcessingParameterDistance *>( definition ) )
970 {
971 mMinLineEdit->setText( QLocale().toString( distParam->minimum() ) );
972 mMaxLineEdit->setText( QLocale().toString( distParam->maximum() ) );
973 mDefaultLineEdit->setText( distParam->defaultValueForGui().toString() );
974 }
975 connect( mMinLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
976 connect( mMaxLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
977 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
978
979 setLayout( vlayout );
980}
981
982QgsProcessingParameterDefinition *QgsProcessingDistanceParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
983{
984 bool ok;
985 double val = QgsDoubleValidator::toDouble( mDefaultLineEdit->text(), &ok );
986
987 auto param = std::make_unique<QgsProcessingParameterDistance>( name, description, ok ? val : QVariant(), mParentLayerComboBox->currentData().toString() );
988
989 val = QgsDoubleValidator::toDouble( mMinLineEdit->text(), &ok );
990 if ( ok )
991 {
992 param->setMinimum( val );
993 }
994
995 val = QgsDoubleValidator::toDouble( mMaxLineEdit->text(), &ok );
996 if ( ok )
997 {
998 param->setMaximum( val );
999 }
1000
1001 param->setFlags( flags );
1002 return param.release();
1003}
1004
1005QgsProcessingDistanceWidgetWrapper::QgsProcessingDistanceWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
1006 : QgsProcessingNumericWidgetWrapper( parameter, type, parent )
1007{}
1008
1009QString QgsProcessingDistanceWidgetWrapper::parameterType() const
1010{
1012}
1013
1014QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingDistanceWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
1015{
1016 return new QgsProcessingDistanceWidgetWrapper( parameter, type );
1017}
1018
1019QWidget *QgsProcessingDistanceWidgetWrapper::createWidget()
1020{
1021 const QgsProcessingParameterDistance *distanceDef = static_cast<const QgsProcessingParameterDistance *>( parameterDefinition() );
1022
1023 QWidget *spin = QgsProcessingNumericWidgetWrapper::createWidget();
1024 switch ( type() )
1025 {
1027 {
1028 mLabel = new QLabel();
1029 mUnitsCombo = new QComboBox();
1030
1031 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Meters ), static_cast<int>( Qgis::DistanceUnit::Meters ) );
1032 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Kilometers ), static_cast<int>( Qgis::DistanceUnit::Kilometers ) );
1033 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Feet ), static_cast<int>( Qgis::DistanceUnit::Feet ) );
1034 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Yards ), static_cast<int>( Qgis::DistanceUnit::Yards ) );
1035 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Miles ), static_cast<int>( Qgis::DistanceUnit::Miles ) );
1037 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Centimeters ), static_cast<int>( Qgis::DistanceUnit::Centimeters ) );
1038 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Millimeters ), static_cast<int>( Qgis::DistanceUnit::Millimeters ) );
1039 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::DistanceUnit::Inches ), static_cast<int>( Qgis::DistanceUnit::Inches ) );
1040
1041 const int labelMargin = static_cast<int>( std::round( mUnitsCombo->fontMetrics().horizontalAdvance( 'X' ) ) );
1042 QHBoxLayout *layout = new QHBoxLayout();
1043 layout->addWidget( spin, 1 );
1044 layout->insertSpacing( 1, labelMargin / 2 );
1045 layout->insertWidget( 2, mLabel );
1046 layout->insertWidget( 3, mUnitsCombo );
1047
1048 // bit of fiddlyness here -- we want the initial spacing to only be visible
1049 // when the warning label is shown, so it's embedded inside mWarningLabel
1050 // instead of outside it
1051 mWarningLabel = new QWidget();
1052 QHBoxLayout *warningLayout = new QHBoxLayout();
1053 warningLayout->setContentsMargins( 0, 0, 0, 0 );
1054 QLabel *warning = new QLabel();
1055 QIcon icon = QgsApplication::getThemeIcon( u"mIconWarning.svg"_s );
1056 const int size = static_cast<int>( std::max( 24.0, spin->minimumSize().height() * 0.5 ) );
1057 warning->setPixmap( icon.pixmap( icon.actualSize( QSize( size, size ) ) ) );
1058 warning->setToolTip( tr( "Distance is in geographic degrees. Consider reprojecting to a projected local coordinate system for accurate results." ) );
1059 warningLayout->insertSpacing( 0, labelMargin / 2 );
1060 warningLayout->insertWidget( 1, warning );
1061 mWarningLabel->setLayout( warningLayout );
1062 layout->insertWidget( 4, mWarningLabel );
1063
1064 QWidget *w = new QWidget();
1065 layout->setContentsMargins( 0, 0, 0, 0 );
1066 w->setLayout( layout );
1067
1068 setUnits( distanceDef->defaultUnit() );
1069
1070 return w;
1071 }
1072
1075 return spin;
1076 }
1077 return nullptr;
1078}
1079
1080void QgsProcessingDistanceWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
1081{
1082 QgsProcessingNumericWidgetWrapper::postInitialize( wrappers );
1083 switch ( type() )
1084 {
1086 {
1087 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
1088 {
1089 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterDistance *>( parameterDefinition() )->parentParameterName() )
1090 {
1091 setUnitParameterValue( wrapper->parameterValue(), wrapper );
1092 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setUnitParameterValue( wrapper->parameterValue(), wrapper ); } );
1093 break;
1094 }
1095 }
1096 break;
1097 }
1098
1101 break;
1102 }
1103}
1104
1105void QgsProcessingDistanceWidgetWrapper::setUnitParameterValue( const QVariant &value, const QgsAbstractProcessingParameterWidgetWrapper *wrapper )
1106{
1108
1109 // evaluate value to layer
1110 QgsProcessingContext *context = nullptr;
1111 std::unique_ptr<QgsProcessingContext> tmpContext;
1112 if ( mProcessingContextGenerator )
1113 context = mProcessingContextGenerator->processingContext();
1114
1115 if ( !context )
1116 {
1117 tmpContext = std::make_unique<QgsProcessingContext>();
1118 context = tmpContext.get();
1119 }
1120
1121 const QgsCoordinateReferenceSystem crs = wrapper ? QgsProcessingParameters::parameterAsCrs( wrapper->parameterDefinition(), wrapper->parameterValue(), *context )
1122 : QgsProcessingUtils::variantToCrs( value, *context );
1123 if ( crs.isValid() )
1124 {
1125 units = crs.mapUnits();
1126 }
1127
1128 setUnits( units );
1129}
1130
1131void QgsProcessingDistanceWidgetWrapper::setUnits( Qgis::DistanceUnit units )
1132{
1133 mLabel->setText( QgsUnitTypes::toString( units ) );
1135 {
1136 mUnitsCombo->hide();
1137 mLabel->show();
1138 }
1139 else
1140 {
1141 // Only reset the combo if the base units have changed
1142 // or we might loose any user-set unit when changing layer.
1143 // See issue #61470
1144 if ( mBaseUnit != units )
1145 {
1146 mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( static_cast<int>( units ) ) );
1147 }
1148
1149 mUnitsCombo->show();
1150 mLabel->hide();
1151 }
1152 mWarningLabel->setVisible( units == Qgis::DistanceUnit::Degrees );
1153 mBaseUnit = units;
1154}
1155
1156QVariant QgsProcessingDistanceWidgetWrapper::widgetValue() const
1157{
1158 const QVariant val = QgsProcessingNumericWidgetWrapper::widgetValue();
1159 if ( val.userType() == QMetaType::Type::Double && mUnitsCombo && mUnitsCombo->isVisible() )
1160 {
1161 Qgis::DistanceUnit displayUnit = static_cast<Qgis::DistanceUnit>( mUnitsCombo->currentData().toInt() );
1162 return val.toDouble() * QgsUnitTypes::fromUnitToUnitFactor( displayUnit, mBaseUnit );
1163 }
1164 else
1165 {
1166 return val;
1167 }
1168}
1169
1170QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingDistanceWidgetWrapper::createParameterDefinitionWidget(
1172)
1173{
1174 return new QgsProcessingDistanceParameterDefinitionWidget( context, widgetContext, definition, algorithm );
1175}
1176
1177
1178//
1179// QgsProcessingAreaParameterDefinitionWidget
1180//
1181
1182QgsProcessingAreaParameterDefinitionWidget::QgsProcessingAreaParameterDefinitionWidget(
1183 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
1184)
1185 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
1186{
1187 QVBoxLayout *vlayout = new QVBoxLayout();
1188 vlayout->setContentsMargins( 0, 0, 0, 0 );
1189
1190 vlayout->addWidget( new QLabel( tr( "Linked input" ) ) );
1191
1192 mParentLayerComboBox = new QComboBox();
1193
1194 QString initialParent;
1195 if ( const QgsProcessingParameterArea *areaParam = dynamic_cast<const QgsProcessingParameterArea *>( definition ) )
1196 initialParent = areaParam->parentParameterName();
1197
1198 if ( auto *lModel = widgetContext.model() )
1199 {
1200 // populate combo box with other model input choices
1201 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
1202 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
1203 {
1204 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1205 {
1206 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1207 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1208 {
1209 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1210 }
1211 }
1212 else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast<const QgsProcessingParameterVectorLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1213 {
1214 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1215 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1216 {
1217 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1218 }
1219 }
1220 else if ( const QgsProcessingParameterMapLayer *definition = dynamic_cast<const QgsProcessingParameterMapLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1221 {
1222 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1223 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1224 {
1225 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1226 }
1227 }
1228 else if ( const QgsProcessingParameterCrs *definition = dynamic_cast<const QgsProcessingParameterCrs *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1229 {
1230 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1231 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1232 {
1233 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1234 }
1235 }
1236 }
1237 }
1238
1239 if ( mParentLayerComboBox->count() == 0 && !initialParent.isEmpty() )
1240 {
1241 // if no parent candidates found, we just add the existing one as a placeholder
1242 mParentLayerComboBox->addItem( initialParent, initialParent );
1243 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1244 }
1245
1246 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1247
1248 vlayout->addWidget( mParentLayerComboBox );
1249
1250 vlayout->addWidget( new QLabel( tr( "Minimum value" ) ) );
1251 mMinLineEdit = new QLineEdit();
1252 vlayout->addWidget( mMinLineEdit );
1253
1254 vlayout->addWidget( new QLabel( tr( "Maximum value" ) ) );
1255 mMaxLineEdit = new QLineEdit();
1256 vlayout->addWidget( mMaxLineEdit );
1257
1258 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
1259 mDefaultLineEdit = new QLineEdit();
1260 vlayout->addWidget( mDefaultLineEdit );
1261
1262 if ( const QgsProcessingParameterArea *areaParam = dynamic_cast<const QgsProcessingParameterArea *>( definition ) )
1263 {
1264 mMinLineEdit->setText( QLocale().toString( areaParam->minimum() ) );
1265 mMaxLineEdit->setText( QLocale().toString( areaParam->maximum() ) );
1266 mDefaultLineEdit->setText( areaParam->defaultValueForGui().toString() );
1267 }
1268
1269 connect( mMinLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1270 connect( mMaxLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1271 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1272
1273 setLayout( vlayout );
1274}
1275
1276QgsProcessingParameterDefinition *QgsProcessingAreaParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
1277{
1278 bool ok;
1279 double val = QgsDoubleValidator::toDouble( mDefaultLineEdit->text(), &ok );
1280
1281 auto param = std::make_unique<QgsProcessingParameterArea>( name, description, ok ? val : QVariant(), mParentLayerComboBox->currentData().toString() );
1282
1283 val = QgsDoubleValidator::toDouble( mMinLineEdit->text(), &ok );
1284 if ( ok )
1285 {
1286 param->setMinimum( val );
1287 }
1288
1289 val = QgsDoubleValidator::toDouble( mMaxLineEdit->text(), &ok );
1290 if ( ok )
1291 {
1292 param->setMaximum( val );
1293 }
1294
1295 param->setFlags( flags );
1296 return param.release();
1297}
1298
1299
1300//
1301// QgsProcessingAreaWidgetWrapper
1302//
1303
1304QgsProcessingAreaWidgetWrapper::QgsProcessingAreaWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
1305 : QgsProcessingNumericWidgetWrapper( parameter, type, parent )
1306{}
1307
1308QString QgsProcessingAreaWidgetWrapper::parameterType() const
1309{
1311}
1312
1313QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingAreaWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
1314{
1315 return new QgsProcessingAreaWidgetWrapper( parameter, type );
1316}
1317
1318QWidget *QgsProcessingAreaWidgetWrapper::createWidget()
1319{
1320 const QgsProcessingParameterArea *areaDef = static_cast<const QgsProcessingParameterArea *>( parameterDefinition() );
1321
1322 QWidget *spin = QgsProcessingNumericWidgetWrapper::createWidget();
1323 switch ( type() )
1324 {
1326 {
1327 mLabel = new QLabel();
1328 mUnitsCombo = new QComboBox();
1329
1330 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareMeters ), QVariant::fromValue( Qgis::AreaUnit::SquareMeters ) );
1331 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareKilometers ), QVariant::fromValue( Qgis::AreaUnit::SquareKilometers ) );
1332 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareFeet ), QVariant::fromValue( Qgis::AreaUnit::SquareFeet ) );
1333 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareYards ), QVariant::fromValue( Qgis::AreaUnit::SquareYards ) );
1334 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareMiles ), QVariant::fromValue( Qgis::AreaUnit::SquareMiles ) );
1335 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::Hectares ), QVariant::fromValue( Qgis::AreaUnit::Hectares ) );
1336 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::Acres ), QVariant::fromValue( Qgis::AreaUnit::Acres ) );
1338 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareDegrees ), QVariant::fromValue( Qgis::AreaUnit::SquareDegrees ) );
1339 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareCentimeters ), QVariant::fromValue( Qgis::AreaUnit::SquareCentimeters ) );
1340 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareMillimeters ), QVariant::fromValue( Qgis::AreaUnit::SquareMillimeters ) );
1341 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::AreaUnit::SquareInches ), QVariant::fromValue( Qgis::AreaUnit::SquareInches ) );
1342
1343 const int labelMargin = static_cast<int>( std::round( mUnitsCombo->fontMetrics().horizontalAdvance( 'X' ) ) );
1344 QHBoxLayout *layout = new QHBoxLayout();
1345 layout->addWidget( spin, 1 );
1346 layout->insertSpacing( 1, labelMargin / 2 );
1347 layout->insertWidget( 2, mLabel );
1348 layout->insertWidget( 3, mUnitsCombo );
1349
1350 // bit of fiddlyness here -- we want the initial spacing to only be visible
1351 // when the warning label is shown, so it's embedded inside mWarningLabel
1352 // instead of outside it
1353 mWarningLabel = new QWidget();
1354 QHBoxLayout *warningLayout = new QHBoxLayout();
1355 warningLayout->setContentsMargins( 0, 0, 0, 0 );
1356 QLabel *warning = new QLabel();
1357 QIcon icon = QgsApplication::getThemeIcon( u"mIconWarning.svg"_s );
1358 const int size = static_cast<int>( std::max( 24.0, spin->minimumSize().height() * 0.5 ) );
1359 warning->setPixmap( icon.pixmap( icon.actualSize( QSize( size, size ) ) ) );
1360 warning->setToolTip( tr( "Area is in geographic degrees. Consider reprojecting to a projected local coordinate system for accurate results." ) );
1361 warningLayout->insertSpacing( 0, labelMargin / 2 );
1362 warningLayout->insertWidget( 1, warning );
1363 mWarningLabel->setLayout( warningLayout );
1364 layout->insertWidget( 4, mWarningLabel );
1365
1366 QWidget *w = new QWidget();
1367 layout->setContentsMargins( 0, 0, 0, 0 );
1368 w->setLayout( layout );
1369
1370 setUnits( areaDef->defaultUnit() );
1371
1372 return w;
1373 }
1374
1377 return spin;
1378 }
1379 return nullptr;
1380}
1381
1382void QgsProcessingAreaWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
1383{
1384 QgsProcessingNumericWidgetWrapper::postInitialize( wrappers );
1385 switch ( type() )
1386 {
1388 {
1389 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
1390 {
1391 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterArea *>( parameterDefinition() )->parentParameterName() )
1392 {
1393 setUnitParameterValue( wrapper->parameterValue(), wrapper );
1394 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setUnitParameterValue( wrapper->parameterValue(), wrapper ); } );
1395 break;
1396 }
1397 }
1398 break;
1399 }
1400
1403 break;
1404 }
1405}
1406
1407void QgsProcessingAreaWidgetWrapper::setUnitParameterValue( const QVariant &value, const QgsAbstractProcessingParameterWidgetWrapper *wrapper )
1408{
1410
1411 // evaluate value to layer
1412 QgsProcessingContext *context = nullptr;
1413 std::unique_ptr<QgsProcessingContext> tmpContext;
1414 if ( mProcessingContextGenerator )
1415 context = mProcessingContextGenerator->processingContext();
1416
1417 if ( !context )
1418 {
1419 tmpContext = std::make_unique<QgsProcessingContext>();
1420 context = tmpContext.get();
1421 }
1422
1423 const QgsCoordinateReferenceSystem crs = wrapper ? QgsProcessingParameters::parameterAsCrs( wrapper->parameterDefinition(), wrapper->parameterValue(), *context )
1424 : QgsProcessingUtils::variantToCrs( value, *context );
1425 if ( crs.isValid() )
1426 {
1427 units = QgsUnitTypes::distanceToAreaUnit( crs.mapUnits() );
1428 }
1429
1430 setUnits( units );
1431}
1432
1433void QgsProcessingAreaWidgetWrapper::setUnits( Qgis::AreaUnit units )
1434{
1435 mLabel->setText( QgsUnitTypes::toString( units ) );
1437 {
1438 mUnitsCombo->hide();
1439 mLabel->show();
1440 }
1441 else
1442 {
1443 mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QVariant::fromValue( units ) ) );
1444 mUnitsCombo->show();
1445 mLabel->hide();
1446 }
1447 mWarningLabel->setVisible( units == Qgis::AreaUnit::SquareDegrees );
1448 mBaseUnit = units;
1449}
1450
1451QVariant QgsProcessingAreaWidgetWrapper::widgetValue() const
1452{
1453 const QVariant val = QgsProcessingNumericWidgetWrapper::widgetValue();
1454 if ( val.userType() == QMetaType::Type::Double && mUnitsCombo && mUnitsCombo->isVisible() )
1455 {
1456 const Qgis::AreaUnit displayUnit = mUnitsCombo->currentData().value<Qgis::AreaUnit>();
1457 return val.toDouble() * QgsUnitTypes::fromUnitToUnitFactor( displayUnit, mBaseUnit );
1458 }
1459 else
1460 {
1461 return val;
1462 }
1463}
1464
1465QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingAreaWidgetWrapper::createParameterDefinitionWidget(
1467)
1468{
1469 return new QgsProcessingAreaParameterDefinitionWidget( context, widgetContext, definition, algorithm );
1470}
1471
1472
1473//
1474// QgsProcessingVolumeParameterDefinitionWidget
1475//
1476
1477QgsProcessingVolumeParameterDefinitionWidget::QgsProcessingVolumeParameterDefinitionWidget(
1478 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
1479)
1480 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
1481{
1482 QVBoxLayout *vlayout = new QVBoxLayout();
1483 vlayout->setContentsMargins( 0, 0, 0, 0 );
1484
1485 vlayout->addWidget( new QLabel( tr( "Linked input" ) ) );
1486
1487 mParentLayerComboBox = new QComboBox();
1488
1489 QString initialParent;
1490 if ( const QgsProcessingParameterVolume *volumeParam = dynamic_cast<const QgsProcessingParameterVolume *>( definition ) )
1491 initialParent = volumeParam->parentParameterName();
1492
1493 if ( auto *lModel = widgetContext.model() )
1494 {
1495 // populate combo box with other model input choices
1496 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
1497 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
1498 {
1499 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1500 {
1501 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1502 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1503 {
1504 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1505 }
1506 }
1507 else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast<const QgsProcessingParameterVectorLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1508 {
1509 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1510 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1511 {
1512 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1513 }
1514 }
1515 else if ( const QgsProcessingParameterMapLayer *definition = dynamic_cast<const QgsProcessingParameterMapLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1516 {
1517 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1518 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1519 {
1520 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1521 }
1522 }
1523 else if ( const QgsProcessingParameterCrs *definition = dynamic_cast<const QgsProcessingParameterCrs *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
1524 {
1525 mParentLayerComboBox->addItem( definition->description(), definition->name() );
1526 if ( !initialParent.isEmpty() && initialParent == definition->name() )
1527 {
1528 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1529 }
1530 }
1531 }
1532 }
1533
1534 if ( mParentLayerComboBox->count() == 0 && !initialParent.isEmpty() )
1535 {
1536 // if no parent candidates found, we just add the existing one as a placeholder
1537 mParentLayerComboBox->addItem( initialParent, initialParent );
1538 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
1539 }
1540
1541 vlayout->addWidget( mParentLayerComboBox );
1542
1543 vlayout->addWidget( new QLabel( tr( "Minimum value" ) ) );
1544 mMinLineEdit = new QLineEdit();
1545 vlayout->addWidget( mMinLineEdit );
1546
1547 vlayout->addWidget( new QLabel( tr( "Maximum value" ) ) );
1548 mMaxLineEdit = new QLineEdit();
1549 vlayout->addWidget( mMaxLineEdit );
1550
1551 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
1552 mDefaultLineEdit = new QLineEdit();
1553 vlayout->addWidget( mDefaultLineEdit );
1554
1555 if ( const QgsProcessingParameterVolume *volumeParam = dynamic_cast<const QgsProcessingParameterVolume *>( definition ) )
1556 {
1557 mMinLineEdit->setText( QLocale().toString( volumeParam->minimum() ) );
1558 mMaxLineEdit->setText( QLocale().toString( volumeParam->maximum() ) );
1559 mDefaultLineEdit->setText( volumeParam->defaultValueForGui().toString() );
1560 }
1561
1562 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1563 connect( mMinLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1564 connect( mMaxLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1565 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1566
1567 setLayout( vlayout );
1568}
1569
1570QgsProcessingParameterDefinition *QgsProcessingVolumeParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
1571{
1572 bool ok;
1573 double val = QgsDoubleValidator::toDouble( mDefaultLineEdit->text(), &ok );
1574
1575 auto param = std::make_unique<QgsProcessingParameterVolume>( name, description, ok ? val : QVariant(), mParentLayerComboBox->currentData().toString() );
1576
1577 val = QgsDoubleValidator::toDouble( mMinLineEdit->text(), &ok );
1578 if ( ok )
1579 {
1580 param->setMinimum( val );
1581 }
1582
1583 val = QgsDoubleValidator::toDouble( mMaxLineEdit->text(), &ok );
1584 if ( ok )
1585 {
1586 param->setMaximum( val );
1587 }
1588
1589 param->setFlags( flags );
1590 return param.release();
1591}
1592
1593
1594//
1595// QgsProcessingVolumeWidgetWrapper
1596//
1597
1598QgsProcessingVolumeWidgetWrapper::QgsProcessingVolumeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
1599 : QgsProcessingNumericWidgetWrapper( parameter, type, parent )
1600{}
1601
1602QString QgsProcessingVolumeWidgetWrapper::parameterType() const
1603{
1605}
1606
1607QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingVolumeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
1608{
1609 return new QgsProcessingVolumeWidgetWrapper( parameter, type );
1610}
1611
1612QWidget *QgsProcessingVolumeWidgetWrapper::createWidget()
1613{
1614 const QgsProcessingParameterVolume *volumeDef = static_cast<const QgsProcessingParameterVolume *>( parameterDefinition() );
1615
1616 QWidget *spin = QgsProcessingNumericWidgetWrapper::createWidget();
1617 switch ( type() )
1618 {
1620 {
1621 mLabel = new QLabel();
1622 mUnitsCombo = new QComboBox();
1623
1624 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicMeters ), QVariant::fromValue( Qgis::VolumeUnit::CubicMeters ) );
1625 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicFeet ), QVariant::fromValue( Qgis::VolumeUnit::CubicFeet ) );
1626 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicYards ), QVariant::fromValue( Qgis::VolumeUnit::CubicYards ) );
1627 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::Barrel ), QVariant::fromValue( Qgis::VolumeUnit::Barrel ) );
1628 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicDecimeter ), QVariant::fromValue( Qgis::VolumeUnit::CubicDecimeter ) );
1629 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::Liters ), QVariant::fromValue( Qgis::VolumeUnit::Liters ) );
1630 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::GallonUS ), QVariant::fromValue( Qgis::VolumeUnit::GallonUS ) );
1631 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicInch ), QVariant::fromValue( Qgis::VolumeUnit::CubicInch ) );
1632 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicCentimeter ), QVariant::fromValue( Qgis::VolumeUnit::CubicCentimeter ) );
1633 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::VolumeUnit::CubicDegrees ), QVariant::fromValue( Qgis::VolumeUnit::CubicDegrees ) );
1634
1635 const int labelMargin = static_cast<int>( std::round( mUnitsCombo->fontMetrics().horizontalAdvance( 'X' ) ) );
1636 QHBoxLayout *layout = new QHBoxLayout();
1637 layout->addWidget( spin, 1 );
1638 layout->insertSpacing( 1, labelMargin / 2 );
1639 layout->insertWidget( 2, mLabel );
1640 layout->insertWidget( 3, mUnitsCombo );
1641
1642 // bit of fiddlyness here -- we want the initial spacing to only be visible
1643 // when the warning label is shown, so it's embedded inside mWarningLabel
1644 // instead of outside it
1645 mWarningLabel = new QWidget();
1646 QHBoxLayout *warningLayout = new QHBoxLayout();
1647 warningLayout->setContentsMargins( 0, 0, 0, 0 );
1648 QLabel *warning = new QLabel();
1649 QIcon icon = QgsApplication::getThemeIcon( u"mIconWarning.svg"_s );
1650 const int size = static_cast<int>( std::max( 24.0, spin->minimumSize().height() * 0.5 ) );
1651 warning->setPixmap( icon.pixmap( icon.actualSize( QSize( size, size ) ) ) );
1652 warning->setToolTip( tr( "Volume is in geographic degrees. Consider reprojecting to a projected local coordinate system for accurate results." ) );
1653 warningLayout->insertSpacing( 0, labelMargin / 2 );
1654 warningLayout->insertWidget( 1, warning );
1655 mWarningLabel->setLayout( warningLayout );
1656 layout->insertWidget( 4, mWarningLabel );
1657
1658 QWidget *w = new QWidget();
1659 layout->setContentsMargins( 0, 0, 0, 0 );
1660 w->setLayout( layout );
1661
1662 setUnits( volumeDef->defaultUnit() );
1663
1664 return w;
1665 }
1666
1669 return spin;
1670 }
1671 return nullptr;
1672}
1673
1674void QgsProcessingVolumeWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
1675{
1676 QgsProcessingNumericWidgetWrapper::postInitialize( wrappers );
1677 switch ( type() )
1678 {
1680 {
1681 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
1682 {
1683 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterVolume *>( parameterDefinition() )->parentParameterName() )
1684 {
1685 setUnitParameterValue( wrapper->parameterValue(), wrapper );
1686 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setUnitParameterValue( wrapper->parameterValue(), wrapper ); } );
1687 break;
1688 }
1689 }
1690 break;
1691 }
1692
1695 break;
1696 }
1697}
1698
1699void QgsProcessingVolumeWidgetWrapper::setUnitParameterValue( const QVariant &value, const QgsAbstractProcessingParameterWidgetWrapper *wrapper )
1700{
1702
1703 // evaluate value to layer
1704 QgsProcessingContext *context = nullptr;
1705 std::unique_ptr<QgsProcessingContext> tmpContext;
1706 if ( mProcessingContextGenerator )
1707 context = mProcessingContextGenerator->processingContext();
1708
1709 if ( !context )
1710 {
1711 tmpContext = std::make_unique<QgsProcessingContext>();
1712 context = tmpContext.get();
1713 }
1714
1715 const QgsCoordinateReferenceSystem crs = wrapper ? QgsProcessingParameters::parameterAsCrs( wrapper->parameterDefinition(), wrapper->parameterValue(), *context )
1716 : QgsProcessingUtils::variantToCrs( value, *context );
1717 if ( crs.isValid() )
1718 {
1719 units = QgsUnitTypes::distanceToVolumeUnit( crs.mapUnits() );
1720 }
1721
1722 setUnits( units );
1723}
1724
1725void QgsProcessingVolumeWidgetWrapper::setUnits( Qgis::VolumeUnit units )
1726{
1727 mLabel->setText( QgsUnitTypes::toString( units ) );
1729 {
1730 mUnitsCombo->hide();
1731 mLabel->show();
1732 }
1733 else
1734 {
1735 mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QVariant::fromValue( units ) ) );
1736 mUnitsCombo->show();
1737 mLabel->hide();
1738 }
1739 mWarningLabel->setVisible( units == Qgis::VolumeUnit::CubicDegrees );
1740 mBaseUnit = units;
1741}
1742
1743QVariant QgsProcessingVolumeWidgetWrapper::widgetValue() const
1744{
1745 const QVariant val = QgsProcessingNumericWidgetWrapper::widgetValue();
1746 if ( val.userType() == QMetaType::Type::Double && mUnitsCombo && mUnitsCombo->isVisible() )
1747 {
1748 const Qgis::VolumeUnit displayUnit = mUnitsCombo->currentData().value<Qgis::VolumeUnit>();
1749 return val.toDouble() * QgsUnitTypes::fromUnitToUnitFactor( displayUnit, mBaseUnit );
1750 }
1751 else
1752 {
1753 return val;
1754 }
1755}
1756
1757QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingVolumeWidgetWrapper::createParameterDefinitionWidget(
1759)
1760{
1761 return new QgsProcessingVolumeParameterDefinitionWidget( context, widgetContext, definition, algorithm );
1762}
1763
1764
1765//
1766// QgsProcessingDurationWidgetWrapper
1767//
1768
1769QgsProcessingDurationParameterDefinitionWidget::QgsProcessingDurationParameterDefinitionWidget(
1770 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
1771)
1772 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
1773{
1774 QVBoxLayout *vlayout = new QVBoxLayout();
1775 vlayout->setContentsMargins( 0, 0, 0, 0 );
1776
1777 vlayout->addWidget( new QLabel( tr( "Minimum value" ) ) );
1778 mMinLineEdit = new QLineEdit();
1779 vlayout->addWidget( mMinLineEdit );
1780
1781 vlayout->addWidget( new QLabel( tr( "Maximum value" ) ) );
1782 mMaxLineEdit = new QLineEdit();
1783 vlayout->addWidget( mMaxLineEdit );
1784
1785 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
1786 mDefaultLineEdit = new QLineEdit();
1787 vlayout->addWidget( mDefaultLineEdit );
1788
1789 vlayout->addWidget( new QLabel( tr( "Default unit type" ) ) );
1790
1791 mUnitsCombo = new QComboBox();
1793 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Seconds ), static_cast<int>( Qgis::TemporalUnit::Seconds ) );
1794 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Minutes ), static_cast<int>( Qgis::TemporalUnit::Minutes ) );
1795 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Hours ), static_cast<int>( Qgis::TemporalUnit::Hours ) );
1796 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Days ), static_cast<int>( Qgis::TemporalUnit::Days ) );
1797 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Weeks ), static_cast<int>( Qgis::TemporalUnit::Weeks ) );
1798 mUnitsCombo->addItem( tr( "years (365.25 days)" ), static_cast<int>( Qgis::TemporalUnit::Years ) );
1799 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Decades ), static_cast<int>( Qgis::TemporalUnit::Decades ) );
1800 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Centuries ), static_cast<int>( Qgis::TemporalUnit::Centuries ) );
1801 vlayout->addWidget( mUnitsCombo );
1802
1803 connect( mUnitsCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1804
1805 if ( const QgsProcessingParameterDuration *durationParam = dynamic_cast<const QgsProcessingParameterDuration *>( definition ) )
1806 {
1807 mMinLineEdit->setText( QLocale().toString( durationParam->minimum() ) );
1808 mMaxLineEdit->setText( QLocale().toString( durationParam->maximum() ) );
1809 mDefaultLineEdit->setText( durationParam->defaultValueForGui().toString() );
1810 mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( static_cast<int>( durationParam->defaultUnit() ) ) );
1811 }
1812 connect( mMinLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1813 connect( mMaxLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1814 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1815
1816 setLayout( vlayout );
1817}
1818
1819QgsProcessingParameterDefinition *QgsProcessingDurationParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
1820{
1821 bool ok;
1822 double val = QgsDoubleValidator::toDouble( mDefaultLineEdit->text(), &ok );
1823
1824 auto param = std::make_unique<QgsProcessingParameterDuration>( name, description, ok ? val : QVariant() );
1825
1826 val = QgsDoubleValidator::toDouble( mMinLineEdit->text(), &ok );
1827 if ( ok )
1828 {
1829 param->setMinimum( val );
1830 }
1831
1832 val = QgsDoubleValidator::toDouble( mMaxLineEdit->text(), &ok );
1833 if ( ok )
1834 {
1835 param->setMaximum( val );
1836 }
1837
1838 param->setDefaultUnit( static_cast<Qgis::TemporalUnit>( mUnitsCombo->currentData().toInt() ) );
1839
1840 param->setFlags( flags );
1841 return param.release();
1842}
1843
1844QgsProcessingDurationWidgetWrapper::QgsProcessingDurationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
1845 : QgsProcessingNumericWidgetWrapper( parameter, type, parent )
1846{}
1847
1848QString QgsProcessingDurationWidgetWrapper::parameterType() const
1849{
1851}
1852
1853QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingDurationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
1854{
1855 return new QgsProcessingDurationWidgetWrapper( parameter, type );
1856}
1857
1858QWidget *QgsProcessingDurationWidgetWrapper::createWidget()
1859{
1860 const QgsProcessingParameterDuration *durationDef = static_cast<const QgsProcessingParameterDuration *>( parameterDefinition() );
1861
1862 QWidget *spin = QgsProcessingNumericWidgetWrapper::createWidget();
1863 switch ( type() )
1864 {
1866 {
1867 mUnitsCombo = new QComboBox();
1868
1870 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Seconds ), static_cast<int>( Qgis::TemporalUnit::Seconds ) );
1871 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Minutes ), static_cast<int>( Qgis::TemporalUnit::Minutes ) );
1872 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Hours ), static_cast<int>( Qgis::TemporalUnit::Hours ) );
1873 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Days ), static_cast<int>( Qgis::TemporalUnit::Days ) );
1874 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Weeks ), static_cast<int>( Qgis::TemporalUnit::Weeks ) );
1875 mUnitsCombo->addItem( tr( "years (365.25 days)" ), static_cast<int>( Qgis::TemporalUnit::Years ) );
1876 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Decades ), static_cast<int>( Qgis::TemporalUnit::Decades ) );
1877 mUnitsCombo->addItem( QgsUnitTypes::toString( Qgis::TemporalUnit::Centuries ), static_cast<int>( Qgis::TemporalUnit::Centuries ) );
1878
1879 QHBoxLayout *layout = new QHBoxLayout();
1880 layout->addWidget( spin, 1 );
1881 layout->insertWidget( 1, mUnitsCombo );
1882
1883 QWidget *w = new QWidget();
1884 layout->setContentsMargins( 0, 0, 0, 0 );
1885 w->setLayout( layout );
1886
1887 mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( static_cast<int>( durationDef->defaultUnit() ) ) );
1888 mUnitsCombo->show();
1889
1890 return w;
1891 }
1892
1895 return spin;
1896 }
1897 return nullptr;
1898}
1899
1900QLabel *QgsProcessingDurationWidgetWrapper::createLabel()
1901{
1903
1904 if ( type() == Qgis::ProcessingMode::Modeler )
1905 {
1906 label->setText( u"%1 [%2]"_s.arg( label->text(), QgsUnitTypes::toString( mBaseUnit ) ) );
1907 }
1908
1909 return label;
1910}
1911
1912QVariant QgsProcessingDurationWidgetWrapper::widgetValue() const
1913{
1914 const QVariant val = QgsProcessingNumericWidgetWrapper::widgetValue();
1915 if ( val.userType() == QMetaType::Type::Double && mUnitsCombo )
1916 {
1917 Qgis::TemporalUnit displayUnit = static_cast<Qgis::TemporalUnit>( mUnitsCombo->currentData().toInt() );
1918 return val.toDouble() * QgsUnitTypes::fromUnitToUnitFactor( displayUnit, mBaseUnit );
1919 }
1920 else
1921 {
1922 return val;
1923 }
1924}
1925
1926void QgsProcessingDurationWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
1927{
1928 if ( mUnitsCombo )
1929 {
1930 Qgis::TemporalUnit displayUnit = static_cast<Qgis::TemporalUnit>( mUnitsCombo->currentData().toInt() );
1931 const QVariant val = value.toDouble() * QgsUnitTypes::fromUnitToUnitFactor( mBaseUnit, displayUnit );
1932 QgsProcessingNumericWidgetWrapper::setWidgetValue( val, context );
1933 }
1934 else
1935 {
1936 QgsProcessingNumericWidgetWrapper::setWidgetValue( value, context );
1937 }
1938}
1939
1940QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingDurationWidgetWrapper::createParameterDefinitionWidget(
1942)
1943{
1944 return new QgsProcessingDurationParameterDefinitionWidget( context, widgetContext, definition, algorithm );
1945}
1946
1947//
1948// QgsProcessingScaleWidgetWrapper
1949//
1950
1951QgsProcessingScaleParameterDefinitionWidget::QgsProcessingScaleParameterDefinitionWidget(
1952 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
1953)
1954 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
1955{
1956 QVBoxLayout *vlayout = new QVBoxLayout();
1957 vlayout->setContentsMargins( 0, 0, 0, 0 );
1958
1959 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
1960
1961 mDefaultLineEdit = new QLineEdit();
1962
1963 if ( const QgsProcessingParameterScale *scaleParam = dynamic_cast<const QgsProcessingParameterScale *>( definition ) )
1964 {
1965 mDefaultLineEdit->setText( scaleParam->defaultValueForGui().toString() );
1966 }
1967
1968 vlayout->addWidget( mDefaultLineEdit );
1969 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
1970
1971 setLayout( vlayout );
1972}
1973
1974QgsProcessingParameterDefinition *QgsProcessingScaleParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
1975{
1976 bool ok;
1977 double val = mDefaultLineEdit->text().toDouble( &ok );
1978 auto param = std::make_unique<QgsProcessingParameterScale>( name, description, ok ? val : QVariant() );
1979 param->setFlags( flags );
1980 return param.release();
1981}
1982
1983QgsProcessingScaleWidgetWrapper::QgsProcessingScaleWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
1984 : QgsProcessingNumericWidgetWrapper( parameter, type, parent )
1985{}
1986
1987QString QgsProcessingScaleWidgetWrapper::parameterType() const
1988{
1990}
1991
1992QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingScaleWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
1993{
1994 return new QgsProcessingScaleWidgetWrapper( parameter, type );
1995}
1996
1997QWidget *QgsProcessingScaleWidgetWrapper::createWidget()
1998{
1999 const QgsProcessingParameterScale *scaleDef = static_cast<const QgsProcessingParameterScale *>( parameterDefinition() );
2000
2001 switch ( type() )
2002 {
2006 {
2007 mScaleWidget = new QgsScaleWidget( nullptr );
2009 mScaleWidget->setAllowNull( true );
2010
2011 mScaleWidget->setMapCanvas( widgetContext().mapCanvas() );
2012 mScaleWidget->setShowCurrentScaleButton( true );
2013
2014 mScaleWidget->setToolTip( parameterDefinition()->toolTip() );
2015 connect( mScaleWidget, &QgsScaleWidget::scaleChanged, this, [this]( double ) { emit widgetValueHasChanged( this ); } );
2016 return mScaleWidget;
2017 }
2018 }
2019 return nullptr;
2020}
2021
2022void QgsProcessingScaleWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
2023{
2024 if ( mScaleWidget )
2025 mScaleWidget->setMapCanvas( context.mapCanvas() );
2027}
2028
2029
2030QVariant QgsProcessingScaleWidgetWrapper::widgetValue() const
2031{
2032 return mScaleWidget && !mScaleWidget->isNull() ? QVariant( mScaleWidget->scale() ) : QVariant();
2033}
2034
2035void QgsProcessingScaleWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
2036{
2037 if ( mScaleWidget )
2038 {
2039 if ( mScaleWidget->allowNull() && !value.isValid() )
2040 mScaleWidget->setNull();
2041 else
2042 {
2043 const double v = QgsProcessingParameters::parameterAsDouble( parameterDefinition(), value, context );
2044 mScaleWidget->setScale( v );
2045 }
2046 }
2047}
2048
2049QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingScaleWidgetWrapper::createParameterDefinitionWidget(
2051)
2052{
2053 return new QgsProcessingScaleParameterDefinitionWidget( context, widgetContext, definition, algorithm );
2054}
2055
2056
2057//
2058// QgsProcessingRangeWidgetWrapper
2059//
2060
2061QgsProcessingRangeParameterDefinitionWidget::QgsProcessingRangeParameterDefinitionWidget(
2062 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
2063)
2064 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
2065{
2066 QVBoxLayout *vlayout = new QVBoxLayout();
2067 vlayout->setContentsMargins( 0, 0, 0, 0 );
2068
2069 vlayout->addWidget( new QLabel( tr( "Number type" ) ) );
2070
2071 mTypeComboBox = new QComboBox();
2072 mTypeComboBox->addItem( tr( "Float" ), static_cast<int>( Qgis::ProcessingNumberParameterType::Double ) );
2073 mTypeComboBox->addItem( tr( "Integer" ), static_cast<int>( Qgis::ProcessingNumberParameterType::Integer ) );
2074 vlayout->addWidget( mTypeComboBox );
2075
2076 vlayout->addWidget( new QLabel( tr( "Minimum value" ) ) );
2077 mMinLineEdit = new QLineEdit();
2078 vlayout->addWidget( mMinLineEdit );
2079
2080 vlayout->addWidget( new QLabel( tr( "Maximum value" ) ) );
2081 mMaxLineEdit = new QLineEdit();
2082 vlayout->addWidget( mMaxLineEdit );
2083
2084 if ( const QgsProcessingParameterRange *rangeParam = dynamic_cast<const QgsProcessingParameterRange *>( definition ) )
2085 {
2086 mTypeComboBox->setCurrentIndex( mTypeComboBox->findData( static_cast<int>( rangeParam->dataType() ) ) );
2087 const QList<double> range = QgsProcessingParameters::parameterAsRange( rangeParam, rangeParam->defaultValueForGui(), context );
2088 mMinLineEdit->setText( QLocale().toString( range.at( 0 ) ) );
2089 mMaxLineEdit->setText( QLocale().toString( range.at( 1 ) ) );
2090 }
2091
2092 connect( mTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2093 connect( mMinLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2094 connect( mMaxLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2095
2096 setLayout( vlayout );
2097}
2098
2099QgsProcessingParameterDefinition *QgsProcessingRangeParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
2100{
2101 QString defaultValue;
2102 if ( mMinLineEdit->text().isEmpty() )
2103 {
2104 defaultValue = u"None"_s;
2105 }
2106 else
2107 {
2108 bool ok;
2109 defaultValue = QString::number( QgsDoubleValidator::toDouble( mMinLineEdit->text(), &ok ) );
2110 if ( !ok )
2111 {
2112 defaultValue = u"None"_s;
2113 }
2114 }
2115
2116 if ( mMaxLineEdit->text().isEmpty() )
2117 {
2118 defaultValue += ",None"_L1;
2119 }
2120 else
2121 {
2122 bool ok;
2123 const double val { QgsDoubleValidator::toDouble( mMaxLineEdit->text(), &ok ) };
2124 defaultValue += u",%1"_s.arg( ok ? QString::number( val ) : "None"_L1 );
2125 }
2126
2127 Qgis::ProcessingNumberParameterType dataType = static_cast<Qgis::ProcessingNumberParameterType>( mTypeComboBox->currentData().toInt() );
2128 auto param = std::make_unique<QgsProcessingParameterRange>( name, description, dataType, defaultValue );
2129 param->setFlags( flags );
2130 return param.release();
2131}
2132
2133
2134QgsProcessingRangeWidgetWrapper::QgsProcessingRangeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
2135 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
2136{}
2137
2138QWidget *QgsProcessingRangeWidgetWrapper::createWidget()
2139{
2140 const QgsProcessingParameterRange *rangeDef = static_cast<const QgsProcessingParameterRange *>( parameterDefinition() );
2141 switch ( type() )
2142 {
2146 {
2147 QHBoxLayout *layout = new QHBoxLayout();
2148
2149 mMinSpinBox = new QgsDoubleSpinBox();
2150 mMaxSpinBox = new QgsDoubleSpinBox();
2151
2152 mMinSpinBox->setExpressionsEnabled( true );
2153 mMinSpinBox->setShowClearButton( false );
2154 mMaxSpinBox->setExpressionsEnabled( true );
2155 mMaxSpinBox->setShowClearButton( false );
2156
2157 QLabel *minLabel = new QLabel( tr( "Min" ) );
2158 layout->addWidget( minLabel );
2159 layout->addWidget( mMinSpinBox, 1 );
2160
2161 QLabel *maxLabel = new QLabel( tr( "Max" ) );
2162 layout->addWidget( maxLabel );
2163 layout->addWidget( mMaxSpinBox, 1 );
2164
2165 QWidget *w = new QWidget();
2166 layout->setContentsMargins( 0, 0, 0, 0 );
2167 w->setLayout( layout );
2168
2170 {
2171 mMinSpinBox->setDecimals( 6 );
2172 mMaxSpinBox->setDecimals( 6 );
2173 }
2174 else
2175 {
2176 mMinSpinBox->setDecimals( 0 );
2177 mMaxSpinBox->setDecimals( 0 );
2178 }
2179
2180 mMinSpinBox->setMinimum( -99999999.999999 );
2181 mMaxSpinBox->setMinimum( -99999999.999999 );
2182 mMinSpinBox->setMaximum( 99999999.999999 );
2183 mMaxSpinBox->setMaximum( 99999999.999999 );
2184
2186 {
2187 mAllowingNull = true;
2188
2189 const double min = mMinSpinBox->minimum() - 1;
2190 mMinSpinBox->setMinimum( min );
2191 mMaxSpinBox->setMinimum( min );
2192 mMinSpinBox->setValue( min );
2193 mMaxSpinBox->setValue( min );
2194
2195 mMinSpinBox->setShowClearButton( true );
2196 mMaxSpinBox->setShowClearButton( true );
2197 mMinSpinBox->setSpecialValueText( tr( "Not set" ) );
2198 mMaxSpinBox->setSpecialValueText( tr( "Not set" ) );
2199 }
2200
2201 w->setToolTip( parameterDefinition()->toolTip() );
2202
2203 connect( mMinSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, [this]( const double v ) {
2204 mBlockChangedSignal++;
2205 if ( !mAllowingNull && v > mMaxSpinBox->value() )
2206 mMaxSpinBox->setValue( v );
2207 mBlockChangedSignal--;
2208
2209 if ( !mBlockChangedSignal )
2210 emit widgetValueHasChanged( this );
2211 } );
2212 connect( mMaxSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, [this]( const double v ) {
2213 mBlockChangedSignal++;
2214 if ( !mAllowingNull && v < mMinSpinBox->value() )
2215 mMinSpinBox->setValue( v );
2216 mBlockChangedSignal--;
2217
2218 if ( !mBlockChangedSignal )
2219 emit widgetValueHasChanged( this );
2220 } );
2221
2222 return w;
2223 }
2224 }
2225 return nullptr;
2226}
2227
2228void QgsProcessingRangeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
2229{
2230 const QList<double> v = QgsProcessingParameters::parameterAsRange( parameterDefinition(), value, context );
2231 if ( mAllowingNull && v.empty() )
2232 {
2233 mMinSpinBox->clear();
2234 mMaxSpinBox->clear();
2235 }
2236 else
2237 {
2238 if ( v.empty() )
2239 return;
2240
2241 if ( mAllowingNull )
2242 {
2243 mBlockChangedSignal++;
2244 if ( std::isnan( v.at( 0 ) ) )
2245 mMinSpinBox->clear();
2246 else
2247 mMinSpinBox->setValue( v.at( 0 ) );
2248
2249 if ( v.count() >= 2 )
2250 {
2251 if ( std::isnan( v.at( 1 ) ) )
2252 mMaxSpinBox->clear();
2253 else
2254 mMaxSpinBox->setValue( v.at( 1 ) );
2255 }
2256 mBlockChangedSignal--;
2257 }
2258 else
2259 {
2260 mBlockChangedSignal++;
2261 mMinSpinBox->setValue( v.at( 0 ) );
2262 if ( v.count() >= 2 )
2263 mMaxSpinBox->setValue( v.at( 1 ) );
2264 mBlockChangedSignal--;
2265 }
2266 }
2267
2268 if ( !mBlockChangedSignal )
2269 emit widgetValueHasChanged( this );
2270}
2271
2272QVariant QgsProcessingRangeWidgetWrapper::widgetValue() const
2273{
2274 if ( mAllowingNull )
2275 {
2276 QString value;
2277 if ( qgsDoubleNear( mMinSpinBox->value(), mMinSpinBox->minimum() ) )
2278 value = u"None"_s;
2279 else
2280 value = QString::number( mMinSpinBox->value() );
2281
2282 if ( qgsDoubleNear( mMaxSpinBox->value(), mMaxSpinBox->minimum() ) )
2283 value += ",None"_L1;
2284 else
2285 value += u",%1"_s.arg( mMaxSpinBox->value() );
2286
2287 return value;
2288 }
2289 else
2290 return u"%1,%2"_s.arg( mMinSpinBox->value() ).arg( mMaxSpinBox->value() );
2291}
2292
2293QString QgsProcessingRangeWidgetWrapper::modelerExpressionFormatString() const
2294{
2295 return tr( "string as two comma delimited floats, e.g. '1,10'" );
2296}
2297
2298QString QgsProcessingRangeWidgetWrapper::parameterType() const
2299{
2301}
2302
2303QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingRangeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
2304{
2305 return new QgsProcessingRangeWidgetWrapper( parameter, type );
2306}
2307
2308QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingRangeWidgetWrapper::createParameterDefinitionWidget(
2310)
2311{
2312 return new QgsProcessingRangeParameterDefinitionWidget( context, widgetContext, definition, algorithm );
2313}
2314
2315
2316//
2317// QgsProcessingMatrixWidgetWrapper
2318//
2319
2320QgsProcessingMatrixParameterDefinitionWidget::QgsProcessingMatrixParameterDefinitionWidget(
2321 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
2322)
2323 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
2324{
2325 QVBoxLayout *vlayout = new QVBoxLayout();
2326 vlayout->setContentsMargins( 0, 0, 0, 0 );
2327
2328 mMatrixWidget = new QgsProcessingMatrixModelerWidget();
2329 if ( const QgsProcessingParameterMatrix *matrixParam = dynamic_cast<const QgsProcessingParameterMatrix *>( definition ) )
2330 {
2331 mMatrixWidget->setValue( matrixParam->headers(), matrixParam->defaultValueForGui() );
2332 mMatrixWidget->setFixedRows( matrixParam->hasFixedNumberRows() );
2333 }
2334 vlayout->addWidget( mMatrixWidget );
2335
2336 connect( mMatrixWidget, &QgsProcessingMatrixModelerWidget::changed, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2337
2338 setLayout( vlayout );
2339}
2340
2341QgsProcessingParameterDefinition *QgsProcessingMatrixParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
2342{
2343 auto param = std::make_unique<QgsProcessingParameterMatrix>( name, description, 1, mMatrixWidget->fixedRows(), mMatrixWidget->headers(), mMatrixWidget->value() );
2344 param->setFlags( flags );
2345 return param.release();
2346}
2347
2348
2349QgsProcessingMatrixWidgetWrapper::QgsProcessingMatrixWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
2350 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
2351{}
2352
2353QWidget *QgsProcessingMatrixWidgetWrapper::createWidget()
2354{
2355 mMatrixWidget = new QgsProcessingMatrixParameterPanel( nullptr, dynamic_cast<const QgsProcessingParameterMatrix *>( parameterDefinition() ) );
2356 mMatrixWidget->setToolTip( parameterDefinition()->toolTip() );
2357
2358 connect( mMatrixWidget, &QgsProcessingMatrixParameterPanel::changed, this, [this] { emit widgetValueHasChanged( this ); } );
2359
2360 switch ( type() )
2361 {
2365 {
2366 return mMatrixWidget;
2367 }
2368 }
2369 return nullptr;
2370}
2371
2372void QgsProcessingMatrixWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
2373{
2374 const QVariantList v = QgsProcessingParameters::parameterAsMatrix( parameterDefinition(), value, context );
2375 if ( mMatrixWidget )
2376 mMatrixWidget->setValue( v );
2377}
2378
2379QVariant QgsProcessingMatrixWidgetWrapper::widgetValue() const
2380{
2381 if ( mMatrixWidget )
2382 return mMatrixWidget->value().isEmpty() ? QVariant() : mMatrixWidget->value();
2383 else
2384 return QVariant();
2385}
2386
2387QString QgsProcessingMatrixWidgetWrapper::modelerExpressionFormatString() const
2388{
2389 return tr( "comma delimited string of values, or an array of values" );
2390}
2391
2392QString QgsProcessingMatrixWidgetWrapper::parameterType() const
2393{
2395}
2396
2397QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingMatrixWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
2398{
2399 return new QgsProcessingMatrixWidgetWrapper( parameter, type );
2400}
2401
2402QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingMatrixWidgetWrapper::createParameterDefinitionWidget(
2404)
2405{
2406 return new QgsProcessingMatrixParameterDefinitionWidget( context, widgetContext, definition, algorithm );
2407}
2408
2409
2410//
2411// QgsProcessingFileWidgetWrapper
2412//
2413
2414
2415QgsProcessingFileParameterDefinitionWidget::QgsProcessingFileParameterDefinitionWidget(
2416 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
2417)
2418 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
2419{
2420 QVBoxLayout *vlayout = new QVBoxLayout();
2421 vlayout->setContentsMargins( 0, 0, 0, 0 );
2422
2423 vlayout->addWidget( new QLabel( tr( "Type" ) ) );
2424
2425 mTypeComboBox = new QComboBox();
2426 mTypeComboBox->addItem( tr( "File" ), static_cast<int>( Qgis::ProcessingFileParameterBehavior::File ) );
2427 mTypeComboBox->addItem( tr( "Folder" ), static_cast<int>( Qgis::ProcessingFileParameterBehavior::Folder ) );
2428 if ( const QgsProcessingParameterFile *fileParam = dynamic_cast<const QgsProcessingParameterFile *>( definition ) )
2429 mTypeComboBox->setCurrentIndex( mTypeComboBox->findData( static_cast<int>( fileParam->behavior() ) ) );
2430 else
2431 mTypeComboBox->setCurrentIndex( 0 );
2432 vlayout->addWidget( mTypeComboBox );
2433
2434 connect( mTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2435
2436 vlayout->addWidget( new QLabel( tr( "File filter" ) ) );
2437
2438 mFilterComboBox = new QComboBox();
2439 mFilterComboBox->setEditable( true );
2440 // add some standard ones -- these also act as a demonstration of the required format
2441 mFilterComboBox->addItem( tr( "All Files (*.*)" ) );
2442 mFilterComboBox->addItem( tr( "CSV Files (*.csv)" ) );
2443 mFilterComboBox->addItem( tr( "HTML Files (*.html *.htm)" ) );
2444 mFilterComboBox->addItem( tr( "Text Files (*.txt)" ) );
2445 if ( const QgsProcessingParameterFile *fileParam = dynamic_cast<const QgsProcessingParameterFile *>( definition ) )
2446 mFilterComboBox->setCurrentText( fileParam->fileFilter() );
2447 else
2448 mFilterComboBox->setCurrentIndex( 0 );
2449 vlayout->addWidget( mFilterComboBox );
2450
2451 connect( mFilterComboBox, &QComboBox::currentTextChanged, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2452
2453 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
2454
2455 mDefaultFileWidget = new QgsFileWidget();
2456 mDefaultFileWidget->lineEdit()->setShowClearButton( true );
2457 if ( const QgsProcessingParameterFile *fileParam = dynamic_cast<const QgsProcessingParameterFile *>( definition ) )
2458 {
2459 mDefaultFileWidget->setStorageMode( fileParam->behavior() == Qgis::ProcessingFileParameterBehavior::File ? QgsFileWidget::GetFile : QgsFileWidget::GetDirectory );
2460 mDefaultFileWidget->setFilePath( fileParam->defaultValueForGui().toString() );
2461 }
2462 else
2463 mDefaultFileWidget->setStorageMode( QgsFileWidget::GetFile );
2464 vlayout->addWidget( mDefaultFileWidget );
2466
2467 connect( mTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this] {
2468 Qgis::ProcessingFileParameterBehavior behavior = static_cast<Qgis::ProcessingFileParameterBehavior>( mTypeComboBox->currentData().toInt() );
2469 mFilterComboBox->setEnabled( behavior == Qgis::ProcessingFileParameterBehavior::File );
2470 mDefaultFileWidget->setStorageMode( behavior == Qgis::ProcessingFileParameterBehavior::File ? QgsFileWidget::GetFile : QgsFileWidget::GetDirectory );
2471 } );
2472 mFilterComboBox->setEnabled( static_cast<Qgis::ProcessingFileParameterBehavior>( mTypeComboBox->currentData().toInt() ) == Qgis::ProcessingFileParameterBehavior::File );
2473
2474
2475 setLayout( vlayout );
2476}
2477
2478QgsProcessingParameterDefinition *QgsProcessingFileParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
2479{
2480 auto param = std::make_unique<QgsProcessingParameterFile>( name, description );
2481 param->setBehavior( static_cast<Qgis::ProcessingFileParameterBehavior>( mTypeComboBox->currentData().toInt() ) );
2482 if ( param->behavior() == Qgis::ProcessingFileParameterBehavior::File )
2483 param->setFileFilter( mFilterComboBox->currentText() );
2484 if ( !mDefaultFileWidget->filePath().isEmpty() )
2485 param->setDefaultValue( mDefaultFileWidget->filePath() );
2486 param->setFlags( flags );
2487 return param.release();
2488}
2489
2490
2491QgsProcessingFileWidgetWrapper::QgsProcessingFileWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
2492 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
2493{}
2494
2495QWidget *QgsProcessingFileWidgetWrapper::createWidget()
2496{
2497 const QgsProcessingParameterFile *fileParam = dynamic_cast<const QgsProcessingParameterFile *>( parameterDefinition() );
2498 if ( !fileParam )
2499 return nullptr;
2500
2501 switch ( type() )
2502 {
2506 {
2507 mFileWidget = new QgsFileWidget();
2508 mFileWidget->setToolTip( parameterDefinition()->toolTip() );
2509 mFileWidget->setDialogTitle( parameterDefinition()->description() );
2510
2511 mFileWidget->setDefaultRoot( QgsSettings().value( u"/Processing/LastInputPath"_s, QDir::homePath() ).toString() );
2512
2513 switch ( fileParam->behavior() )
2514 {
2516 mFileWidget->setStorageMode( QgsFileWidget::GetFile );
2517 if ( !fileParam->fileFilter().isEmpty() )
2518 mFileWidget->setFilter( fileParam->fileFilter() );
2519 else if ( !fileParam->extension().isEmpty() )
2520 mFileWidget->setFilter( tr( "%1 files" ).arg( fileParam->extension().toUpper() ) + u" (*."_s + fileParam->extension().toLower() + ')' );
2521 break;
2522
2524 mFileWidget->setStorageMode( QgsFileWidget::GetDirectory );
2525 break;
2526 }
2527
2528 connect( mFileWidget, &QgsFileWidget::fileChanged, this, [this]( const QString &path ) {
2529 QgsSettings().setValue( u"/Processing/LastInputPath"_s, QFileInfo( path ).canonicalPath() );
2530 emit widgetValueHasChanged( this );
2531 } );
2532 return mFileWidget;
2533 }
2534 }
2535 return nullptr;
2536}
2537
2538void QgsProcessingFileWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
2539{
2540 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
2541 if ( mFileWidget )
2542 mFileWidget->setFilePath( v );
2543}
2544
2545QVariant QgsProcessingFileWidgetWrapper::widgetValue() const
2546{
2547 if ( mFileWidget )
2548 return mFileWidget->filePath();
2549 else
2550 return QVariant();
2551}
2552
2553QString QgsProcessingFileWidgetWrapper::modelerExpressionFormatString() const
2554{
2555 return tr( "string representing a path to a file or folder" );
2556}
2557
2558QString QgsProcessingFileWidgetWrapper::parameterType() const
2559{
2561}
2562
2563QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFileWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
2564{
2565 return new QgsProcessingFileWidgetWrapper( parameter, type );
2566}
2567
2568QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingFileWidgetWrapper::createParameterDefinitionWidget(
2570)
2571{
2572 return new QgsProcessingFileParameterDefinitionWidget( context, widgetContext, definition, algorithm );
2573}
2574
2575
2576//
2577// QgsProcessingExpressionWidgetWrapper
2578//
2579
2580QgsProcessingExpressionParameterDefinitionWidget::QgsProcessingExpressionParameterDefinitionWidget(
2581 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
2582)
2583 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
2584{
2585 QVBoxLayout *vlayout = new QVBoxLayout();
2586 vlayout->setContentsMargins( 0, 0, 0, 0 );
2587 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
2588
2589 mDefaultQgisLineEdit = new QgsExpressionLineEdit();
2590 mDefaultQgisLineEdit->registerExpressionContextGenerator( this );
2591
2592 mDefaultPointCloudLineEdit = new QgsProcessingPointCloudExpressionLineEdit();
2593 mDefaultRasterCalculatorLineEdit = new QgsProcessingRasterCalculatorExpressionLineEdit();
2594
2595 QStackedWidget *stackedWidget = new QStackedWidget();
2596 stackedWidget->addWidget( mDefaultQgisLineEdit );
2597 stackedWidget->addWidget( mDefaultPointCloudLineEdit );
2598 stackedWidget->addWidget( mDefaultRasterCalculatorLineEdit );
2599 vlayout->addWidget( stackedWidget );
2600
2602 connect( mDefaultPointCloudLineEdit, &QgsProcessingPointCloudExpressionLineEdit::expressionChanged, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2603 connect( mDefaultRasterCalculatorLineEdit, &QgsProcessingRasterCalculatorExpressionLineEdit::expressionChanged, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2604
2605 if ( const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( definition ) )
2606 {
2607 const QString expr = QgsProcessingParameters::parameterAsExpression( expParam, expParam->defaultValueForGui(), context );
2608 mDefaultQgisLineEdit->setExpression( expr );
2609 mDefaultPointCloudLineEdit->setExpression( expr );
2610 }
2611
2612 vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
2613
2614 mParentLayerComboBox = new QComboBox();
2615 vlayout->addWidget( mParentLayerComboBox );
2616
2617 vlayout->addWidget( new QLabel( tr( "Expression type" ) ) );
2618 mExpressionTypeComboBox = new QComboBox();
2619 mExpressionTypeComboBox->addItem( tr( "QGIS" ), static_cast<int>( Qgis::ExpressionType::Qgis ) );
2620 mExpressionTypeComboBox->addItem( tr( "Point Cloud" ), static_cast<int>( Qgis::ExpressionType::PointCloud ) );
2621 mExpressionTypeComboBox->addItem( tr( "Raster Calculator" ), static_cast<int>( Qgis::ExpressionType::RasterCalculator ) );
2622
2623 connect( mExpressionTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [this, stackedWidget, definition, widgetContext]( int ) {
2624 mParentLayerComboBox->clear();
2625 mParentLayerComboBox->addItem( tr( "None" ), QVariant() );
2626
2627 stackedWidget->setCurrentIndex( mExpressionTypeComboBox->currentIndex() > 0 ? mExpressionTypeComboBox->currentIndex() : 0 );
2628
2629 QString initialParent;
2630 if ( const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( definition ) )
2631 initialParent = expParam->parentLayerParameterName();
2632
2633 Qgis::ExpressionType exprType = static_cast<Qgis::ExpressionType>( mExpressionTypeComboBox->currentData().toInt() );
2634
2635 if ( QgsProcessingModelAlgorithm *model = widgetContext.model() )
2636 {
2637 // populate combo box with other model input choices
2638 const QMap<QString, QgsProcessingModelParameter> components = model->parameterComponents();
2639 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
2640 {
2641 switch ( exprType )
2642 {
2644 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( model->parameterDefinition( it.value().parameterName() ) ) )
2645 {
2646 mParentLayerComboBox->addItem( definition->description(), definition->name() );
2647 if ( !initialParent.isEmpty() && initialParent == definition->name() )
2648 {
2649 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
2650 }
2651 }
2652 else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast<const QgsProcessingParameterVectorLayer *>( model->parameterDefinition( it.value().parameterName() ) ) )
2653 {
2654 mParentLayerComboBox->addItem( definition->description(), definition->name() );
2655 if ( !initialParent.isEmpty() && initialParent == definition->name() )
2656 {
2657 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
2658 }
2659 }
2660 break;
2662 if ( const QgsProcessingParameterPointCloudLayer *definition = dynamic_cast<const QgsProcessingParameterPointCloudLayer *>( model->parameterDefinition( it.value().parameterName() ) ) )
2663 {
2664 mParentLayerComboBox->addItem( definition->description(), definition->name() );
2665 if ( !initialParent.isEmpty() && initialParent == definition->name() )
2666 {
2667 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
2668 }
2669 }
2670 break;
2672 if ( const QgsProcessingParameterMultipleLayers *definition = dynamic_cast<const QgsProcessingParameterMultipleLayers *>( model->parameterDefinition( it.value().parameterName() ) ) )
2673 {
2674 if ( definition->layerType() != Qgis::ProcessingSourceType::Raster )
2675 {
2676 continue;
2677 }
2678 mParentLayerComboBox->addItem( definition->description(), definition->name() );
2679 if ( !initialParent.isEmpty() && initialParent == definition->name() )
2680 {
2681 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
2682 }
2683 }
2684 break;
2685 }
2686 }
2687 }
2688
2689 if ( mParentLayerComboBox->count() == 1 && !initialParent.isEmpty() )
2690 {
2691 // if no parent candidates found, we just add the existing one as a placeholder
2692 mParentLayerComboBox->addItem( initialParent, initialParent );
2693 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
2694 }
2695 } );
2696
2697 mExpressionTypeComboBox->setCurrentIndex( -1 );
2698 if ( const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( definition ) )
2699 mExpressionTypeComboBox->setCurrentIndex( mExpressionTypeComboBox->findData( static_cast<int>( expParam->expressionType() ) ) );
2700 else
2701 mExpressionTypeComboBox->setCurrentIndex( 0 );
2702
2703 vlayout->addWidget( mExpressionTypeComboBox );
2704
2705 connect( mExpressionTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2706 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
2707
2708 setLayout( vlayout );
2709}
2710
2711QgsProcessingParameterDefinition *QgsProcessingExpressionParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
2712{
2713 Qgis::ExpressionType expressionType = static_cast<Qgis::ExpressionType>( mExpressionTypeComboBox->currentData().toInt() );
2714 QString expression;
2715 switch ( expressionType )
2716 {
2718 expression = mDefaultQgisLineEdit->expression();
2719 break;
2721 expression = mDefaultPointCloudLineEdit->expression();
2722 break;
2724 expression = mDefaultRasterCalculatorLineEdit->expression();
2725 break;
2726 }
2727 auto param = std::make_unique<QgsProcessingParameterExpression>( name, description, expression, mParentLayerComboBox->currentData().toString(), false, expressionType );
2728 param->setFlags( flags );
2729 return param.release();
2730}
2731
2732QgsProcessingExpressionWidgetWrapper::QgsProcessingExpressionWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
2733 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
2734{}
2735
2736QWidget *QgsProcessingExpressionWidgetWrapper::createWidget()
2737{
2738 const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( parameterDefinition() );
2739 if ( !expParam )
2740 return nullptr;
2741
2742 switch ( type() )
2743 {
2747 {
2748 if ( expParam->parentLayerParameterName().isEmpty() )
2749 {
2750 mExpLineEdit = new QgsExpressionLineEdit();
2751 mExpLineEdit->setToolTip( parameterDefinition()->toolTip() );
2752 mExpLineEdit->setExpressionDialogTitle( parameterDefinition()->description() );
2753 mExpLineEdit->registerExpressionContextGenerator( this );
2754 connect( mExpLineEdit, &QgsExpressionLineEdit::expressionChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
2755 return mExpLineEdit;
2756 }
2757 else
2758 {
2760 {
2761 mPointCloudExpLineEdit = new QgsProcessingPointCloudExpressionLineEdit();
2762 mPointCloudExpLineEdit->setToolTip( parameterDefinition()->toolTip() );
2763 connect( mPointCloudExpLineEdit, &QgsProcessingPointCloudExpressionLineEdit::expressionChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
2764 return mPointCloudExpLineEdit;
2765 }
2766
2768 {
2769 mRasterCalculatorExpLineEdit = new QgsProcessingRasterCalculatorExpressionLineEdit();
2770 mRasterCalculatorExpLineEdit->setToolTip( parameterDefinition()->toolTip() );
2771 if ( type() == Qgis::ProcessingMode::Modeler )
2772 {
2773 mRasterCalculatorExpLineEdit->setLayers( QVariantList() << "A" << "B" << "C" << "D" << "E" << "F" << "G" );
2774 }
2775 connect( mRasterCalculatorExpLineEdit, &QgsProcessingRasterCalculatorExpressionLineEdit::expressionChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
2776 return mRasterCalculatorExpLineEdit;
2777 }
2778
2779 // native QGIS expression
2780 if ( expParam->metadata().value( u"inlineEditor"_s ).toBool() )
2781 {
2782 mExpBuilderWidget = new QgsExpressionBuilderWidget();
2783 mExpBuilderWidget->setToolTip( parameterDefinition()->toolTip() );
2784 mExpBuilderWidget->init( createExpressionContext() );
2785 connect( mExpBuilderWidget, &QgsExpressionBuilderWidget::expressionParsed, this, [this]( bool changed ) {
2786 Q_UNUSED( changed );
2787 emit widgetValueHasChanged( this );
2788 } );
2789 return mExpBuilderWidget;
2790 }
2791 else
2792 {
2793 mFieldExpWidget = new QgsFieldExpressionWidget();
2794 mFieldExpWidget->setToolTip( parameterDefinition()->toolTip() );
2795 mFieldExpWidget->setExpressionDialogTitle( parameterDefinition()->description() );
2796 mFieldExpWidget->registerExpressionContextGenerator( this );
2798 mFieldExpWidget->setAllowEmptyFieldName( true );
2799
2800 connect( mFieldExpWidget, static_cast<void ( QgsFieldExpressionWidget::* )( const QString & )>( &QgsFieldExpressionWidget::fieldChanged ), this, [this]( const QString & ) {
2801 emit widgetValueHasChanged( this );
2802 } );
2803 return mFieldExpWidget;
2804 }
2805 }
2806 }
2807 }
2808 return nullptr;
2809}
2810
2811void QgsProcessingExpressionWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
2812{
2814 switch ( type() )
2815 {
2818 {
2819 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
2820 {
2821 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterExpression *>( parameterDefinition() )->parentLayerParameterName() )
2822 {
2823 setParentLayerWrapperValue( wrapper );
2824 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentLayerWrapperValue( wrapper ); } );
2825 break;
2826 }
2827 }
2828 break;
2829 }
2830
2832 break;
2833 }
2834}
2835
2836void QgsProcessingExpressionWidgetWrapper::registerProcessingContextGenerator( QgsProcessingContextGenerator *generator )
2837{
2839 if ( mExpBuilderWidget )
2840 {
2841 // we need to regenerate the expression context for use by this widget -- it doesn't fetch automatically on demand
2842 mExpBuilderWidget->setExpressionContext( createExpressionContext() );
2843 }
2844}
2845
2846void QgsProcessingExpressionWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
2847{
2848 // evaluate value to layer
2849 QgsProcessingContext *context = nullptr;
2850 std::unique_ptr<QgsProcessingContext> tmpContext;
2851 if ( mProcessingContextGenerator )
2852 context = mProcessingContextGenerator->processingContext();
2853
2854 if ( !context )
2855 {
2856 tmpContext = std::make_unique<QgsProcessingContext>();
2857 context = tmpContext.get();
2858 }
2859
2860 QVariant val = parentWrapper->parameterValue();
2861
2862 const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( parameterDefinition() );
2863 if ( !expParam )
2864 return;
2865
2866 switch ( expParam->expressionType() )
2867 {
2869 {
2870 if ( val.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
2871 {
2872 // input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
2873 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
2874 val = fromVar.source;
2875 }
2876
2878 if ( !layer )
2879 {
2880 if ( mFieldExpWidget )
2881 mFieldExpWidget->setLayer( nullptr );
2882 else if ( mExpBuilderWidget )
2883 mExpBuilderWidget->setLayer( nullptr );
2884 else if ( mExpLineEdit )
2885 mExpLineEdit->setLayer( nullptr );
2886 return;
2887 }
2888
2889 // need to grab ownership of layer if required - otherwise layer may be deleted when context
2890 // goes out of scope
2891 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
2892 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Vector )
2893 {
2894 mParentLayer = std::move( ownedLayer );
2895 layer = static_cast<QgsVectorLayer *>( mParentLayer.get() );
2896 }
2897 else
2898 {
2899 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
2900 }
2901
2902 if ( mFieldExpWidget )
2903 mFieldExpWidget->setLayer( layer );
2904 if ( mExpBuilderWidget )
2905 mExpBuilderWidget->setLayer( layer );
2906 else if ( mExpLineEdit )
2907 mExpLineEdit->setLayer( layer );
2908
2909 break;
2910 }
2912 {
2914 if ( !layer )
2915 {
2916 if ( mPointCloudExpLineEdit )
2917 mPointCloudExpLineEdit->setLayer( nullptr );
2918 return;
2919 }
2920
2921 // need to grab ownership of layer if required - otherwise layer may be deleted when context
2922 // goes out of scope
2923 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
2924 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::PointCloud )
2925 {
2926 mParentLayer = std::move( ownedLayer );
2927 layer = static_cast<QgsPointCloudLayer *>( mParentLayer.get() );
2928 }
2929 else
2930 {
2931 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
2932 }
2933
2934 if ( mPointCloudExpLineEdit )
2935 mPointCloudExpLineEdit->setLayer( layer );
2936
2937 break;
2938 }
2940 {
2942 if ( layers.isEmpty() )
2943 {
2944 if ( mRasterCalculatorExpLineEdit )
2945 {
2946 mRasterCalculatorExpLineEdit->setLayers( val.userType() == QMetaType::Type::QVariantList ? val.toList() : QVariantList() << val );
2947 }
2948 return;
2949 }
2950
2951 if ( mRasterCalculatorExpLineEdit )
2952 {
2953 QVariantList layersList;
2954 for ( QgsMapLayer *layer : layers )
2955 {
2956 layersList << layer->name();
2957 }
2958 mRasterCalculatorExpLineEdit->setLayers( layersList );
2959 }
2960
2961 break;
2962 }
2963 }
2964}
2965
2966void QgsProcessingExpressionWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
2967{
2968 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
2969 if ( mFieldExpWidget )
2970 mFieldExpWidget->setExpression( v );
2971 else if ( mExpBuilderWidget )
2972 mExpBuilderWidget->setExpressionText( v );
2973 else if ( mExpLineEdit )
2974 mExpLineEdit->setExpression( v );
2975 else if ( mPointCloudExpLineEdit )
2976 mPointCloudExpLineEdit->setExpression( v );
2977 else if ( mRasterCalculatorExpLineEdit )
2978 mRasterCalculatorExpLineEdit->setExpression( v );
2979}
2980
2981QVariant QgsProcessingExpressionWidgetWrapper::widgetValue() const
2982{
2983 if ( mFieldExpWidget )
2984 return mFieldExpWidget->expression();
2985 if ( mExpBuilderWidget )
2986 return mExpBuilderWidget->expressionText();
2987 else if ( mExpLineEdit )
2988 return mExpLineEdit->expression();
2989 else if ( mPointCloudExpLineEdit )
2990 return mPointCloudExpLineEdit->expression();
2991 else if ( mRasterCalculatorExpLineEdit )
2992 return mRasterCalculatorExpLineEdit->expression();
2993 else
2994 return QVariant();
2995}
2996
2997QString QgsProcessingExpressionWidgetWrapper::modelerExpressionFormatString() const
2998{
2999 return tr( "string representation of an expression" );
3000}
3001
3002const QgsVectorLayer *QgsProcessingExpressionWidgetWrapper::linkedVectorLayer() const
3003{
3004 if ( mFieldExpWidget && mFieldExpWidget->layer() )
3005 return mFieldExpWidget->layer();
3006
3007 if ( mExpBuilderWidget && mExpBuilderWidget->layer() )
3008 return mExpBuilderWidget->layer();
3009
3011}
3012
3013QString QgsProcessingExpressionWidgetWrapper::parameterType() const
3014{
3016}
3017
3018QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingExpressionWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
3019{
3020 return new QgsProcessingExpressionWidgetWrapper( parameter, type );
3021}
3022
3023QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingExpressionWidgetWrapper::createParameterDefinitionWidget(
3025)
3026{
3027 return new QgsProcessingExpressionParameterDefinitionWidget( context, widgetContext, definition, algorithm );
3028}
3029
3030
3031//
3032// QgsProcessingEnumPanelWidget
3033//
3034
3035QgsProcessingEnumPanelWidget::QgsProcessingEnumPanelWidget( QWidget *parent, const QgsProcessingParameterEnum *param )
3036 : QWidget( parent )
3037 , mParam( param )
3038{
3039 QHBoxLayout *hl = new QHBoxLayout();
3040 hl->setContentsMargins( 0, 0, 0, 0 );
3041
3042 mLineEdit = new QLineEdit();
3043 mLineEdit->setEnabled( false );
3044 hl->addWidget( mLineEdit, 1 );
3045
3046 mToolButton = new QToolButton();
3047 mToolButton->setText( QString( QChar( 0x2026 ) ) );
3048 hl->addWidget( mToolButton );
3049
3050 setLayout( hl );
3051
3052 if ( mParam )
3053 {
3054 mLineEdit->setText( tr( "%1 options selected" ).arg( 0 ) );
3055 }
3056
3057 connect( mToolButton, &QToolButton::clicked, this, &QgsProcessingEnumPanelWidget::showDialog );
3058}
3059
3060void QgsProcessingEnumPanelWidget::setValue( const QVariant &value )
3061{
3062 if ( value.isValid() )
3063 {
3064 mValue = value.userType() == QMetaType::Type::QVariantList ? value.toList() : QVariantList() << value;
3065
3066 if ( mParam->usesStaticStrings() && mValue.count() == 1 && mValue.at( 0 ).toString().isEmpty() )
3067 mValue.clear();
3068 }
3069 else
3070 mValue.clear();
3071
3072 updateSummaryText();
3073 emit changed();
3074}
3075
3076void QgsProcessingEnumPanelWidget::showDialog()
3077{
3078 QVariantList availableOptions;
3079 if ( mParam )
3080 {
3081 availableOptions.reserve( mParam->options().size() );
3082
3083 if ( mParam->usesStaticStrings() )
3084 {
3085 for ( QString o : mParam->options() )
3086 {
3087 availableOptions << o;
3088 }
3089 }
3090 else
3091 {
3092 for ( int i = 0; i < mParam->options().count(); ++i )
3093 availableOptions << i;
3094 }
3095 }
3096
3097 const QStringList options = mParam ? mParam->options() : QStringList();
3099 if ( panel && panel->dockMode() )
3100 {
3101 QgsProcessingMultipleSelectionPanelWidget *widget = new QgsProcessingMultipleSelectionPanelWidget( availableOptions, mValue );
3102 widget->setPanelTitle( mParam->description() );
3103
3104 if ( mParam->usesStaticStrings() )
3105 {
3106 widget->setValueFormatter( [options]( const QVariant &v ) -> QString {
3107 const QString i = v.toString();
3108 return options.contains( i ) ? i : QString();
3109 } );
3110 }
3111 else
3112 {
3113 widget->setValueFormatter( [options]( const QVariant &v ) -> QString {
3114 const int i = v.toInt();
3115 return options.size() > i ? options.at( i ) : QString();
3116 } );
3117 }
3118
3119 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [this, widget]() { setValue( widget->selectedOptions() ); } );
3120 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
3121 panel->openPanel( widget );
3122 }
3123 else
3124 {
3125 QgsProcessingMultipleSelectionDialog dlg( availableOptions, mValue, this, Qt::WindowFlags() );
3126
3127 dlg.setValueFormatter( [options]( const QVariant &v ) -> QString {
3128 const int i = v.toInt();
3129 return options.size() > i ? options.at( i ) : QString();
3130 } );
3131 if ( dlg.exec() )
3132 {
3133 setValue( dlg.selectedOptions() );
3134 }
3135 }
3136}
3137
3138void QgsProcessingEnumPanelWidget::updateSummaryText()
3139{
3140 if ( !mParam )
3141 return;
3142
3143 if ( mValue.empty() )
3144 {
3145 mLineEdit->setText( tr( "%1 options selected" ).arg( 0 ) );
3146 }
3147 else
3148 {
3149 QStringList values;
3150 values.reserve( mValue.size() );
3151 if ( mParam->usesStaticStrings() )
3152 {
3153 for ( const QVariant &val : std::as_const( mValue ) )
3154 {
3155 values << val.toString();
3156 }
3157 }
3158 else
3159 {
3160 const QStringList options = mParam->options();
3161 for ( const QVariant &val : std::as_const( mValue ) )
3162 {
3163 const int i = val.toInt();
3164 values << ( options.size() > i ? options.at( i ) : QString() );
3165 }
3166 }
3167
3168 const QString concatenated = values.join( tr( "," ) );
3169 if ( concatenated.length() < 100 )
3170 mLineEdit->setText( concatenated );
3171 else
3172 mLineEdit->setText( tr( "%n option(s) selected", nullptr, mValue.count() ) );
3173 }
3174}
3175
3176
3177//
3178// QgsProcessingEnumCheckboxPanelWidget
3179//
3180QgsProcessingEnumCheckboxPanelWidget::QgsProcessingEnumCheckboxPanelWidget( QWidget *parent, const QgsProcessingParameterEnum *param, int columns )
3181 : QWidget( parent )
3182 , mParam( param )
3183 , mButtonGroup( new QButtonGroup( this ) )
3184 , mColumns( columns )
3185{
3186 mButtonGroup->setExclusive( !mParam->allowMultiple() );
3187
3188 QGridLayout *l = new QGridLayout();
3189 l->setContentsMargins( 0, 0, 0, 0 );
3190
3191 int rows = static_cast<int>( std::ceil( mParam->options().count() / static_cast<double>( mColumns ) ) );
3192 for ( int i = 0; i < mParam->options().count(); ++i )
3193 {
3194 QAbstractButton *button = nullptr;
3195 if ( mParam->allowMultiple() )
3196 button = new QCheckBox( mParam->options().at( i ) );
3197 else
3198 button = new QRadioButton( mParam->options().at( i ) );
3199
3200 connect( button, &QAbstractButton::toggled, this, [this] {
3201 if ( !mBlockChangedSignal )
3202 emit changed();
3203 } );
3204
3205 mButtons.insert( i, button );
3206
3207 mButtonGroup->addButton( button, i );
3208 l->addWidget( button, i % rows, i / rows );
3209 }
3210 l->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum ), 0, mColumns );
3211 setLayout( l );
3212
3213 if ( mParam->allowMultiple() )
3214 {
3215 setContextMenuPolicy( Qt::CustomContextMenu );
3216 connect( this, &QWidget::customContextMenuRequested, this, &QgsProcessingEnumCheckboxPanelWidget::showPopupMenu );
3217 }
3218}
3219
3220QVariant QgsProcessingEnumCheckboxPanelWidget::value() const
3221{
3222 if ( mParam->allowMultiple() )
3223 {
3224 QVariantList value;
3225 for ( auto it = mButtons.constBegin(); it != mButtons.constEnd(); ++it )
3226 {
3227 if ( it.value()->isChecked() )
3228 value.append( mParam->usesStaticStrings() ? mParam->options().at( it.key().toInt() ) : it.key() );
3229 }
3230 return value;
3231 }
3232 else
3233 {
3234 if ( mParam->usesStaticStrings() )
3235 return mButtonGroup->checkedId() >= 0 ? mParam->options().at( mButtonGroup->checkedId() ) : QVariant();
3236 else
3237 return mButtonGroup->checkedId() >= 0 ? mButtonGroup->checkedId() : QVariant();
3238 }
3239}
3240
3241void QgsProcessingEnumCheckboxPanelWidget::setValue( const QVariant &value )
3242{
3243 mBlockChangedSignal = true;
3244 if ( mParam->allowMultiple() )
3245 {
3246 QVariantList selected;
3247 if ( value.isValid() )
3248 selected = value.userType() == QMetaType::Type::QVariantList ? value.toList() : QVariantList() << value;
3249 for ( auto it = mButtons.constBegin(); it != mButtons.constEnd(); ++it )
3250 {
3251 QVariant v = mParam->usesStaticStrings() ? mParam->options().at( it.key().toInt() ) : it.key();
3252 it.value()->setChecked( selected.contains( v ) );
3253 }
3254 }
3255 else
3256 {
3257 QVariant v = value;
3258 if ( v.userType() == QMetaType::Type::QVariantList )
3259 v = v.toList().value( 0 );
3260
3261 v = mParam->usesStaticStrings() ? static_cast< int >( mParam->options().indexOf( v.toString() ) ) : v;
3262 if ( mButtons.contains( v ) )
3263 mButtons.value( v )->setChecked( true );
3264 }
3265 mBlockChangedSignal = false;
3266 emit changed();
3267}
3268
3269void QgsProcessingEnumCheckboxPanelWidget::showPopupMenu()
3270{
3271 QMenu popupMenu;
3272 QAction *selectAllAction = new QAction( tr( "Select All" ), &popupMenu );
3273 connect( selectAllAction, &QAction::triggered, this, &QgsProcessingEnumCheckboxPanelWidget::selectAll );
3274 QAction *clearAllAction = new QAction( tr( "Clear Selection" ), &popupMenu );
3275 connect( clearAllAction, &QAction::triggered, this, &QgsProcessingEnumCheckboxPanelWidget::deselectAll );
3276 popupMenu.addAction( selectAllAction );
3277 popupMenu.addAction( clearAllAction );
3278 popupMenu.exec( QCursor::pos() );
3279}
3280
3281void QgsProcessingEnumCheckboxPanelWidget::selectAll()
3282{
3283 mBlockChangedSignal = true;
3284 for ( auto it = mButtons.constBegin(); it != mButtons.constEnd(); ++it )
3285 it.value()->setChecked( true );
3286 mBlockChangedSignal = false;
3287 emit changed();
3288}
3289
3290void QgsProcessingEnumCheckboxPanelWidget::deselectAll()
3291{
3292 mBlockChangedSignal = true;
3293 for ( auto it = mButtons.constBegin(); it != mButtons.constEnd(); ++it )
3294 it.value()->setChecked( false );
3295 mBlockChangedSignal = false;
3296 emit changed();
3297}
3298
3299
3300//
3301// QgsProcessingEnumWidgetWrapper
3302//
3303
3304QgsProcessingEnumParameterDefinitionWidget::QgsProcessingEnumParameterDefinitionWidget(
3305 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
3306)
3307 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
3308{
3309 QVBoxLayout *vlayout = new QVBoxLayout();
3310 vlayout->setContentsMargins( 0, 0, 0, 0 );
3311
3312 mEnumWidget = new QgsProcessingEnumModelerWidget();
3313 if ( const QgsProcessingParameterEnum *enumParam = dynamic_cast<const QgsProcessingParameterEnum *>( definition ) )
3314 {
3315 mEnumWidget->setAllowMultiple( enumParam->allowMultiple() );
3316 mEnumWidget->setOptions( enumParam->options() );
3317 mEnumWidget->setDefaultOptions( enumParam->defaultValueForGui() );
3318 }
3319 vlayout->addWidget( mEnumWidget );
3320
3321 connect( mEnumWidget, &QgsProcessingEnumModelerWidget::changed, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
3322
3323 setLayout( vlayout );
3324}
3325
3326QgsProcessingParameterDefinition *QgsProcessingEnumParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
3327{
3328 auto param = std::make_unique<QgsProcessingParameterEnum>( name, description, mEnumWidget->options(), mEnumWidget->allowMultiple(), mEnumWidget->defaultOptions() );
3329 param->setFlags( flags );
3330 return param.release();
3331}
3332
3333
3334QgsProcessingEnumWidgetWrapper::QgsProcessingEnumWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
3335 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
3336{}
3337
3338QWidget *QgsProcessingEnumWidgetWrapper::createWidget()
3339{
3340 const QgsProcessingParameterEnum *expParam = dynamic_cast<const QgsProcessingParameterEnum *>( parameterDefinition() );
3341 if ( !expParam )
3342 return nullptr;
3343
3344 switch ( type() )
3345 {
3347 {
3348 // checkbox panel only for use outside in standard gui!
3349 if ( expParam->metadata().value( u"widget_wrapper"_s ).toMap().value( u"useCheckBoxes"_s, false ).toBool() )
3350 {
3351 const int columns = expParam->metadata().value( u"widget_wrapper"_s ).toMap().value( u"columns"_s, 2 ).toInt();
3352 mCheckboxPanel = new QgsProcessingEnumCheckboxPanelWidget( nullptr, expParam, columns );
3353 mCheckboxPanel->setToolTip( parameterDefinition()->toolTip() );
3354 connect( mCheckboxPanel, &QgsProcessingEnumCheckboxPanelWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
3355 return mCheckboxPanel;
3356 }
3357 }
3358 [[fallthrough]];
3361 {
3362 if ( expParam->allowMultiple() )
3363 {
3364 mPanel = new QgsProcessingEnumPanelWidget( nullptr, expParam );
3365 mPanel->setToolTip( parameterDefinition()->toolTip() );
3366 connect( mPanel, &QgsProcessingEnumPanelWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
3367 return mPanel;
3368 }
3369 else
3370 {
3371 mComboBox = new QComboBox();
3372
3374 mComboBox->addItem( tr( "[Not selected]" ), QVariant() );
3375 const QStringList options = expParam->options();
3376 const QVariantList iconList = expParam->metadata().value( u"widget_wrapper"_s ).toMap().value( u"icons"_s ).toList();
3377 for ( int i = 0; i < options.count(); ++i )
3378 {
3379 const QIcon icon = iconList.value( i ).value<QIcon>();
3380
3381 if ( expParam->usesStaticStrings() )
3382 mComboBox->addItem( icon, options.at( i ), options.at( i ) );
3383 else
3384 mComboBox->addItem( icon, options.at( i ), i );
3385 }
3386
3387 mComboBox->setToolTip( parameterDefinition()->toolTip() );
3388 mComboBox->setSizeAdjustPolicy( QComboBox::AdjustToMinimumContentsLengthWithIcon );
3389 connect( mComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this]( int ) { emit widgetValueHasChanged( this ); } );
3390 return mComboBox;
3391 }
3392 }
3393 }
3394 return nullptr;
3395}
3396
3397void QgsProcessingEnumWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
3398{
3399 if ( mComboBox )
3400 {
3401 if ( !value.isValid() )
3402 mComboBox->setCurrentIndex( mComboBox->findData( QVariant() ) );
3403 else
3404 {
3405 if ( auto *enumDef = dynamic_cast<const QgsProcessingParameterEnum *>( parameterDefinition() ); enumDef && enumDef->usesStaticStrings() )
3406 {
3407 const QString v = QgsProcessingParameters::parameterAsEnumString( parameterDefinition(), value, context );
3408 mComboBox->setCurrentIndex( mComboBox->findData( v ) );
3409 }
3410 else
3411 {
3412 const int v = QgsProcessingParameters::parameterAsEnum( parameterDefinition(), value, context );
3413 mComboBox->setCurrentIndex( mComboBox->findData( v ) );
3414 }
3415 }
3416 }
3417 else if ( mPanel || mCheckboxPanel )
3418 {
3419 QVariantList opts;
3420 if ( value.isValid() )
3421 {
3422 if ( auto *enumDef = dynamic_cast<const QgsProcessingParameterEnum *>( parameterDefinition() ); enumDef && enumDef->usesStaticStrings() )
3423 {
3424 const QStringList v = QgsProcessingParameters::parameterAsEnumStrings( parameterDefinition(), value, context );
3425 opts.reserve( v.size() );
3426 for ( QString i : v )
3427 opts << i;
3428 }
3429 else
3430 {
3431 const QList<int> v = QgsProcessingParameters::parameterAsEnums( parameterDefinition(), value, context );
3432 opts.reserve( v.size() );
3433 for ( int i : v )
3434 opts << i;
3435 }
3436 }
3437 if ( mPanel )
3438 mPanel->setValue( opts );
3439 else if ( mCheckboxPanel )
3440 mCheckboxPanel->setValue( opts );
3441 }
3442}
3443
3444QVariant QgsProcessingEnumWidgetWrapper::widgetValue() const
3445{
3446 if ( mComboBox )
3447 return mComboBox->currentData();
3448 else if ( mPanel )
3449 return mPanel->value();
3450 else if ( mCheckboxPanel )
3451 return mCheckboxPanel->value();
3452 else
3453 return QVariant();
3454}
3455
3456QString QgsProcessingEnumWidgetWrapper::modelerExpressionFormatString() const
3457{
3458 return tr( "selected option index (starting from 0), array of indices, or comma separated string of options (e.g. '1,3')" );
3459}
3460
3461QString QgsProcessingEnumWidgetWrapper::parameterType() const
3462{
3464}
3465
3466QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingEnumWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
3467{
3468 return new QgsProcessingEnumWidgetWrapper( parameter, type );
3469}
3470
3471QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingEnumWidgetWrapper::createParameterDefinitionWidget(
3473)
3474{
3475 return new QgsProcessingEnumParameterDefinitionWidget( context, widgetContext, definition, algorithm );
3476}
3477
3478//
3479// QgsProcessingLayoutWidgetWrapper
3480//
3481
3482QgsProcessingLayoutWidgetWrapper::QgsProcessingLayoutWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
3483 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
3484{}
3485
3486QWidget *QgsProcessingLayoutWidgetWrapper::createWidget()
3487{
3488 const QgsProcessingParameterLayout *layoutParam = dynamic_cast<const QgsProcessingParameterLayout *>( parameterDefinition() );
3489 if ( !layoutParam )
3490 return nullptr;
3491
3492 switch ( type() )
3493 {
3496 {
3497 // combobox only for use outside modeler!
3498 mComboBox = new QgsLayoutComboBox( nullptr, widgetContext().project() ? widgetContext().project()->layoutManager() : nullptr );
3499 if ( layoutParam->flags() & Qgis::ProcessingParameterFlag::Optional )
3500 mComboBox->setAllowEmptyLayout( true );
3501 mComboBox->setFilters( QgsLayoutManagerProxyModel::FilterPrintLayouts );
3502
3503 mComboBox->setToolTip( parameterDefinition()->toolTip() );
3504 connect( mComboBox, &QgsLayoutComboBox::layoutChanged, this, [this]( QgsMasterLayoutInterface * ) { emit widgetValueHasChanged( this ); } );
3505 return mComboBox;
3506 }
3507
3509 {
3510 mPlainComboBox = new QComboBox();
3511 mPlainComboBox->setEditable( true );
3512 mPlainComboBox->setToolTip( tr( "Name of an existing print layout" ) );
3513 if ( widgetContext().project() )
3514 {
3515 const QList<QgsPrintLayout *> layouts = widgetContext().project()->layoutManager()->printLayouts();
3516 for ( const QgsPrintLayout *layout : layouts )
3517 mPlainComboBox->addItem( layout->name() );
3518 }
3519
3520 connect( mPlainComboBox, &QComboBox::currentTextChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
3521 return mPlainComboBox;
3522 }
3523 }
3524 return nullptr;
3525}
3526
3527void QgsProcessingLayoutWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
3528{
3529 if ( mComboBox )
3530 {
3531 if ( !value.isValid() )
3532 mComboBox->setCurrentLayout( nullptr );
3533 else
3534 {
3535 if ( QgsPrintLayout *l = QgsProcessingParameters::parameterAsLayout( parameterDefinition(), value, context ) )
3536 mComboBox->setCurrentLayout( l );
3537 else
3538 mComboBox->setCurrentLayout( nullptr );
3539 }
3540 }
3541 else if ( mPlainComboBox )
3542 {
3543 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
3544 mPlainComboBox->setCurrentText( v );
3545 }
3546}
3547
3548QVariant QgsProcessingLayoutWidgetWrapper::widgetValue() const
3549{
3550 if ( mComboBox )
3551 {
3552 const QgsMasterLayoutInterface *l = mComboBox->currentLayout();
3553 return l ? l->name() : QVariant();
3554 }
3555 else if ( mPlainComboBox )
3556 return mPlainComboBox->currentText().isEmpty() ? QVariant() : mPlainComboBox->currentText();
3557 else
3558 return QVariant();
3559}
3560
3561void QgsProcessingLayoutWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
3562{
3564 if ( mPlainComboBox && context.project() )
3565 {
3566 const QList<QgsPrintLayout *> layouts = widgetContext().project()->layoutManager()->printLayouts();
3567 for ( const QgsPrintLayout *layout : layouts )
3568 mPlainComboBox->addItem( layout->name() );
3569 }
3570}
3571
3572QString QgsProcessingLayoutWidgetWrapper::modelerExpressionFormatString() const
3573{
3574 return tr( "string representing the name of an existing print layout" );
3575}
3576
3577QString QgsProcessingLayoutWidgetWrapper::parameterType() const
3578{
3580}
3581
3582QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingLayoutWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
3583{
3584 return new QgsProcessingLayoutWidgetWrapper( parameter, type );
3585}
3586
3587
3588//
3589// QgsProcessingLayoutItemWidgetWrapper
3590//
3591
3592
3593QgsProcessingLayoutItemParameterDefinitionWidget::QgsProcessingLayoutItemParameterDefinitionWidget(
3594 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
3595)
3596 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
3597{
3598 QVBoxLayout *vlayout = new QVBoxLayout();
3599 vlayout->setContentsMargins( 0, 0, 0, 0 );
3600
3601 vlayout->addWidget( new QLabel( tr( "Parent layout" ) ) );
3602
3603 mParentLayoutComboBox = new QComboBox();
3604 QString initialParent;
3605 if ( const QgsProcessingParameterLayoutItem *itemParam = dynamic_cast<const QgsProcessingParameterLayoutItem *>( definition ) )
3606 initialParent = itemParam->parentLayoutParameterName();
3607
3608 if ( auto *lModel = widgetContext.model() )
3609 {
3610 // populate combo box with other model input choices
3611 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
3612 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
3613 {
3614 if ( const QgsProcessingParameterLayout *definition = dynamic_cast<const QgsProcessingParameterLayout *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
3615 {
3616 mParentLayoutComboBox->addItem( definition->description(), definition->name() );
3617 if ( !initialParent.isEmpty() && initialParent == definition->name() )
3618 {
3619 mParentLayoutComboBox->setCurrentIndex( mParentLayoutComboBox->count() - 1 );
3620 }
3621 }
3622 }
3623 }
3624
3625 if ( mParentLayoutComboBox->count() == 0 && !initialParent.isEmpty() )
3626 {
3627 // if no parent candidates found, we just add the existing one as a placeholder
3628 mParentLayoutComboBox->addItem( initialParent, initialParent );
3629 mParentLayoutComboBox->setCurrentIndex( mParentLayoutComboBox->count() - 1 );
3630 }
3631
3632 vlayout->addWidget( mParentLayoutComboBox );
3633
3634 connect( mParentLayoutComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
3635
3636 setLayout( vlayout );
3637}
3638QgsProcessingParameterDefinition *QgsProcessingLayoutItemParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
3639{
3640 auto param = std::make_unique<QgsProcessingParameterLayoutItem>( name, description, QVariant(), mParentLayoutComboBox->currentData().toString() );
3641 param->setFlags( flags );
3642 return param.release();
3643}
3644
3645
3646QgsProcessingLayoutItemWidgetWrapper::QgsProcessingLayoutItemWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
3647 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
3648{}
3649
3650QWidget *QgsProcessingLayoutItemWidgetWrapper::createWidget()
3651{
3652 const QgsProcessingParameterLayoutItem *layoutParam = dynamic_cast<const QgsProcessingParameterLayoutItem *>( parameterDefinition() );
3653 if ( !layoutParam )
3654 return nullptr;
3655
3656 switch ( type() )
3657 {
3660 {
3661 // combobox only for use outside modeler!
3662 mComboBox = new QgsLayoutItemComboBox( nullptr, nullptr );
3663 if ( layoutParam->flags() & Qgis::ProcessingParameterFlag::Optional )
3664 mComboBox->setAllowEmptyItem( true );
3665 if ( layoutParam->itemType() >= 0 )
3666 mComboBox->setItemType( static_cast<QgsLayoutItemRegistry::ItemType>( layoutParam->itemType() ) );
3667
3668 mComboBox->setToolTip( parameterDefinition()->toolTip() );
3669 connect( mComboBox, &QgsLayoutItemComboBox::itemChanged, this, [this]( QgsLayoutItem * ) { emit widgetValueHasChanged( this ); } );
3670 return mComboBox;
3671 }
3672
3674 {
3675 mLineEdit = new QLineEdit();
3676 mLineEdit->setToolTip( tr( "UUID or ID of an existing print layout item" ) );
3677 connect( mLineEdit, &QLineEdit::textChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
3678 return mLineEdit;
3679 }
3680 }
3681 return nullptr;
3682}
3683
3684void QgsProcessingLayoutItemWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
3685{
3687 switch ( type() )
3688 {
3691 {
3692 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
3693 {
3694 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterLayoutItem *>( parameterDefinition() )->parentLayoutParameterName() )
3695 {
3696 setLayoutParameterValue( wrapper->parameterValue() );
3697 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setLayoutParameterValue( wrapper->parameterValue() ); } );
3698 break;
3699 }
3700 }
3701 break;
3702 }
3703
3705 break;
3706 }
3707}
3708
3709void QgsProcessingLayoutItemWidgetWrapper::setLayoutParameterValue( const QVariant &value )
3710{
3711 QgsPrintLayout *layout = nullptr;
3712
3713 // evaluate value to layout
3714 QgsProcessingContext *context = nullptr;
3715 std::unique_ptr<QgsProcessingContext> tmpContext;
3716 if ( mProcessingContextGenerator )
3717 context = mProcessingContextGenerator->processingContext();
3718
3719 if ( !context )
3720 {
3721 tmpContext = std::make_unique<QgsProcessingContext>();
3722 context = tmpContext.get();
3723 }
3724
3725 layout = QgsProcessingParameters::parameterAsLayout( parameterDefinition(), value, *context );
3726 setLayout( layout );
3727}
3728
3729void QgsProcessingLayoutItemWidgetWrapper::setLayout( QgsPrintLayout *layout )
3730{
3731 if ( mComboBox )
3732 mComboBox->setCurrentLayout( layout );
3733}
3734
3735void QgsProcessingLayoutItemWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
3736{
3737 if ( mComboBox )
3738 {
3739 if ( !value.isValid() )
3740 mComboBox->setItem( nullptr );
3741 else
3742 {
3743 QgsLayoutItem *item = QgsProcessingParameters::parameterAsLayoutItem( parameterDefinition(), value, context, qobject_cast<QgsPrintLayout *>( mComboBox->currentLayout() ) );
3744 mComboBox->setItem( item );
3745 }
3746 }
3747 else if ( mLineEdit )
3748 {
3749 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
3750 mLineEdit->setText( v );
3751 }
3752}
3753
3754QVariant QgsProcessingLayoutItemWidgetWrapper::widgetValue() const
3755{
3756 if ( mComboBox )
3757 {
3758 const QgsLayoutItem *i = mComboBox->currentItem();
3759 return i ? i->uuid() : QVariant();
3760 }
3761 else if ( mLineEdit )
3762 return mLineEdit->text().isEmpty() ? QVariant() : mLineEdit->text();
3763 else
3764 return QVariant();
3765}
3766
3767
3768QString QgsProcessingLayoutItemWidgetWrapper::modelerExpressionFormatString() const
3769{
3770 return tr( "string representing the UUID or ID of an existing print layout item" );
3771}
3772
3773QString QgsProcessingLayoutItemWidgetWrapper::parameterType() const
3774{
3776}
3777
3778QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingLayoutItemWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
3779{
3780 return new QgsProcessingLayoutItemWidgetWrapper( parameter, type );
3781}
3782
3783QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingLayoutItemWidgetWrapper::createParameterDefinitionWidget(
3785)
3786{
3787 return new QgsProcessingLayoutItemParameterDefinitionWidget( context, widgetContext, definition, algorithm );
3788}
3789
3790//
3791// QgsProcessingPointMapTool
3792//
3793
3794QgsProcessingPointMapTool::QgsProcessingPointMapTool( QgsMapCanvas *canvas )
3795 : QgsMapTool( canvas )
3796{
3798 mSnapIndicator = std::make_unique<QgsSnapIndicator>( canvas );
3799}
3800
3801QgsProcessingPointMapTool::~QgsProcessingPointMapTool() = default;
3802
3803void QgsProcessingPointMapTool::deactivate()
3804{
3805 mSnapIndicator->setMatch( QgsPointLocator::Match() );
3807}
3808
3809void QgsProcessingPointMapTool::canvasMoveEvent( QgsMapMouseEvent *e )
3810{
3811 e->snapPoint();
3812 mSnapIndicator->setMatch( e->mapPointMatch() );
3813}
3814
3815void QgsProcessingPointMapTool::canvasPressEvent( QgsMapMouseEvent *e )
3816{
3817 if ( e->button() == Qt::LeftButton )
3818 {
3819 QgsPointXY point = e->snapPoint();
3820 emit clicked( point );
3821 emit complete();
3822 }
3823}
3824
3825void QgsProcessingPointMapTool::keyPressEvent( QKeyEvent *e )
3826{
3827 if ( e->key() == Qt::Key_Escape )
3828 {
3829 // Override default shortcut management in MapCanvas
3830 e->ignore();
3831 emit complete();
3832 }
3833}
3834
3835
3836//
3837// QgsProcessingPointPanel
3838//
3839
3840QgsProcessingPointPanel::QgsProcessingPointPanel( QWidget *parent )
3841 : QWidget( parent )
3842{
3843 QHBoxLayout *l = new QHBoxLayout();
3844 l->setContentsMargins( 0, 0, 0, 0 );
3845 mLineEdit = new QgsFilterLineEdit();
3846 mLineEdit->setShowClearButton( false );
3847 l->addWidget( mLineEdit, 1 );
3848 mButton = new QToolButton();
3849 mButton->setText( QString( QChar( 0x2026 ) ) );
3850 l->addWidget( mButton );
3851 setLayout( l );
3852
3853 connect( mLineEdit, &QLineEdit::textChanged, this, &QgsProcessingPointPanel::changed );
3854 connect( mLineEdit, &QLineEdit::textChanged, this, &QgsProcessingPointPanel::textChanged );
3855 connect( mButton, &QToolButton::clicked, this, &QgsProcessingPointPanel::selectOnCanvas );
3856 mButton->setVisible( false );
3857}
3858
3859QgsProcessingPointPanel::~QgsProcessingPointPanel()
3860{
3861 if ( mCanvas && mPrevTool && mCanvas->mapTool() == mTool.get() )
3862 {
3863 mCanvas->setMapTool( mPrevTool.get() );
3864 }
3865}
3866
3867void QgsProcessingPointPanel::setMapCanvas( QgsMapCanvas *canvas )
3868{
3869 mCanvas = canvas;
3870 if ( mAllowSelectOnCanvas )
3871 {
3872 mButton->setVisible( true );
3873
3874 mCrs = canvas->mapSettings().destinationCrs();
3875 mTool = std::make_unique<QgsProcessingPointMapTool>( mCanvas );
3876 connect( mTool.get(), &QgsProcessingPointMapTool::clicked, this, &QgsProcessingPointPanel::updatePoint );
3877 connect( mTool.get(), &QgsProcessingPointMapTool::complete, this, &QgsProcessingPointPanel::pointPicked );
3878 }
3879}
3880
3881void QgsProcessingPointPanel::setAllowNull( bool allowNull )
3882{
3883 mLineEdit->setShowClearButton( allowNull );
3884}
3885
3886void QgsProcessingPointPanel::setShowPointOnCanvas( bool show )
3887{
3888 if ( mShowPointOnCanvas == show )
3889 return;
3890
3891 mShowPointOnCanvas = show;
3892 if ( mShowPointOnCanvas )
3893 {
3894 updateRubberBand();
3895 }
3896 else
3897 {
3898 mMapPointRubberBand.reset();
3899 }
3900}
3901
3902void QgsProcessingPointPanel::setAllowSelectOnCanvas( bool allow )
3903{
3904 mAllowSelectOnCanvas = allow;
3905 mButton->setVisible( mAllowSelectOnCanvas && static_cast<bool>( mTool ) );
3906}
3907
3908QVariant QgsProcessingPointPanel::value() const
3909{
3910 return mLineEdit->showClearButton() && mLineEdit->text().trimmed().isEmpty() ? QVariant() : QVariant( mLineEdit->text() );
3911}
3912
3913void QgsProcessingPointPanel::clear()
3914{
3915 mLineEdit->clear();
3916}
3917
3918void QgsProcessingPointPanel::setValue( const QgsPointXY &point, const QgsCoordinateReferenceSystem &crs )
3919{
3920 mPoint = point;
3921 QString newText = u"%1,%2"_s.arg( QString::number( point.x(), 'f' ), QString::number( point.y(), 'f' ) );
3922
3923 mCrs = crs;
3924 if ( mCrs.isValid() )
3925 {
3926 newText += u" [%1]"_s.arg( mCrs.authid() );
3927 }
3928 mLineEdit->setText( newText );
3929 updateRubberBand();
3930}
3931
3932void QgsProcessingPointPanel::showEvent( QShowEvent * )
3933{
3934 if ( mFirstShow )
3935 {
3936 // we don't support select on canvas if the dialog is modal
3937 if ( QWidget *parentWindow = window() )
3938 {
3939 setAllowSelectOnCanvas( !parentWindow->isModal() );
3940 }
3941 mFirstShow = false;
3942 }
3943}
3944
3945void QgsProcessingPointPanel::selectOnCanvas()
3946{
3947 if ( !mCanvas )
3948 return;
3949
3950 mPrevTool = mCanvas->mapTool();
3951 mCanvas->setMapTool( mTool.get() );
3952
3953 emit toggleDialogVisibility( false );
3954}
3955
3956void QgsProcessingPointPanel::updatePoint( const QgsPointXY &point )
3957{
3958 setValue( point, mCanvas->mapSettings().destinationCrs() );
3959}
3960
3961void QgsProcessingPointPanel::pointPicked()
3962{
3963 if ( !mCanvas )
3964 return;
3965
3966 mCanvas->setMapTool( mPrevTool );
3967
3968 emit toggleDialogVisibility( true );
3969}
3970
3971void QgsProcessingPointPanel::textChanged( const QString &text )
3972{
3973 const thread_local QRegularExpression rx( u"^\\s*\\(?\\s*(.*?)\\s*,\\s*(.*?)\\s*(?:\\[(.*)\\])?\\s*\\)?\\s*$"_s );
3974
3975 const QRegularExpressionMatch match = rx.match( text );
3976 if ( match.hasMatch() )
3977 {
3978 bool xOk = false;
3979 const double x = match.captured( 1 ).toDouble( &xOk );
3980 bool yOk = false;
3981 const double y = match.captured( 2 ).toDouble( &yOk );
3982
3983 if ( xOk && yOk )
3984 {
3985 mPoint = QgsPointXY( x, y );
3986
3987 const QgsCoordinateReferenceSystem pointCrs( match.captured( 3 ) );
3988 if ( pointCrs.isValid() )
3989 {
3990 mCrs = pointCrs;
3991 }
3992 }
3993 else
3994 {
3995 mPoint = QgsPointXY();
3996 }
3997 }
3998 else
3999 {
4000 mPoint = QgsPointXY();
4001 }
4002
4003 updateRubberBand();
4004}
4005
4006void QgsProcessingPointPanel::updateRubberBand()
4007{
4008 if ( !mShowPointOnCanvas || !mCanvas )
4009 return;
4010
4011 if ( mPoint.isEmpty() )
4012 {
4013 mMapPointRubberBand.reset();
4014 return;
4015 }
4016
4017 if ( !mMapPointRubberBand )
4018 {
4019 mMapPointRubberBand = make_qobject_unique<QgsRubberBand>( mCanvas, Qgis::GeometryType::Point );
4020 mMapPointRubberBand->setZValue( 1000 );
4021 mMapPointRubberBand->setIcon( QgsRubberBand::ICON_X );
4022
4023 const double scaleFactor = mCanvas->fontMetrics().xHeight() * .4;
4024 mMapPointRubberBand->setWidth( scaleFactor );
4025 mMapPointRubberBand->setIconSize( scaleFactor * 5 );
4026
4027 mMapPointRubberBand->setSecondaryStrokeColor( QColor( 255, 255, 255, 100 ) );
4028 mMapPointRubberBand->setColor( QColor( 200, 0, 200 ) );
4029 }
4030
4031 mMapPointRubberBand->setToGeometry( QgsGeometry::fromPointXY( mPoint ), mCrs );
4032}
4033
4034
4035//
4036// QgsProcessingPointWidgetWrapper
4037//
4038
4039QgsProcessingPointParameterDefinitionWidget::QgsProcessingPointParameterDefinitionWidget(
4040 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
4041)
4042 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
4043{
4044 QVBoxLayout *vlayout = new QVBoxLayout();
4045 vlayout->setContentsMargins( 0, 0, 0, 0 );
4046
4047 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
4048
4049 mDefaultLineEdit = new QLineEdit();
4050 mDefaultLineEdit->setToolTip( tr( "Point as 'x,y'" ) );
4051 mDefaultLineEdit->setPlaceholderText( tr( "Point as 'x,y'" ) );
4052 if ( const QgsProcessingParameterPoint *pointParam = dynamic_cast<const QgsProcessingParameterPoint *>( definition ) )
4053 {
4054 QgsPointXY point = QgsProcessingParameters::parameterAsPoint( pointParam, pointParam->defaultValueForGui(), context );
4055 mDefaultLineEdit->setText( u"%1,%2"_s.arg( QString::number( point.x(), 'f' ), QString::number( point.y(), 'f' ) ) );
4056 }
4057
4058 vlayout->addWidget( mDefaultLineEdit );
4059
4060 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4061
4062 setLayout( vlayout );
4063}
4064
4065QgsProcessingParameterDefinition *QgsProcessingPointParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
4066{
4067 auto param = std::make_unique<QgsProcessingParameterPoint>( name, description, mDefaultLineEdit->text() );
4068 param->setFlags( flags );
4069 return param.release();
4070}
4071
4072QgsProcessingPointWidgetWrapper::QgsProcessingPointWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
4073 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
4074{}
4075
4076QWidget *QgsProcessingPointWidgetWrapper::createWidget()
4077{
4078 const QgsProcessingParameterPoint *pointParam = dynamic_cast<const QgsProcessingParameterPoint *>( parameterDefinition() );
4079 if ( !pointParam )
4080 return nullptr;
4081
4082 switch ( type() )
4083 {
4086 {
4087 mPanel = new QgsProcessingPointPanel( nullptr );
4088 if ( widgetContext().mapCanvas() )
4089 mPanel->setMapCanvas( widgetContext().mapCanvas() );
4090
4091 if ( pointParam->flags() & Qgis::ProcessingParameterFlag::Optional )
4092 mPanel->setAllowNull( true );
4093
4094 if ( type() == Qgis::ProcessingMode::Standard )
4095 mPanel->setShowPointOnCanvas( true );
4096
4097 mPanel->setToolTip( parameterDefinition()->toolTip() );
4098
4099 connect( mPanel, &QgsProcessingPointPanel::changed, this, [this] { emit widgetValueHasChanged( this ); } );
4100
4101 if ( mDialog )
4102 setDialog( mDialog ); // setup connections to panel - dialog was previously set before the widget was created
4103 return mPanel;
4104 }
4105
4107 {
4108 mLineEdit = new QLineEdit();
4109 mLineEdit->setToolTip( tr( "Point as 'x,y'" ) );
4110 connect( mLineEdit, &QLineEdit::textChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
4111 return mLineEdit;
4112 }
4113 }
4114 return nullptr;
4115}
4116
4117void QgsProcessingPointWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
4118{
4120 if ( mPanel && context.mapCanvas() )
4121 mPanel->setMapCanvas( context.mapCanvas() );
4122}
4123
4124void QgsProcessingPointWidgetWrapper::setDialog( QWidget *dialog )
4125{
4126 mDialog = dialog;
4127 if ( mPanel )
4128 {
4129 connect( mPanel, &QgsProcessingPointPanel::toggleDialogVisibility, mDialog, [this]( bool visible ) {
4130 if ( !visible )
4131 mDialog->showMinimized();
4132 else
4133 {
4134 mDialog->showNormal();
4135 mDialog->raise();
4136 mDialog->activateWindow();
4137 }
4138 } );
4139 }
4141}
4142
4143void QgsProcessingPointWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
4144{
4145 if ( mPanel )
4146 {
4147 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString && value.toString().isEmpty() ) )
4148 mPanel->clear();
4149 else
4150 {
4151 QgsPointXY p = QgsProcessingParameters::parameterAsPoint( parameterDefinition(), value, context );
4152 QgsCoordinateReferenceSystem crs = QgsProcessingParameters::parameterAsPointCrs( parameterDefinition(), value, context );
4153 mPanel->setValue( p, crs );
4154 }
4155 }
4156 else if ( mLineEdit )
4157 {
4158 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
4159 mLineEdit->setText( v );
4160 }
4161}
4162
4163QVariant QgsProcessingPointWidgetWrapper::widgetValue() const
4164{
4165 if ( mPanel )
4166 {
4167 return mPanel->value();
4168 }
4169 else if ( mLineEdit )
4170 return mLineEdit->text().isEmpty() ? QVariant() : mLineEdit->text();
4171 else
4172 return QVariant();
4173}
4174
4175QString QgsProcessingPointWidgetWrapper::modelerExpressionFormatString() const
4176{
4177 return tr( "string of the format 'x,y' or a geometry value (centroid is used)" );
4178}
4179
4180QString QgsProcessingPointWidgetWrapper::parameterType() const
4181{
4183}
4184
4185QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingPointWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
4186{
4187 return new QgsProcessingPointWidgetWrapper( parameter, type );
4188}
4189
4190QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingPointWidgetWrapper::createParameterDefinitionWidget(
4192)
4193{
4194 return new QgsProcessingPointParameterDefinitionWidget( context, widgetContext, definition, algorithm );
4195}
4196
4197
4198//
4199// QgsProcessingGeometryWidgetWrapper
4200//
4201
4202
4203QgsProcessingGeometryParameterDefinitionWidget::QgsProcessingGeometryParameterDefinitionWidget(
4204 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
4205)
4206 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
4207{
4208 QVBoxLayout *vlayout = new QVBoxLayout();
4209 vlayout->setContentsMargins( 0, 0, 0, 0 );
4210
4211 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
4212
4213 mGeometryWidget = new QgsGeometryWidget();
4214 if ( const QgsProcessingParameterGeometry *geometryParam = dynamic_cast<const QgsProcessingParameterGeometry *>( definition ) )
4215 {
4216 QgsGeometry g = QgsProcessingParameters::parameterAsGeometry( geometryParam, geometryParam->defaultValueForGui(), context );
4217 if ( !g.isNull() )
4218 {
4219 mGeometryWidget->setGeometryValue( QgsReferencedGeometry( g, QgsCoordinateReferenceSystem() ) );
4220 }
4221 }
4222
4223 vlayout->addWidget( mGeometryWidget );
4224
4226
4227 setLayout( vlayout );
4228}
4229
4230QgsProcessingParameterDefinition *QgsProcessingGeometryParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
4231{
4232 const QgsReferencedGeometry geometry = mGeometryWidget->geometryValue();
4233 auto param = std::make_unique<QgsProcessingParameterGeometry>( name, description, geometry.isEmpty() ? QVariant() : geometry.asWkt() );
4234 param->setFlags( flags );
4235 return param.release();
4236}
4237
4238QgsProcessingGeometryWidgetWrapper::QgsProcessingGeometryWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
4239 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
4240{}
4241
4242QWidget *QgsProcessingGeometryWidgetWrapper::createWidget()
4243{
4244 switch ( type() )
4245 {
4249 {
4250 mGeometryWidget = new QgsGeometryWidget();
4251 mGeometryWidget->setToolTip( parameterDefinition()->toolTip() );
4252 connect( mGeometryWidget, &QgsGeometryWidget::geometryValueChanged, this, [this]( const QgsReferencedGeometry & ) { emit widgetValueHasChanged( this ); } );
4253 return mGeometryWidget;
4254 }
4255 }
4256 return nullptr;
4257}
4258
4259void QgsProcessingGeometryWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
4260{
4261 if ( mGeometryWidget )
4262 {
4263 QgsGeometry g = QgsProcessingParameters::parameterAsGeometry( parameterDefinition(), value, context );
4264 if ( !g.isNull() )
4265 {
4266 mGeometryWidget->setGeometryValue( QgsReferencedGeometry( g, QgsCoordinateReferenceSystem() ) );
4267 }
4268 else
4269 {
4270 mGeometryWidget->clearGeometry();
4271 }
4272 }
4273}
4274
4275QVariant QgsProcessingGeometryWidgetWrapper::widgetValue() const
4276{
4277 if ( mGeometryWidget )
4278 {
4279 const QgsReferencedGeometry geometry = mGeometryWidget->geometryValue();
4280 return geometry.isEmpty() ? QVariant() : geometry.asWkt();
4281 }
4282 else
4283 {
4284 return QVariant();
4285 }
4286}
4287
4288QString QgsProcessingGeometryWidgetWrapper::modelerExpressionFormatString() const
4289{
4290 return tr( "string in the Well-Known-Text format or a geometry value" );
4291}
4292
4293QString QgsProcessingGeometryWidgetWrapper::parameterType() const
4294{
4296}
4297
4298QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingGeometryWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
4299{
4300 return new QgsProcessingGeometryWidgetWrapper( parameter, type );
4301}
4302
4303QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingGeometryWidgetWrapper::createParameterDefinitionWidget(
4305)
4306{
4307 return new QgsProcessingGeometryParameterDefinitionWidget( context, widgetContext, definition, algorithm );
4308}
4309
4310
4311//
4312// QgsProcessingColorWidgetWrapper
4313//
4314
4315
4316QgsProcessingColorParameterDefinitionWidget::QgsProcessingColorParameterDefinitionWidget(
4317 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
4318)
4319 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
4320{
4321 QVBoxLayout *vlayout = new QVBoxLayout();
4322 vlayout->setContentsMargins( 0, 0, 0, 0 );
4323
4324 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
4325
4326 mDefaultColorButton = new QgsColorButton();
4327 mDefaultColorButton->setShowNull( true );
4328 mAllowOpacity = new QCheckBox( tr( "Allow opacity control" ) );
4329
4330 if ( const QgsProcessingParameterColor *colorParam = dynamic_cast<const QgsProcessingParameterColor *>( definition ) )
4331 {
4332 const QColor c = QgsProcessingParameters::parameterAsColor( colorParam, colorParam->defaultValueForGui(), context );
4333 if ( !c.isValid() )
4334 mDefaultColorButton->setToNull();
4335 else
4336 mDefaultColorButton->setColor( c );
4337 mAllowOpacity->setChecked( colorParam->opacityEnabled() );
4338 }
4339 else
4340 {
4341 mDefaultColorButton->setToNull();
4342 mAllowOpacity->setChecked( true );
4343 }
4344
4346 connect( mAllowOpacity, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4347
4348 vlayout->addWidget( mDefaultColorButton );
4349 vlayout->addWidget( mAllowOpacity );
4350 setLayout( vlayout );
4351}
4352
4353QgsProcessingParameterDefinition *QgsProcessingColorParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
4354{
4355 auto param = std::make_unique<QgsProcessingParameterColor>( name, description, mDefaultColorButton->color(), mAllowOpacity->isChecked() );
4356 param->setFlags( flags );
4357 return param.release();
4358}
4359
4360QgsProcessingColorWidgetWrapper::QgsProcessingColorWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
4361 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
4362{}
4363
4364QWidget *QgsProcessingColorWidgetWrapper::createWidget()
4365{
4366 const QgsProcessingParameterColor *colorParam = dynamic_cast<const QgsProcessingParameterColor *>( parameterDefinition() );
4367 if ( !colorParam )
4368 return nullptr;
4369
4370 switch ( type() )
4371 {
4375 {
4376 mColorButton = new QgsColorButton( nullptr );
4377 mColorButton->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
4378
4379 if ( colorParam->flags() & Qgis::ProcessingParameterFlag::Optional )
4380 mColorButton->setShowNull( true );
4381
4382 mColorButton->setAllowOpacity( colorParam->opacityEnabled() );
4383 mColorButton->setToolTip( parameterDefinition()->toolTip() );
4384 mColorButton->setColorDialogTitle( parameterDefinition()->description() );
4385 if ( colorParam->defaultValueForGui().value<QColor>().isValid() )
4386 {
4387 mColorButton->setDefaultColor( colorParam->defaultValueForGui().value<QColor>() );
4388 }
4389
4390 connect( mColorButton, &QgsColorButton::colorChanged, this, [this] { emit widgetValueHasChanged( this ); } );
4391
4392 return mColorButton;
4393 }
4394 }
4395 return nullptr;
4396}
4397
4398void QgsProcessingColorWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
4399{
4400 if ( mColorButton )
4401 {
4402 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString && value.toString().isEmpty() ) || ( value.userType() == QMetaType::Type::QColor && !value.value<QColor>().isValid() ) )
4403 mColorButton->setToNull();
4404 else
4405 {
4406 const QColor c = QgsProcessingParameters::parameterAsColor( parameterDefinition(), value, context );
4407 if ( !c.isValid() && mColorButton->showNull() )
4408 mColorButton->setToNull();
4409 else
4410 mColorButton->setColor( c );
4411 }
4412 }
4413}
4414
4415QVariant QgsProcessingColorWidgetWrapper::widgetValue() const
4416{
4417 if ( mColorButton )
4418 return mColorButton->isNull() ? QVariant() : mColorButton->color();
4419 else
4420 return QVariant();
4421}
4422
4423QString QgsProcessingColorWidgetWrapper::modelerExpressionFormatString() const
4424{
4425 return tr( "color style string, e.g. #ff0000 or 255,0,0" );
4426}
4427
4428QString QgsProcessingColorWidgetWrapper::parameterType() const
4429{
4431}
4432
4433QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingColorWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
4434{
4435 return new QgsProcessingColorWidgetWrapper( parameter, type );
4436}
4437
4438QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingColorWidgetWrapper::createParameterDefinitionWidget(
4440)
4441{
4442 return new QgsProcessingColorParameterDefinitionWidget( context, widgetContext, definition, algorithm );
4443}
4444
4445
4446//
4447// QgsProcessingCoordinateOperationWidgetWrapper
4448//
4449
4450QgsProcessingCoordinateOperationParameterDefinitionWidget::QgsProcessingCoordinateOperationParameterDefinitionWidget(
4451 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
4452)
4453 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
4454{
4455 QVBoxLayout *vlayout = new QVBoxLayout();
4456 vlayout->setContentsMargins( 0, 0, 0, 0 );
4457
4458 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
4459
4460 mDefaultLineEdit = new QLineEdit();
4461 if ( const QgsProcessingParameterCoordinateOperation *coordParam = dynamic_cast<const QgsProcessingParameterCoordinateOperation *>( definition ) )
4462 mDefaultLineEdit->setText( QgsProcessingParameters::parameterAsString( coordParam, coordParam->defaultValueForGui(), context ) );
4463 vlayout->addWidget( mDefaultLineEdit );
4464
4465 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4466
4467 mSourceParamComboBox = new QComboBox();
4468 mDestParamComboBox = new QComboBox();
4469 QString initialSource;
4470 QString initialDest;
4473 if ( const QgsProcessingParameterCoordinateOperation *itemParam = dynamic_cast<const QgsProcessingParameterCoordinateOperation *>( definition ) )
4474 {
4475 initialSource = itemParam->sourceCrsParameterName();
4476 initialDest = itemParam->destinationCrsParameterName();
4477 sourceCrs = QgsProcessingUtils::variantToCrs( itemParam->sourceCrs(), context );
4478 destCrs = QgsProcessingUtils::variantToCrs( itemParam->destinationCrs(), context );
4479 }
4480
4481 mSourceParamComboBox->addItem( QString(), QString() );
4482 mDestParamComboBox->addItem( QString(), QString() );
4483 if ( auto *lModel = widgetContext.model() )
4484 {
4485 // populate combo box with other model input choices
4486 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
4487 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
4488 {
4489 if ( definition && it->parameterName() == definition->name() )
4490 continue;
4491
4492 // TODO - we should probably filter this list?
4493 mSourceParamComboBox->addItem( it->parameterName(), it->parameterName() );
4494 mDestParamComboBox->addItem( it->parameterName(), it->parameterName() );
4495 if ( !initialSource.isEmpty() && initialSource == it->parameterName() )
4496 {
4497 mSourceParamComboBox->setCurrentIndex( mSourceParamComboBox->count() - 1 );
4498 }
4499 if ( !initialDest.isEmpty() && initialDest == it->parameterName() )
4500 {
4501 mDestParamComboBox->setCurrentIndex( mDestParamComboBox->count() - 1 );
4502 }
4503 }
4504 }
4505
4506 if ( mSourceParamComboBox->count() == 1 && !initialSource.isEmpty() )
4507 {
4508 // if no source candidates found, we just add the existing one as a placeholder
4509 mSourceParamComboBox->addItem( initialSource, initialSource );
4510 mSourceParamComboBox->setCurrentIndex( mSourceParamComboBox->count() - 1 );
4511 }
4512 if ( mDestParamComboBox->count() == 1 && !initialDest.isEmpty() )
4513 {
4514 // if no dest candidates found, we just add the existing one as a placeholder
4515 mDestParamComboBox->addItem( initialDest, initialDest );
4516 mDestParamComboBox->setCurrentIndex( mDestParamComboBox->count() - 1 );
4517 }
4518
4519 vlayout->addWidget( new QLabel( tr( "Source CRS parameter" ) ) );
4520 vlayout->addWidget( mSourceParamComboBox );
4521 vlayout->addWidget( new QLabel( tr( "Destination CRS parameter" ) ) );
4522 vlayout->addWidget( mDestParamComboBox );
4523
4524 connect( mSourceParamComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4525 connect( mDestParamComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4526
4527 mStaticSourceWidget = new QgsProjectionSelectionWidget();
4528 mStaticSourceWidget->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, true );
4529 mStaticSourceWidget->setCrs( sourceCrs );
4530 mStaticDestWidget = new QgsProjectionSelectionWidget();
4531 mStaticDestWidget->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, true );
4532 mStaticDestWidget->setCrs( destCrs );
4533
4534 vlayout->addWidget( new QLabel( tr( "Static source CRS" ) ) );
4535 vlayout->addWidget( mStaticSourceWidget );
4536 vlayout->addWidget( new QLabel( tr( "Static destination CRS" ) ) );
4537 vlayout->addWidget( mStaticDestWidget );
4538
4541
4542 setLayout( vlayout );
4543}
4544
4545QgsProcessingParameterDefinition *QgsProcessingCoordinateOperationParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
4546{
4547 auto param = std::make_unique<QgsProcessingParameterCoordinateOperation>(
4548 name,
4549 description,
4550 mDefaultLineEdit->text(),
4551 mSourceParamComboBox->currentText(),
4552 mDestParamComboBox->currentText(),
4553 mStaticSourceWidget->crs().isValid() ? QVariant::fromValue( mStaticSourceWidget->crs() ) : QVariant(),
4554 mStaticDestWidget->crs().isValid() ? QVariant::fromValue( mStaticDestWidget->crs() ) : QVariant()
4555 );
4556 param->setFlags( flags );
4557 return param.release();
4558}
4559
4560QgsProcessingCoordinateOperationWidgetWrapper::QgsProcessingCoordinateOperationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
4561 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
4562{}
4563
4564QWidget *QgsProcessingCoordinateOperationWidgetWrapper::createWidget()
4565{
4566 const QgsProcessingParameterCoordinateOperation *coordParam = dynamic_cast<const QgsProcessingParameterCoordinateOperation *>( parameterDefinition() );
4567 if ( !coordParam )
4568 return nullptr;
4569
4571 mSourceCrs = QgsProcessingUtils::variantToCrs( coordParam->sourceCrs(), c );
4572 mDestCrs = QgsProcessingUtils::variantToCrs( coordParam->destinationCrs(), c );
4573 switch ( type() )
4574 {
4576 {
4577 mOperationWidget = new QgsCoordinateOperationWidget( nullptr );
4578 mOperationWidget->setShowMakeDefault( false );
4579 mOperationWidget->setShowFallbackOption( false );
4580 mOperationWidget->setToolTip( parameterDefinition()->toolTip() );
4581 mOperationWidget->setSourceCrs( mSourceCrs );
4582 mOperationWidget->setDestinationCrs( mDestCrs );
4583 mOperationWidget->setMapCanvas( mCanvas );
4584 if ( !coordParam->defaultValueForGui().toString().isEmpty() )
4585 {
4587 deets.proj = coordParam->defaultValueForGui().toString();
4588 mOperationWidget->setSelectedOperation( deets );
4589 }
4590
4591 connect( mOperationWidget, &QgsCoordinateOperationWidget::operationChanged, this, [this] { emit widgetValueHasChanged( this ); } );
4592
4593 return mOperationWidget;
4594 }
4595
4598 {
4599 mLineEdit = new QLineEdit();
4600 QHBoxLayout *layout = new QHBoxLayout();
4601 layout->addWidget( mLineEdit, 1 );
4602 connect( mLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
4603
4604 QToolButton *button = new QToolButton();
4605 button->setText( QString( QChar( 0x2026 ) ) );
4606 connect( button, &QToolButton::clicked, this, [this, button] {
4607 QgsDatumTransformDialog dlg( mSourceCrs, mDestCrs, false, false, false, qMakePair( -1, -1 ), button, Qt::WindowFlags(), mLineEdit->text(), mCanvas );
4608 if ( dlg.exec() )
4609 {
4610 mLineEdit->setText( dlg.selectedDatumTransform().proj );
4611 emit widgetValueHasChanged( this );
4612 }
4613 } );
4614 layout->addWidget( button );
4615
4616 QWidget *w = new QWidget();
4617 layout->setContentsMargins( 0, 0, 0, 0 );
4618 w->setLayout( layout );
4619 return w;
4620 }
4621 }
4622 return nullptr;
4623}
4624
4625void QgsProcessingCoordinateOperationWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
4626{
4628 switch ( type() )
4629 {
4632 {
4633 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
4634 {
4635 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterCoordinateOperation *>( parameterDefinition() )->sourceCrsParameterName() )
4636 {
4637 setSourceCrsParameterValue( wrapper->parameterValue() );
4638 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setSourceCrsParameterValue( wrapper->parameterValue() ); } );
4639 }
4640 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterCoordinateOperation *>( parameterDefinition() )->destinationCrsParameterName() )
4641 {
4642 setDestinationCrsParameterValue( wrapper->parameterValue() );
4643 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setDestinationCrsParameterValue( wrapper->parameterValue() ); } );
4644 }
4645 }
4646 break;
4647 }
4648
4650 break;
4651 }
4652}
4653
4654void QgsProcessingCoordinateOperationWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
4655{
4656 mCanvas = context.mapCanvas();
4657 if ( mOperationWidget )
4658 mOperationWidget->setMapCanvas( context.mapCanvas() );
4659}
4660
4661void QgsProcessingCoordinateOperationWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
4662{
4663 if ( mOperationWidget )
4664 {
4665 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString ) )
4666 {
4668 deets.proj = value.toString();
4669 mOperationWidget->setSelectedOperation( deets );
4670 }
4671 }
4672 if ( mLineEdit )
4673 {
4674 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString ) )
4675 {
4676 mLineEdit->setText( value.toString() );
4677 }
4678 }
4679}
4680
4681QVariant QgsProcessingCoordinateOperationWidgetWrapper::widgetValue() const
4682{
4683 if ( mOperationWidget )
4684 return mOperationWidget->selectedOperation().proj;
4685 else if ( mLineEdit )
4686 return mLineEdit->text();
4687 else
4688 return QVariant();
4689}
4690
4691QString QgsProcessingCoordinateOperationWidgetWrapper::modelerExpressionFormatString() const
4692{
4693 return tr( "Proj coordinate operation string, e.g. '+proj=pipeline +step +inv...'" );
4694}
4695
4696void QgsProcessingCoordinateOperationWidgetWrapper::setSourceCrsParameterValue( const QVariant &value )
4697{
4698 QgsProcessingContext *context = nullptr;
4699 std::unique_ptr<QgsProcessingContext> tmpContext;
4700 if ( mProcessingContextGenerator )
4701 context = mProcessingContextGenerator->processingContext();
4702
4703 if ( !context )
4704 {
4705 tmpContext = std::make_unique<QgsProcessingContext>();
4706 context = tmpContext.get();
4707 }
4708
4709 mSourceCrs = QgsProcessingUtils::variantToCrs( value, *context );
4710 if ( mOperationWidget )
4711 {
4712 mOperationWidget->setSourceCrs( mSourceCrs );
4713 mOperationWidget->setSelectedOperationUsingContext( context->transformContext() );
4714 }
4715}
4716
4717void QgsProcessingCoordinateOperationWidgetWrapper::setDestinationCrsParameterValue( const QVariant &value )
4718{
4719 QgsProcessingContext *context = nullptr;
4720 std::unique_ptr<QgsProcessingContext> tmpContext;
4721 if ( mProcessingContextGenerator )
4722 context = mProcessingContextGenerator->processingContext();
4723
4724 if ( !context )
4725 {
4726 tmpContext = std::make_unique<QgsProcessingContext>();
4727 context = tmpContext.get();
4728 }
4729
4730 mDestCrs = QgsProcessingUtils::variantToCrs( value, *context );
4731 if ( mOperationWidget )
4732 {
4733 mOperationWidget->setDestinationCrs( mDestCrs );
4734 mOperationWidget->setSelectedOperationUsingContext( context->transformContext() );
4735 }
4736}
4737
4738QString QgsProcessingCoordinateOperationWidgetWrapper::parameterType() const
4739{
4741}
4742
4743QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingCoordinateOperationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
4744{
4745 return new QgsProcessingCoordinateOperationWidgetWrapper( parameter, type );
4746}
4747
4748QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingCoordinateOperationWidgetWrapper::createParameterDefinitionWidget(
4750)
4751{
4752 return new QgsProcessingCoordinateOperationParameterDefinitionWidget( context, widgetContext, definition, algorithm );
4753}
4754
4755
4756//
4757// QgsProcessingFieldPanelWidget
4758//
4759
4760QgsProcessingFieldPanelWidget::QgsProcessingFieldPanelWidget( QWidget *parent, const QgsProcessingParameterField *param )
4761 : QWidget( parent )
4762 , mParam( param )
4763{
4764 QHBoxLayout *hl = new QHBoxLayout();
4765 hl->setContentsMargins( 0, 0, 0, 0 );
4766
4767 mLineEdit = new QLineEdit();
4768 mLineEdit->setEnabled( false );
4769 hl->addWidget( mLineEdit, 1 );
4770
4771 mToolButton = new QToolButton();
4772 mToolButton->setText( QString( QChar( 0x2026 ) ) );
4773 hl->addWidget( mToolButton );
4774
4775 setLayout( hl );
4776
4777 if ( mParam )
4778 {
4779 mLineEdit->setText( tr( "%n field(s) selected", nullptr, 0 ) );
4780 }
4781
4782 connect( mToolButton, &QToolButton::clicked, this, &QgsProcessingFieldPanelWidget::showDialog );
4783}
4784
4785void QgsProcessingFieldPanelWidget::setFields( const QgsFields &fields )
4786{
4787 mFields = fields;
4788
4789 // ensure that after setting new fields the value does not contain fields
4790 // which are not available anymore, see https://github.com/qgis/QGIS/issues/39351
4791 QVariantList availableFields;
4792 for ( const QgsField &field : std::as_const( mFields ) )
4793 {
4794 availableFields << field.name();
4795 }
4796 QList<QVariant>::iterator it = std::remove_if( mValue.begin(), mValue.end(), [&availableFields]( const QVariant &value ) { return !availableFields.contains( value ); } );
4797 mValue.erase( it, mValue.end() );
4798
4799 updateSummaryText();
4800 emit changed();
4801}
4802
4803void QgsProcessingFieldPanelWidget::setValue( const QVariant &value )
4804{
4805 if ( value.isValid() )
4806 mValue = value.userType() == QMetaType::Type::QVariantList ? value.toList() : QVariantList() << value;
4807 else
4808 mValue.clear();
4809
4810 updateSummaryText();
4811 emit changed();
4812}
4813
4814void QgsProcessingFieldPanelWidget::showDialog()
4815{
4816 QVariantList availableOptions;
4817 availableOptions.reserve( mFields.size() );
4818 for ( const QgsField &field : std::as_const( mFields ) )
4819 {
4820 availableOptions << field.name();
4821 }
4822
4824 if ( panel && panel->dockMode() )
4825 {
4826 QgsProcessingMultipleSelectionPanelWidget *widget = new QgsProcessingMultipleSelectionPanelWidget( availableOptions, mValue );
4827 widget->setPanelTitle( mParam->description() );
4828
4829 widget->setValueFormatter( []( const QVariant &v ) -> QString { return v.toString(); } );
4830
4831 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [this, widget]() { setValue( widget->selectedOptions() ); } );
4832 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
4833 panel->openPanel( widget );
4834 }
4835 else
4836 {
4837 QgsProcessingMultipleSelectionDialog dlg( availableOptions, mValue, this, Qt::WindowFlags() );
4838
4839 dlg.setValueFormatter( []( const QVariant &v ) -> QString { return v.toString(); } );
4840 if ( dlg.exec() )
4841 {
4842 setValue( dlg.selectedOptions() );
4843 }
4844 }
4845}
4846
4847void QgsProcessingFieldPanelWidget::updateSummaryText()
4848{
4849 if ( !mParam )
4850 return;
4851
4852 if ( mValue.empty() )
4853 {
4854 mLineEdit->setText( tr( "%n field(s) selected", nullptr, 0 ) );
4855 }
4856 else
4857 {
4858 QStringList values;
4859 values.reserve( mValue.size() );
4860 for ( const QVariant &val : std::as_const( mValue ) )
4861 {
4862 values << val.toString();
4863 }
4864
4865 const QString concatenated = values.join( tr( "," ) );
4866 if ( concatenated.length() < 100 )
4867 mLineEdit->setText( concatenated );
4868 else
4869 mLineEdit->setText( tr( "%n field(s) selected", nullptr, mValue.count() ) );
4870 }
4871}
4872
4873
4874//
4875// QgsProcessingFieldWidgetWrapper
4876//
4877
4878QgsProcessingFieldParameterDefinitionWidget::QgsProcessingFieldParameterDefinitionWidget(
4879 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
4880)
4881 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
4882{
4883 QVBoxLayout *vlayout = new QVBoxLayout();
4884 vlayout->setContentsMargins( 0, 0, 0, 0 );
4885
4886 vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
4887 mParentLayerComboBox = new QComboBox();
4888
4889 QString initialParent;
4890 if ( const QgsProcessingParameterField *fieldParam = dynamic_cast<const QgsProcessingParameterField *>( definition ) )
4891 initialParent = fieldParam->parentLayerParameterName();
4892
4893 if ( auto *lModel = widgetContext.model() )
4894 {
4895 // populate combo box with other model input choices
4896 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
4897 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
4898 {
4899 if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast<const QgsProcessingParameterFeatureSource *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
4900 {
4901 mParentLayerComboBox->addItem( definition->description(), definition->name() );
4902 if ( !initialParent.isEmpty() && initialParent == definition->name() )
4903 {
4904 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
4905 }
4906 }
4907 else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast<const QgsProcessingParameterVectorLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
4908 {
4909 mParentLayerComboBox->addItem( definition->description(), definition->name() );
4910 if ( !initialParent.isEmpty() && initialParent == definition->name() )
4911 {
4912 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
4913 }
4914 }
4915 else if ( const QgsProcessingParameterMultipleLayers *definition = dynamic_cast<const QgsProcessingParameterMultipleLayers *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
4916 {
4917 if ( definition->layerType() == Qgis::ProcessingSourceType::Vector )
4918 {
4919 mParentLayerComboBox->addItem( definition->description(), definition->name() );
4920 if ( !initialParent.isEmpty() && initialParent == definition->name() )
4921 {
4922 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
4923 }
4924 }
4925 }
4926 }
4927 }
4928
4929 if ( mParentLayerComboBox->count() == 0 && !initialParent.isEmpty() )
4930 {
4931 // if no parent candidates found, we just add the existing one as a placeholder
4932 mParentLayerComboBox->addItem( initialParent, initialParent );
4933 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
4934 }
4935
4936 vlayout->addWidget( mParentLayerComboBox );
4937 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4938
4939 vlayout->addWidget( new QLabel( tr( "Allowed data type" ) ) );
4940 mDataTypeComboBox = new QComboBox();
4941 mDataTypeComboBox->addItem( tr( "Any" ), static_cast<int>( Qgis::ProcessingFieldParameterDataType::Any ) );
4942 mDataTypeComboBox->addItem( tr( "Number" ), static_cast<int>( Qgis::ProcessingFieldParameterDataType::Numeric ) );
4943 mDataTypeComboBox->addItem( tr( "String" ), static_cast<int>( Qgis::ProcessingFieldParameterDataType::String ) );
4944 mDataTypeComboBox->addItem( tr( "Date/time" ), static_cast<int>( Qgis::ProcessingFieldParameterDataType::DateTime ) );
4945 mDataTypeComboBox->addItem( tr( "Binary" ), static_cast<int>( Qgis::ProcessingFieldParameterDataType::Binary ) );
4946 mDataTypeComboBox->addItem( tr( "Boolean" ), static_cast<int>( Qgis::ProcessingFieldParameterDataType::Boolean ) );
4947 if ( const QgsProcessingParameterField *fieldParam = dynamic_cast<const QgsProcessingParameterField *>( definition ) )
4948 mDataTypeComboBox->setCurrentIndex( mDataTypeComboBox->findData( static_cast<int>( fieldParam->dataType() ) ) );
4949
4950 vlayout->addWidget( mDataTypeComboBox );
4951 connect( mDataTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4952
4953 mAllowMultipleCheckBox = new QCheckBox( tr( "Accept multiple fields" ) );
4954 if ( const QgsProcessingParameterField *fieldParam = dynamic_cast<const QgsProcessingParameterField *>( definition ) )
4955 mAllowMultipleCheckBox->setChecked( fieldParam->allowMultiple() );
4956
4957 vlayout->addWidget( mAllowMultipleCheckBox );
4958 connect( mDataTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4959
4960 connect( mAllowMultipleCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4961
4962 mDefaultToAllCheckBox = new QCheckBox( tr( "Select all fields by default" ) );
4963 mDefaultToAllCheckBox->setEnabled( mAllowMultipleCheckBox->isChecked() );
4964 if ( const QgsProcessingParameterField *fieldParam = dynamic_cast<const QgsProcessingParameterField *>( definition ) )
4965 mDefaultToAllCheckBox->setChecked( fieldParam->defaultToAllFields() );
4966
4967 vlayout->addWidget( mDefaultToAllCheckBox );
4968 connect( mDefaultToAllCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4969
4970 connect( mAllowMultipleCheckBox, &QCheckBox::stateChanged, this, [this] { mDefaultToAllCheckBox->setEnabled( mAllowMultipleCheckBox->isChecked() ); } );
4971
4972 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
4973
4974 mDefaultLineEdit = new QLineEdit();
4975 mDefaultLineEdit->setToolTip( tr( "Default field name, or ; separated list of field names for multiple field parameters" ) );
4976 if ( const QgsProcessingParameterField *fieldParam = dynamic_cast<const QgsProcessingParameterField *>( definition ) )
4977 {
4978 const QStringList fields = QgsProcessingParameters::parameterAsStrings( fieldParam, fieldParam->defaultValueForGui(), context );
4979 mDefaultLineEdit->setText( fields.join( ';' ) );
4980 }
4981 vlayout->addWidget( mDefaultLineEdit );
4982
4983 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
4984
4985 setLayout( vlayout );
4986}
4987
4988QgsProcessingParameterDefinition *QgsProcessingFieldParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
4989{
4990 Qgis::ProcessingFieldParameterDataType dataType = static_cast<Qgis::ProcessingFieldParameterDataType>( mDataTypeComboBox->currentData().toInt() );
4991
4992 QVariant defaultValue;
4993 if ( !mDefaultLineEdit->text().trimmed().isEmpty() )
4994 {
4995 defaultValue = mDefaultLineEdit->text();
4996 }
4997 auto param = std::make_unique<
4998 QgsProcessingParameterField>( name, description, defaultValue, mParentLayerComboBox->currentData().toString(), dataType, mAllowMultipleCheckBox->isChecked(), false, mDefaultToAllCheckBox->isChecked() );
4999 param->setFlags( flags );
5000 return param.release();
5001}
5002
5003QgsProcessingFieldWidgetWrapper::QgsProcessingFieldWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
5004 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
5005{}
5006
5007QWidget *QgsProcessingFieldWidgetWrapper::createWidget()
5008{
5009 const QgsProcessingParameterField *fieldParam = dynamic_cast<const QgsProcessingParameterField *>( parameterDefinition() );
5010 if ( !fieldParam )
5011 return nullptr;
5012
5013 switch ( type() )
5014 {
5017 {
5018 if ( fieldParam->allowMultiple() )
5019 {
5020 mPanel = new QgsProcessingFieldPanelWidget( nullptr, fieldParam );
5021 mPanel->setToolTip( parameterDefinition()->toolTip() );
5022 connect( mPanel, &QgsProcessingFieldPanelWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
5023 return mPanel;
5024 }
5025 else
5026 {
5027 mComboBox = new QgsFieldComboBox();
5028 mComboBox->setAllowEmptyFieldName( fieldParam->flags() & Qgis::ProcessingParameterFlag::Optional );
5029
5031 mComboBox->setFilters( QgsFieldProxyModel::Numeric );
5032 else if ( fieldParam->dataType() == Qgis::ProcessingFieldParameterDataType::String )
5033 mComboBox->setFilters( QgsFieldProxyModel::String );
5036 else if ( fieldParam->dataType() == Qgis::ProcessingFieldParameterDataType::Binary )
5037 mComboBox->setFilters( QgsFieldProxyModel::Binary );
5039 mComboBox->setFilters( QgsFieldProxyModel::Boolean );
5040
5041 mComboBox->setToolTip( parameterDefinition()->toolTip() );
5042 connect( mComboBox, &QgsFieldComboBox::fieldChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
5043 return mComboBox;
5044 }
5045 }
5046
5048 {
5049 mLineEdit = new QLineEdit();
5050 mLineEdit->setToolTip( QObject::tr( "Name of field (separate field names with ; for multiple field parameters)" ) );
5051 connect( mLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
5052 return mLineEdit;
5053 }
5054 }
5055 return nullptr;
5056}
5057
5058void QgsProcessingFieldWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
5059{
5061 switch ( type() )
5062 {
5065 {
5066 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
5067 {
5068 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterField *>( parameterDefinition() )->parentLayerParameterName() )
5069 {
5070 setParentLayerWrapperValue( wrapper );
5071 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentLayerWrapperValue( wrapper ); } );
5072 break;
5073 }
5074 }
5075 break;
5076 }
5077
5079 break;
5080 }
5081}
5082
5083void QgsProcessingFieldWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
5084{
5085 // evaluate value to layer
5086 QgsProcessingContext *context = nullptr;
5087 std::unique_ptr<QgsProcessingContext> tmpContext;
5088 if ( mProcessingContextGenerator )
5089 context = mProcessingContextGenerator->processingContext();
5090
5091 if ( !context )
5092 {
5093 tmpContext = std::make_unique<QgsProcessingContext>();
5094 context = tmpContext.get();
5095 }
5096
5097 QVariant value = parentWrapper->parameterValue();
5098
5099 if ( value.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
5100 {
5101 // input is a QgsProcessingFeatureSourceDefinition - source from it.
5102 // this is normally discouraged, and algorithms should NEVER do this -- but in this case we can make
5103 // certain assumptions due to the fact that we're calling this outside of algorithm/model execution and all sources
5104 // should be real map layers at this stage
5105 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( value );
5106 value = fromVar.source;
5107 }
5108
5109 bool valueSet = false;
5110 const QList<QgsMapLayer *> layers = QgsProcessingParameters::parameterAsLayerList( parentWrapper->parameterDefinition(), value, *context );
5111
5112 // several layers, populate with intersection of layers fields
5113 if ( layers.count() > 1 )
5114 {
5115 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layers.at( 0 ) );
5116 QgsFields fields = vlayer && vlayer->isValid() ? vlayer->fields() : QgsFields();
5117 const QList<QgsMapLayer *> remainingLayers = layers.mid( 1 );
5118 for ( QgsMapLayer *layer : remainingLayers )
5119 {
5120 if ( fields.isEmpty() )
5121 break;
5122
5123 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
5124 if ( !vlayer || !vlayer->isValid() )
5125 {
5126 fields = QgsFields();
5127 break;
5128 }
5129
5130 for ( int fieldIdx = fields.count() - 1; fieldIdx >= 0; fieldIdx-- )
5131 {
5132 if ( vlayer->fields().lookupField( fields.at( fieldIdx ).name() ) < 0 )
5133 fields.remove( fieldIdx );
5134 }
5135 }
5136
5137 if ( mComboBox )
5138 mComboBox->setFields( fields );
5139 else if ( mPanel )
5140 mPanel->setFields( filterFields( fields ) );
5141
5142 valueSet = true;
5143 }
5144
5145 // only one layer
5146 if ( !valueSet && !layers.isEmpty() && layers.at( 0 )->isValid() )
5147 {
5148 QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( layers.at( 0 ) );
5149
5150 // need to grab ownership of layer if required - otherwise layer may be deleted when context
5151 // goes out of scope
5152 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
5153 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Vector )
5154 {
5155 mParentLayer.reset( qobject_cast<QgsVectorLayer *>( ownedLayer.release() ) );
5156 layer = mParentLayer.get();
5157 }
5158 else
5159 {
5160 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
5161 }
5162
5163 if ( mComboBox )
5164 mComboBox->setLayer( layer );
5165 else if ( mPanel )
5166 mPanel->setFields( filterFields( layer->fields() ) );
5167
5168 valueSet = true;
5169 }
5170
5171 if ( !valueSet )
5172 {
5173 std::unique_ptr<QgsProcessingFeatureSource> source( QgsProcessingParameters::parameterAsSource( parentWrapper->parameterDefinition(), value, *context ) );
5174 if ( source )
5175 {
5176 const QgsFields fields = source->fields();
5177 if ( mComboBox )
5178 mComboBox->setFields( fields );
5179 else if ( mPanel )
5180 mPanel->setFields( filterFields( fields ) );
5181
5182 valueSet = true;
5183 }
5184 }
5185
5186 if ( !valueSet )
5187 {
5188 if ( mComboBox )
5189 mComboBox->setLayer( nullptr );
5190 else if ( mPanel )
5191 mPanel->setFields( QgsFields() );
5192
5193 if ( value.isValid() && widgetContext().messageBar() )
5194 {
5195 widgetContext().messageBar()->clearWidgets();
5196 widgetContext().messageBar()->pushMessage( QString(), QObject::tr( "Could not load selected layer/table. Dependent field could not be populated" ), Qgis::MessageLevel::Info );
5197 }
5198 return;
5199 }
5200
5201 const QgsProcessingParameterField *fieldParam = static_cast<const QgsProcessingParameterField *>( parameterDefinition() );
5202 if ( mPanel && fieldParam->defaultToAllFields() )
5203 {
5204 QVariantList val;
5205 val.reserve( mPanel->fields().size() );
5206 for ( const QgsField &field : mPanel->fields() )
5207 val << field.name();
5208 setWidgetValue( val, *context );
5209 }
5210 else if ( fieldParam->defaultValueForGui().isValid() )
5211 setWidgetValue( parameterDefinition()->defaultValueForGui(), *context );
5212}
5213
5214void QgsProcessingFieldWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
5215{
5216 if ( mComboBox )
5217 {
5218 if ( !value.isValid() )
5219 mComboBox->setField( QString() );
5220 else
5221 {
5222 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
5223 mComboBox->setField( v );
5224 }
5225 }
5226 else if ( mPanel )
5227 {
5228 QVariantList opts;
5229 if ( value.isValid() )
5230 {
5231 const QStringList v = QgsProcessingParameters::parameterAsStrings( parameterDefinition(), value, context );
5232 opts.reserve( v.size() );
5233 for ( const QString &i : v )
5234 opts << i;
5235 }
5236 if ( mPanel )
5237 mPanel->setValue( opts );
5238 }
5239 else if ( mLineEdit )
5240 {
5241 const QgsProcessingParameterField *fieldParam = static_cast<const QgsProcessingParameterField *>( parameterDefinition() );
5242 if ( fieldParam->allowMultiple() )
5243 {
5244 const QStringList v = QgsProcessingParameters::parameterAsStrings( parameterDefinition(), value, context );
5245 mLineEdit->setText( v.join( ';' ) );
5246 }
5247 else
5248 {
5249 mLineEdit->setText( QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context ) );
5250 }
5251 }
5252}
5253
5254QVariant QgsProcessingFieldWidgetWrapper::widgetValue() const
5255{
5256 if ( mComboBox )
5257 return mComboBox->currentField();
5258 else if ( mPanel )
5259 return mPanel->value();
5260 else if ( mLineEdit )
5261 {
5262 const QgsProcessingParameterField *fieldParam = static_cast<const QgsProcessingParameterField *>( parameterDefinition() );
5263 if ( fieldParam->allowMultiple() )
5264 {
5265 return mLineEdit->text().split( ';' );
5266 }
5267 else
5268 return mLineEdit->text();
5269 }
5270 else
5271 return QVariant();
5272}
5273
5274QString QgsProcessingFieldWidgetWrapper::modelerExpressionFormatString() const
5275{
5276 return tr( "selected field names as an array of names, or semicolon separated string of options (e.g. 'fid;place_name')" );
5277}
5278
5279const QgsVectorLayer *QgsProcessingFieldWidgetWrapper::linkedVectorLayer() const
5280{
5281 if ( mComboBox && mComboBox->layer() )
5282 return mComboBox->layer();
5283
5285}
5286
5287QgsFields QgsProcessingFieldWidgetWrapper::filterFields( const QgsFields &fields ) const
5288{
5289 const QgsProcessingParameterField *fieldParam = static_cast<const QgsProcessingParameterField *>( parameterDefinition() );
5290 QgsFields res;
5291 for ( const QgsField &f : fields )
5292 {
5293 switch ( fieldParam->dataType() )
5294 {
5296 res.append( f );
5297 break;
5298
5300 if ( f.isNumeric() )
5301 res.append( f );
5302 break;
5303
5305 if ( f.type() == QMetaType::Type::QString )
5306 res.append( f );
5307 break;
5308
5310 if ( f.type() == QMetaType::Type::QDate || f.type() == QMetaType::Type::QTime || f.type() == QMetaType::Type::QDateTime )
5311 res.append( f );
5312 break;
5313
5315 if ( f.type() == QMetaType::Type::QByteArray )
5316 res.append( f );
5317 break;
5318
5320 if ( f.type() == QMetaType::Type::Bool )
5321 res.append( f );
5322 break;
5323 }
5324 }
5325
5326 return res;
5327}
5328
5329QString QgsProcessingFieldWidgetWrapper::parameterType() const
5330{
5332}
5333
5334QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFieldWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
5335{
5336 return new QgsProcessingFieldWidgetWrapper( parameter, type );
5337}
5338
5339QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingFieldWidgetWrapper::createParameterDefinitionWidget(
5341)
5342{
5343 return new QgsProcessingFieldParameterDefinitionWidget( context, widgetContext, definition, algorithm );
5344}
5345
5346//
5347// QgsProcessingMapThemeWidgetWrapper
5348//
5349
5350
5351QgsProcessingMapThemeParameterDefinitionWidget::QgsProcessingMapThemeParameterDefinitionWidget(
5352 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
5353)
5354 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
5355{
5356 QVBoxLayout *vlayout = new QVBoxLayout();
5357 vlayout->setContentsMargins( 0, 0, 0, 0 );
5358
5359 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
5360
5361 mDefaultComboBox = new QComboBox();
5362 mDefaultComboBox->addItem( QString(), QVariant( -1 ) );
5363
5364 const QStringList mapThemes = widgetContext.project() ? widgetContext.project()->mapThemeCollection()->mapThemes() : QgsProject::instance()->mapThemeCollection()->mapThemes();
5365 for ( const QString &theme : mapThemes )
5366 {
5367 mDefaultComboBox->addItem( QgsApplication::getThemeIcon( u"/mActionShowAllLayers.svg"_s ), theme, theme );
5368 }
5369 mDefaultComboBox->setEditable( true );
5370
5371 if ( const QgsProcessingParameterMapTheme *themeParam = dynamic_cast<const QgsProcessingParameterMapTheme *>( definition ) )
5372 {
5373 if ( themeParam->defaultValueForGui().isValid() )
5374 mDefaultComboBox->setCurrentText( QgsProcessingParameters::parameterAsString( themeParam, themeParam->defaultValueForGui(), context ) );
5375 else
5376 mDefaultComboBox->setCurrentIndex( mDefaultComboBox->findData( -1 ) );
5377 }
5378 else
5379 mDefaultComboBox->setCurrentIndex( mDefaultComboBox->findData( -1 ) );
5380
5381 vlayout->addWidget( mDefaultComboBox );
5382
5383 connect( mDefaultComboBox, &QComboBox::currentTextChanged, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
5384
5385 setLayout( vlayout );
5386}
5387
5388QgsProcessingParameterDefinition *QgsProcessingMapThemeParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
5389{
5390 QVariant defaultVal;
5391 if ( mDefaultComboBox->currentText().isEmpty() )
5392 defaultVal = QVariant();
5393 else
5394 defaultVal = mDefaultComboBox->currentText();
5395 auto param = std::make_unique<QgsProcessingParameterMapTheme>( name, description, defaultVal );
5396 param->setFlags( flags );
5397 return param.release();
5398}
5399
5400
5401QgsProcessingMapThemeWidgetWrapper::QgsProcessingMapThemeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
5402 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
5403{}
5404
5405QWidget *QgsProcessingMapThemeWidgetWrapper::createWidget()
5406{
5407 const QgsProcessingParameterMapTheme *themeParam = dynamic_cast<const QgsProcessingParameterMapTheme *>( parameterDefinition() );
5408 if ( !themeParam )
5409 return nullptr;
5410
5411 mComboBox = new QComboBox();
5412
5413 if ( themeParam->flags() & Qgis::ProcessingParameterFlag::Optional )
5414 mComboBox->addItem( tr( "[Not selected]" ), QVariant( -1 ) );
5415
5416 const QStringList mapThemes = widgetContext().project() ? widgetContext().project()->mapThemeCollection()->mapThemes() : QgsProject::instance()->mapThemeCollection()->mapThemes();
5417 for ( const QString &theme : mapThemes )
5418 {
5419 mComboBox->addItem( QgsApplication::getThemeIcon( u"/mActionShowAllLayers.svg"_s ), theme, theme );
5420 }
5421
5422 switch ( type() )
5423 {
5426 break;
5427
5429 mComboBox->setEditable( true );
5430 break;
5431 }
5432
5433 mComboBox->setToolTip( parameterDefinition()->toolTip() );
5434 connect( mComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this]( int ) { emit widgetValueHasChanged( this ); } );
5435
5436 return mComboBox;
5437}
5438
5439void QgsProcessingMapThemeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
5440{
5441 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
5442
5443 if ( !value.isValid() )
5444 mComboBox->setCurrentIndex( mComboBox->findData( QVariant( -1 ) ) );
5445 else
5446 {
5447 if ( mComboBox->isEditable() && mComboBox->findData( v ) == -1 )
5448 {
5449 const QString prev = mComboBox->currentText();
5450 mComboBox->setCurrentText( v );
5451 if ( prev != v )
5452 emit widgetValueHasChanged( this );
5453 }
5454 else
5455 mComboBox->setCurrentIndex( mComboBox->findData( v ) );
5456 }
5457}
5458
5459QVariant QgsProcessingMapThemeWidgetWrapper::widgetValue() const
5460{
5461 if ( mComboBox )
5462 return mComboBox->currentData().toInt() == -1 ? QVariant()
5463 : !mComboBox->currentData().isValid() && mComboBox->isEditable() ? mComboBox->currentText().isEmpty() ? QVariant() : QVariant( mComboBox->currentText() )
5464 : mComboBox->currentData();
5465 else
5466 return QVariant();
5467}
5468
5469QString QgsProcessingMapThemeWidgetWrapper::modelerExpressionFormatString() const
5470{
5471 return tr( "map theme as a string value (e.g. 'base maps')" );
5472}
5473
5474QString QgsProcessingMapThemeWidgetWrapper::parameterType() const
5475{
5477}
5478
5479QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingMapThemeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
5480{
5481 return new QgsProcessingMapThemeWidgetWrapper( parameter, type );
5482}
5483
5484QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingMapThemeWidgetWrapper::createParameterDefinitionWidget(
5486)
5487{
5488 return new QgsProcessingMapThemeParameterDefinitionWidget( context, widgetContext, definition, algorithm );
5489}
5490
5491
5492//
5493// QgsProcessingDateTimeWidgetWrapper
5494//
5495
5496
5497QgsProcessingDateTimeParameterDefinitionWidget::QgsProcessingDateTimeParameterDefinitionWidget(
5498 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
5499)
5500 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
5501{
5502 QVBoxLayout *vlayout = new QVBoxLayout();
5503 vlayout->setContentsMargins( 0, 0, 0, 0 );
5504
5505 vlayout->addWidget( new QLabel( tr( "Type" ) ) );
5506
5507 mTypeComboBox = new QComboBox();
5508 mTypeComboBox->addItem( tr( "Date and Time" ), static_cast<int>( Qgis::ProcessingDateTimeParameterDataType::DateTime ) );
5509 mTypeComboBox->addItem( tr( "Date" ), static_cast<int>( Qgis::ProcessingDateTimeParameterDataType::Date ) );
5510 mTypeComboBox->addItem( tr( "Time" ), static_cast<int>( Qgis::ProcessingDateTimeParameterDataType::Time ) );
5511 if ( const QgsProcessingParameterDateTime *datetimeParam = dynamic_cast<const QgsProcessingParameterDateTime *>( definition ) )
5512 mTypeComboBox->setCurrentIndex( mTypeComboBox->findData( static_cast<int>( datetimeParam->dataType() ) ) );
5513 else
5514 mTypeComboBox->setCurrentIndex( 0 );
5515 vlayout->addWidget( mTypeComboBox );
5516
5517 connect( mTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
5518
5519 setLayout( vlayout );
5520}
5521
5522QgsProcessingParameterDefinition *QgsProcessingDateTimeParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
5523{
5524 auto param = std::make_unique<QgsProcessingParameterDateTime>( name, description );
5525 param->setDataType( static_cast<Qgis::ProcessingDateTimeParameterDataType>( mTypeComboBox->currentData().toInt() ) );
5526 param->setFlags( flags );
5527 return param.release();
5528}
5529
5530
5531QgsProcessingDateTimeWidgetWrapper::QgsProcessingDateTimeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
5532 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
5533{}
5534
5535QWidget *QgsProcessingDateTimeWidgetWrapper::createWidget()
5536{
5537 const QgsProcessingParameterDateTime *dateTimeParam = dynamic_cast<const QgsProcessingParameterDateTime *>( parameterDefinition() );
5538 if ( !dateTimeParam )
5539 return nullptr;
5540
5541 QgsDateTimeEdit *widget = nullptr;
5542 switch ( dateTimeParam->dataType() )
5543 {
5545 mDateTimeEdit = new QgsDateTimeEdit();
5546 widget = mDateTimeEdit;
5547 break;
5548
5550 mDateEdit = new QgsDateEdit();
5551 widget = mDateEdit;
5552 break;
5553
5555 mTimeEdit = new QgsTimeEdit();
5556 widget = mTimeEdit;
5557 break;
5558 }
5559 if ( !widget )
5560 return nullptr;
5561
5562 if ( dateTimeParam->flags() & Qgis::ProcessingParameterFlag::Optional )
5563 {
5564 widget->setNullRepresentation( tr( "[Not selected]" ) );
5565 widget->setAllowNull( true );
5566 }
5567 else
5568 {
5569 widget->setAllowNull( false );
5570 }
5571 widget->setToolTip( parameterDefinition()->toolTip() );
5572
5573 if ( mDateTimeEdit )
5574 {
5575 connect( mDateTimeEdit, &QgsDateTimeEdit::valueChanged, this, [this]( const QDateTime & ) { emit widgetValueHasChanged( this ); } );
5576 }
5577 else if ( mDateEdit )
5578 {
5579 connect( mDateEdit, &QgsDateEdit::dateValueChanged, this, [this]( const QDate & ) { emit widgetValueHasChanged( this ); } );
5580 }
5581 else if ( mTimeEdit )
5582 {
5583 connect( mTimeEdit, &QgsTimeEdit::timeValueChanged, this, [this]( const QTime & ) { emit widgetValueHasChanged( this ); } );
5584 }
5585
5586 return widget;
5587}
5588
5589QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingDateTimeWidgetWrapper::createParameterDefinitionWidget(
5591)
5592{
5593 return new QgsProcessingDateTimeParameterDefinitionWidget( context, widgetContext, definition, algorithm );
5594}
5595
5596void QgsProcessingDateTimeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
5597{
5598 if ( mDateTimeEdit )
5599 {
5600 mDateTimeEdit->setDateTime( QgsProcessingParameters::parameterAsDateTime( parameterDefinition(), value, context ) );
5601 }
5602 else if ( mDateEdit )
5603 {
5604 mDateEdit->setDate( QgsProcessingParameters::parameterAsDate( parameterDefinition(), value, context ) );
5605 }
5606 else if ( mTimeEdit )
5607 {
5608 mTimeEdit->setTime( QgsProcessingParameters::parameterAsTime( parameterDefinition(), value, context ) );
5609 }
5610}
5611
5612QVariant QgsProcessingDateTimeWidgetWrapper::widgetValue() const
5613{
5614 if ( mDateTimeEdit )
5615 return !mDateTimeEdit->dateTime().isNull() && mDateTimeEdit->dateTime().isValid() ? QVariant( mDateTimeEdit->dateTime() ) : QVariant();
5616 else if ( mDateEdit )
5617 return !mDateEdit->date().isNull() && mDateEdit->date().isValid() ? QVariant( mDateEdit->date() ) : QVariant();
5618 else if ( mTimeEdit )
5619 return !mTimeEdit->time().isNull() && mTimeEdit->time().isValid() ? QVariant( mTimeEdit->time() ) : QVariant();
5620 else
5621 return QVariant();
5622}
5623
5624QString QgsProcessingDateTimeWidgetWrapper::modelerExpressionFormatString() const
5625{
5626 const QgsProcessingParameterDateTime *dateTimeParam = dynamic_cast<const QgsProcessingParameterDateTime *>( parameterDefinition() );
5627 if ( dateTimeParam )
5628 {
5629 switch ( dateTimeParam->dataType() )
5630 {
5632 return tr( "datetime value, or a ISO string representation of a datetime" );
5633
5635 return tr( "date value, or a ISO string representation of a date" );
5636
5638 return tr( "time value, or a ISO string representation of a time" );
5639 }
5640 }
5641 return QString();
5642}
5643
5644QString QgsProcessingDateTimeWidgetWrapper::parameterType() const
5645{
5647}
5648
5649QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingDateTimeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
5650{
5651 return new QgsProcessingDateTimeWidgetWrapper( parameter, type );
5652}
5653
5654
5655//
5656// QgsProcessingProviderConnectionWidgetWrapper
5657//
5658
5659QgsProcessingProviderConnectionParameterDefinitionWidget::QgsProcessingProviderConnectionParameterDefinitionWidget(
5660 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
5661)
5662 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
5663{
5664 const QgsProcessingParameterProviderConnection *connectionParam = dynamic_cast<const QgsProcessingParameterProviderConnection *>( definition );
5665
5666 QVBoxLayout *vlayout = new QVBoxLayout();
5667 vlayout->setContentsMargins( 0, 0, 0, 0 );
5668
5669 vlayout->addWidget( new QLabel( tr( "Provider" ) ) );
5670 mProviderComboBox = new QComboBox();
5671 mProviderComboBox->addItem( QObject::tr( "Postgres" ), u"postgres"_s );
5672 mProviderComboBox->addItem( QObject::tr( "GeoPackage" ), u"ogr"_s );
5673 mProviderComboBox->addItem( QObject::tr( "Spatialite" ), u"spatialite"_s );
5674
5675 vlayout->addWidget( mProviderComboBox );
5676
5677 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
5678
5679 mDefaultEdit = new QLineEdit();
5680 vlayout->addWidget( mDefaultEdit );
5681 setLayout( vlayout );
5682
5683 if ( connectionParam )
5684 {
5685 mProviderComboBox->setCurrentIndex( mProviderComboBox->findData( connectionParam->providerId() ) );
5686 mDefaultEdit->setText( connectionParam->defaultValueForGui().toString() );
5687 }
5688
5689 connect( mProviderComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
5690 connect( mDefaultEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
5691}
5692
5693QgsProcessingParameterDefinition *QgsProcessingProviderConnectionParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
5694{
5695 QVariant defaultVal;
5696 if ( mDefaultEdit->text().isEmpty() )
5697 defaultVal = QVariant();
5698 else
5699 defaultVal = mDefaultEdit->text();
5700 auto param = std::make_unique<QgsProcessingParameterProviderConnection>( name, description, mProviderComboBox->currentData().toString(), defaultVal );
5701 param->setFlags( flags );
5702 return param.release();
5703}
5704
5705
5706QgsProcessingProviderConnectionWidgetWrapper::QgsProcessingProviderConnectionWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
5707 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
5708{}
5709
5710QWidget *QgsProcessingProviderConnectionWidgetWrapper::createWidget()
5711{
5712 const QgsProcessingParameterProviderConnection *connectionParam = dynamic_cast<const QgsProcessingParameterProviderConnection *>( parameterDefinition() );
5713 if ( !connectionParam )
5714 return nullptr;
5715
5716 mProviderComboBox = new QgsProviderConnectionComboBox( connectionParam->providerId() );
5717 if ( connectionParam->flags() & Qgis::ProcessingParameterFlag::Optional )
5718 mProviderComboBox->setAllowEmptyConnection( true );
5719
5720 switch ( type() )
5721 {
5724 break;
5726 mProviderComboBox->setEditable( true );
5727 break;
5728 }
5729
5730 mProviderComboBox->setToolTip( parameterDefinition()->toolTip() );
5731 connect( mProviderComboBox, &QgsProviderConnectionComboBox::currentTextChanged, this, [this]( const QString & ) {
5732 if ( mBlockSignals )
5733 return;
5734
5735 emit widgetValueHasChanged( this );
5736 } );
5737
5738 return mProviderComboBox;
5739}
5740
5741QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingProviderConnectionWidgetWrapper::createParameterDefinitionWidget(
5743)
5744{
5745 return new QgsProcessingProviderConnectionParameterDefinitionWidget( context, widgetContext, definition, algorithm );
5746}
5747
5748void QgsProcessingProviderConnectionWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
5749{
5750 const QString v = QgsProcessingParameters::parameterAsConnectionName( parameterDefinition(), value, context );
5751
5752 if ( !value.isValid() )
5753 mProviderComboBox->setCurrentIndex( -1 );
5754 else
5755 {
5756 if ( mProviderComboBox->isEditable() )
5757 {
5758 const QString prev = mProviderComboBox->currentText();
5759 mBlockSignals++;
5760 mProviderComboBox->setConnection( v );
5761 mProviderComboBox->setCurrentText( v );
5762
5763 mBlockSignals--;
5764 if ( prev != v )
5765 emit widgetValueHasChanged( this );
5766 }
5767 else
5768 mProviderComboBox->setConnection( v );
5769 }
5770}
5771
5772QVariant QgsProcessingProviderConnectionWidgetWrapper::widgetValue() const
5773{
5774 if ( mProviderComboBox )
5775 if ( mProviderComboBox->isEditable() )
5776 return mProviderComboBox->currentText().isEmpty() ? QVariant() : QVariant( mProviderComboBox->currentText() );
5777 else
5778 return mProviderComboBox->currentConnection().isEmpty() ? QVariant() : QVariant( mProviderComboBox->currentConnection() );
5779 else
5780 return QVariant();
5781}
5782
5783QString QgsProcessingProviderConnectionWidgetWrapper::modelerExpressionFormatString() const
5784{
5785 return tr( "connection name as a string value" );
5786}
5787
5788QString QgsProcessingProviderConnectionWidgetWrapper::parameterType() const
5789{
5791}
5792
5793QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingProviderConnectionWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
5794{
5795 return new QgsProcessingProviderConnectionWidgetWrapper( parameter, type );
5796}
5797
5798
5799//
5800// QgsProcessingDatabaseSchemaWidgetWrapper
5801//
5802
5803QgsProcessingDatabaseSchemaParameterDefinitionWidget::QgsProcessingDatabaseSchemaParameterDefinitionWidget(
5804 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
5805)
5806 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
5807{
5808 const QgsProcessingParameterDatabaseSchema *schemaParam = dynamic_cast<const QgsProcessingParameterDatabaseSchema *>( definition );
5809
5810 QVBoxLayout *vlayout = new QVBoxLayout();
5811 vlayout->setContentsMargins( 0, 0, 0, 0 );
5812
5813 mConnectionParamComboBox = new QComboBox();
5814 QString initialConnection;
5815 if ( schemaParam )
5816 {
5817 initialConnection = schemaParam->parentConnectionParameterName();
5818 }
5819
5820 if ( auto *lModel = widgetContext.model() )
5821 {
5822 // populate combo box with other model input choices
5823 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
5824 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
5825 {
5826 if ( definition && it->parameterName() == definition->name() )
5827 continue;
5828
5829 if ( !dynamic_cast<const QgsProcessingParameterProviderConnection *>( lModel->parameterDefinition( it->parameterName() ) ) )
5830 continue;
5831
5832 mConnectionParamComboBox->addItem( it->parameterName(), it->parameterName() );
5833 if ( !initialConnection.isEmpty() && initialConnection == it->parameterName() )
5834 {
5835 mConnectionParamComboBox->setCurrentIndex( mConnectionParamComboBox->count() - 1 );
5836 }
5837 }
5838 }
5839
5840 if ( mConnectionParamComboBox->count() == 0 && !initialConnection.isEmpty() )
5841 {
5842 // if no candidates found, we just add the existing one as a placeholder
5843 mConnectionParamComboBox->addItem( initialConnection, initialConnection );
5844 mConnectionParamComboBox->setCurrentIndex( mConnectionParamComboBox->count() - 1 );
5845 }
5846
5847 vlayout->addWidget( new QLabel( tr( "Provider connection parameter" ) ) );
5848 vlayout->addWidget( mConnectionParamComboBox );
5849
5850 connect( mConnectionParamComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
5851
5852 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
5853
5854 mDefaultEdit = new QLineEdit();
5855 vlayout->addWidget( mDefaultEdit );
5856 setLayout( vlayout );
5857
5858 if ( schemaParam )
5859 {
5860 mDefaultEdit->setText( schemaParam->defaultValueForGui().toString() );
5861 }
5862 connect( mDefaultEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
5863}
5864
5865QgsProcessingParameterDefinition *QgsProcessingDatabaseSchemaParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
5866{
5867 QVariant defaultVal;
5868 if ( mDefaultEdit->text().isEmpty() )
5869 defaultVal = QVariant();
5870 else
5871 defaultVal = mDefaultEdit->text();
5872 auto param = std::make_unique<QgsProcessingParameterDatabaseSchema>( name, description, mConnectionParamComboBox->currentData().toString(), defaultVal );
5873 param->setFlags( flags );
5874 return param.release();
5875}
5876
5877
5878QgsProcessingDatabaseSchemaWidgetWrapper::QgsProcessingDatabaseSchemaWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
5879 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
5880{}
5881
5882QWidget *QgsProcessingDatabaseSchemaWidgetWrapper::createWidget()
5883{
5884 const QgsProcessingParameterDatabaseSchema *schemaParam = dynamic_cast<const QgsProcessingParameterDatabaseSchema *>( parameterDefinition() );
5885 if ( !schemaParam )
5886 return nullptr;
5887
5888 mSchemaComboBox = new QgsDatabaseSchemaComboBox( QString(), QString() );
5889 if ( schemaParam->flags() & Qgis::ProcessingParameterFlag::Optional )
5890 mSchemaComboBox->setAllowEmptySchema( true );
5891
5892 switch ( type() )
5893 {
5896 break;
5898 mSchemaComboBox->comboBox()->setEditable( true );
5899 break;
5900 }
5901
5902 mSchemaComboBox->setToolTip( parameterDefinition()->toolTip() );
5903 connect( mSchemaComboBox->comboBox(), &QComboBox::currentTextChanged, this, [this]( const QString & ) {
5904 if ( mBlockSignals )
5905 return;
5906
5907 emit widgetValueHasChanged( this );
5908 } );
5909
5910 return mSchemaComboBox;
5911}
5912
5913QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingDatabaseSchemaWidgetWrapper::createParameterDefinitionWidget(
5915)
5916{
5917 return new QgsProcessingDatabaseSchemaParameterDefinitionWidget( context, widgetContext, definition, algorithm );
5918}
5919
5920void QgsProcessingDatabaseSchemaWidgetWrapper::setParentConnectionWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
5921{
5922 // evaluate value to connection
5923 QgsProcessingContext *context = nullptr;
5924 std::unique_ptr<QgsProcessingContext> tmpContext;
5925 if ( mProcessingContextGenerator )
5926 context = mProcessingContextGenerator->processingContext();
5927
5928 if ( !context )
5929 {
5930 tmpContext = std::make_unique<QgsProcessingContext>();
5931 context = tmpContext.get();
5932 }
5933
5934 const QVariant value = parentWrapper->parameterValue();
5935 const QString connection = value.isValid() ? QgsProcessingParameters::parameterAsConnectionName( parentWrapper->parameterDefinition(), value, *context ) : QString();
5936
5937 if ( mSchemaComboBox )
5938 mSchemaComboBox->setConnectionName( connection, qgis::down_cast<const QgsProcessingParameterProviderConnection *>( parentWrapper->parameterDefinition() )->providerId() );
5939
5940 const QgsProcessingParameterDatabaseSchema *schemaParam = qgis::down_cast<const QgsProcessingParameterDatabaseSchema *>( parameterDefinition() );
5941 if ( schemaParam->defaultValueForGui().isValid() )
5942 setWidgetValue( parameterDefinition()->defaultValueForGui(), *context );
5943}
5944
5945void QgsProcessingDatabaseSchemaWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
5946{
5947 const QString v = QgsProcessingParameters::parameterAsSchema( parameterDefinition(), value, context );
5948
5949 if ( !value.isValid() )
5950 mSchemaComboBox->comboBox()->setCurrentIndex( -1 );
5951 else
5952 {
5953 if ( mSchemaComboBox->comboBox()->isEditable() )
5954 {
5955 const QString prev = mSchemaComboBox->comboBox()->currentText();
5956 mBlockSignals++;
5957 mSchemaComboBox->setSchema( v );
5958 mSchemaComboBox->comboBox()->setCurrentText( v );
5959
5960 mBlockSignals--;
5961 if ( prev != v )
5962 emit widgetValueHasChanged( this );
5963 }
5964 else
5965 mSchemaComboBox->setSchema( v );
5966 }
5967}
5968
5969QVariant QgsProcessingDatabaseSchemaWidgetWrapper::widgetValue() const
5970{
5971 if ( mSchemaComboBox )
5972 if ( mSchemaComboBox->comboBox()->isEditable() )
5973 return mSchemaComboBox->comboBox()->currentText().isEmpty() ? QVariant() : QVariant( mSchemaComboBox->comboBox()->currentText() );
5974 else
5975 return mSchemaComboBox->currentSchema().isEmpty() ? QVariant() : QVariant( mSchemaComboBox->currentSchema() );
5976 else
5977 return QVariant();
5978}
5979
5980QString QgsProcessingDatabaseSchemaWidgetWrapper::modelerExpressionFormatString() const
5981{
5982 return tr( "database schema name as a string value" );
5983}
5984
5985QString QgsProcessingDatabaseSchemaWidgetWrapper::parameterType() const
5986{
5988}
5989
5990QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingDatabaseSchemaWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
5991{
5992 return new QgsProcessingDatabaseSchemaWidgetWrapper( parameter, type );
5993}
5994
5995void QgsProcessingDatabaseSchemaWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
5996{
5998 switch ( type() )
5999 {
6002 {
6003 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
6004 {
6005 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterDatabaseSchema *>( parameterDefinition() )->parentConnectionParameterName() )
6006 {
6007 setParentConnectionWrapperValue( wrapper );
6008 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentConnectionWrapperValue( wrapper ); } );
6009 break;
6010 }
6011 }
6012 break;
6013 }
6014
6016 break;
6017 }
6018}
6019
6020
6021//
6022// QgsProcessingDatabaseTableWidgetWrapper
6023//
6024
6025QgsProcessingDatabaseTableParameterDefinitionWidget::QgsProcessingDatabaseTableParameterDefinitionWidget(
6026 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
6027)
6028 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
6029{
6030 const QgsProcessingParameterDatabaseTable *tableParam = dynamic_cast<const QgsProcessingParameterDatabaseTable *>( definition );
6031
6032 QVBoxLayout *vlayout = new QVBoxLayout();
6033 vlayout->setContentsMargins( 0, 0, 0, 0 );
6034
6035 mConnectionParamComboBox = new QComboBox();
6036 mSchemaParamComboBox = new QComboBox();
6037 QString initialConnection;
6038 QString initialSchema;
6039 if ( tableParam )
6040 {
6041 initialConnection = tableParam->parentConnectionParameterName();
6042 initialSchema = tableParam->parentSchemaParameterName();
6043 }
6044
6045 if ( auto *lModel = widgetContext.model() )
6046 {
6047 // populate combo box with other model input choices
6048 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
6049 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
6050 {
6051 if ( definition && it->parameterName() == definition->name() )
6052 continue;
6053
6054 if ( dynamic_cast<const QgsProcessingParameterProviderConnection *>( lModel->parameterDefinition( it->parameterName() ) ) )
6055 {
6056 mConnectionParamComboBox->addItem( it->parameterName(), it->parameterName() );
6057 if ( !initialConnection.isEmpty() && initialConnection == it->parameterName() )
6058 {
6059 mConnectionParamComboBox->setCurrentIndex( mConnectionParamComboBox->count() - 1 );
6060 }
6061 }
6062 else if ( dynamic_cast<const QgsProcessingParameterDatabaseSchema *>( lModel->parameterDefinition( it->parameterName() ) ) )
6063 {
6064 mSchemaParamComboBox->addItem( it->parameterName(), it->parameterName() );
6065 if ( !initialConnection.isEmpty() && initialConnection == it->parameterName() )
6066 {
6067 mSchemaParamComboBox->setCurrentIndex( mSchemaParamComboBox->count() - 1 );
6068 }
6069 }
6070 }
6071 }
6072
6073 if ( mConnectionParamComboBox->count() == 0 && !initialConnection.isEmpty() )
6074 {
6075 // if no candidates found, we just add the existing one as a placeholder
6076 mConnectionParamComboBox->addItem( initialConnection, initialConnection );
6077 mConnectionParamComboBox->setCurrentIndex( mConnectionParamComboBox->count() - 1 );
6078 }
6079
6080 if ( mSchemaParamComboBox->count() == 0 && !initialSchema.isEmpty() )
6081 {
6082 // if no candidates found, we just add the existing one as a placeholder
6083 mSchemaParamComboBox->addItem( initialSchema, initialSchema );
6084 mSchemaParamComboBox->setCurrentIndex( mSchemaParamComboBox->count() - 1 );
6085 }
6086
6087 vlayout->addWidget( new QLabel( tr( "Provider connection parameter" ) ) );
6088 vlayout->addWidget( mConnectionParamComboBox );
6089
6090 vlayout->addWidget( new QLabel( tr( "Database schema parameter" ) ) );
6091 vlayout->addWidget( mSchemaParamComboBox );
6092
6093 connect( mConnectionParamComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
6094 connect( mSchemaParamComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
6095
6096 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
6097
6098 mDefaultEdit = new QLineEdit();
6099 vlayout->addWidget( mDefaultEdit );
6100 setLayout( vlayout );
6101
6102 if ( tableParam )
6103 {
6104 mDefaultEdit->setText( tableParam->defaultValueForGui().toString() );
6105 }
6106 connect( mDefaultEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
6107}
6108
6109QgsProcessingParameterDefinition *QgsProcessingDatabaseTableParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
6110{
6111 QVariant defaultVal;
6112 if ( mDefaultEdit->text().isEmpty() )
6113 defaultVal = QVariant();
6114 else
6115 defaultVal = mDefaultEdit->text();
6116 auto param = std::make_unique<QgsProcessingParameterDatabaseTable>( name, description, mConnectionParamComboBox->currentData().toString(), mSchemaParamComboBox->currentData().toString(), defaultVal );
6117 param->setFlags( flags );
6118 return param.release();
6119}
6120
6121
6122QgsProcessingDatabaseTableWidgetWrapper::QgsProcessingDatabaseTableWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6123 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
6124{}
6125
6126QWidget *QgsProcessingDatabaseTableWidgetWrapper::createWidget()
6127{
6128 const QgsProcessingParameterDatabaseTable *tableParam = dynamic_cast<const QgsProcessingParameterDatabaseTable *>( parameterDefinition() );
6129 if ( !tableParam )
6130 return nullptr;
6131
6132 mTableComboBox = new QgsDatabaseTableComboBox( QString(), QString() );
6133 if ( tableParam->flags() & Qgis::ProcessingParameterFlag::Optional )
6134 mTableComboBox->setAllowEmptyTable( true );
6135
6136 if ( type() == Qgis::ProcessingMode::Modeler || tableParam->allowNewTableNames() )
6137 mTableComboBox->comboBox()->setEditable( true );
6138
6139 mTableComboBox->setToolTip( parameterDefinition()->toolTip() );
6140 connect( mTableComboBox->comboBox(), &QComboBox::currentTextChanged, this, [this]( const QString & ) {
6141 if ( mBlockSignals )
6142 return;
6143
6144 emit widgetValueHasChanged( this );
6145 } );
6146
6147 return mTableComboBox;
6148}
6149
6150QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingDatabaseTableWidgetWrapper::createParameterDefinitionWidget(
6152)
6153{
6154 return new QgsProcessingDatabaseTableParameterDefinitionWidget( context, widgetContext, definition, algorithm );
6155}
6156
6157void QgsProcessingDatabaseTableWidgetWrapper::setParentConnectionWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
6158{
6159 // evaluate value to connection
6160 QgsProcessingContext *context = nullptr;
6161 std::unique_ptr<QgsProcessingContext> tmpContext;
6162 if ( mProcessingContextGenerator )
6163 context = mProcessingContextGenerator->processingContext();
6164
6165 if ( !context )
6166 {
6167 tmpContext = std::make_unique<QgsProcessingContext>();
6168 context = tmpContext.get();
6169 }
6170
6171 QVariant value = parentWrapper->parameterValue();
6172 mConnection = value.isValid() ? QgsProcessingParameters::parameterAsConnectionName( parentWrapper->parameterDefinition(), value, *context ) : QString();
6173 mProvider = qgis::down_cast<const QgsProcessingParameterProviderConnection *>( parentWrapper->parameterDefinition() )->providerId();
6174 if ( mTableComboBox && !mSchema.isEmpty() )
6175 {
6176 mTableComboBox->setSchema( mSchema );
6177 mTableComboBox->setConnectionName( mConnection, mProvider );
6178
6179 const QgsProcessingParameterDatabaseTable *tableParam = qgis::down_cast<const QgsProcessingParameterDatabaseTable *>( parameterDefinition() );
6180 if ( tableParam->defaultValueForGui().isValid() )
6181 setWidgetValue( parameterDefinition()->defaultValueForGui(), *context );
6182 }
6183}
6184
6185void QgsProcessingDatabaseTableWidgetWrapper::setParentSchemaWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
6186{
6187 // evaluate value to schema
6188 QgsProcessingContext *context = nullptr;
6189 std::unique_ptr<QgsProcessingContext> tmpContext;
6190 if ( mProcessingContextGenerator )
6191 context = mProcessingContextGenerator->processingContext();
6192
6193 if ( !context )
6194 {
6195 tmpContext = std::make_unique<QgsProcessingContext>();
6196 context = tmpContext.get();
6197 }
6198
6199 QVariant value = parentWrapper->parameterValue();
6200 mSchema = value.isValid() ? QgsProcessingParameters::parameterAsSchema( parentWrapper->parameterDefinition(), value, *context ) : QString();
6201
6202 if ( mTableComboBox && !mSchema.isEmpty() && !mConnection.isEmpty() )
6203 {
6204 mTableComboBox->setSchema( mSchema );
6205 mTableComboBox->setConnectionName( mConnection, mProvider );
6206
6207 const QgsProcessingParameterDatabaseTable *tableParam = static_cast<const QgsProcessingParameterDatabaseTable *>( parameterDefinition() );
6208 if ( tableParam->defaultValueForGui().isValid() )
6209 setWidgetValue( parameterDefinition()->defaultValueForGui(), *context );
6210 }
6211}
6212
6213void QgsProcessingDatabaseTableWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
6214{
6215 const QString v = QgsProcessingParameters::parameterAsDatabaseTableName( parameterDefinition(), value, context );
6216
6217 if ( !value.isValid() )
6218 mTableComboBox->comboBox()->setCurrentIndex( -1 );
6219 else
6220 {
6221 if ( mTableComboBox->comboBox()->isEditable() )
6222 {
6223 const QString prev = mTableComboBox->comboBox()->currentText();
6224 mBlockSignals++;
6225 mTableComboBox->setTable( v );
6226 mTableComboBox->comboBox()->setCurrentText( v );
6227
6228 mBlockSignals--;
6229 if ( prev != v )
6230 emit widgetValueHasChanged( this );
6231 }
6232 else
6233 mTableComboBox->setTable( v );
6234 }
6235}
6236
6237QVariant QgsProcessingDatabaseTableWidgetWrapper::widgetValue() const
6238{
6239 if ( mTableComboBox )
6240 if ( mTableComboBox->comboBox()->isEditable() )
6241 return mTableComboBox->comboBox()->currentText().isEmpty() ? QVariant() : QVariant( mTableComboBox->comboBox()->currentText() );
6242 else
6243 return mTableComboBox->currentTable().isEmpty() ? QVariant() : QVariant( mTableComboBox->currentTable() );
6244 else
6245 return QVariant();
6246}
6247
6248QString QgsProcessingDatabaseTableWidgetWrapper::modelerExpressionFormatString() const
6249{
6250 return tr( "database table name as a string value" );
6251}
6252
6253QString QgsProcessingDatabaseTableWidgetWrapper::parameterType() const
6254{
6256}
6257
6258QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingDatabaseTableWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6259{
6260 return new QgsProcessingDatabaseTableWidgetWrapper( parameter, type );
6261}
6262
6263void QgsProcessingDatabaseTableWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
6264{
6266 switch ( type() )
6267 {
6270 {
6271 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
6272 {
6273 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterDatabaseTable *>( parameterDefinition() )->parentConnectionParameterName() )
6274 {
6275 setParentConnectionWrapperValue( wrapper );
6276 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentConnectionWrapperValue( wrapper ); } );
6277 }
6278 else if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterDatabaseTable *>( parameterDefinition() )->parentSchemaParameterName() )
6279 {
6280 setParentSchemaWrapperValue( wrapper );
6281 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentSchemaWrapperValue( wrapper ); } );
6282 }
6283 }
6284 break;
6285 }
6286
6288 break;
6289 }
6290}
6291
6292
6293//
6294// QgsProcessingExtentWidgetWrapper
6295//
6296
6297QgsProcessingExtentParameterDefinitionWidget::QgsProcessingExtentParameterDefinitionWidget(
6298 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
6299)
6300 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
6301{
6302 QVBoxLayout *vlayout = new QVBoxLayout();
6303 vlayout->setContentsMargins( 0, 0, 0, 0 );
6304
6305 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
6306
6307 mDefaultWidget = new QgsExtentWidget();
6308 mDefaultWidget->setNullValueAllowed( true, tr( "Not set" ) );
6309 if ( const QgsProcessingParameterExtent *extentParam = dynamic_cast<const QgsProcessingParameterExtent *>( definition ) )
6310 {
6311 if ( extentParam->defaultValueForGui().isValid() )
6312 {
6313 QgsRectangle rect = QgsProcessingParameters::parameterAsExtent( extentParam, extentParam->defaultValueForGui(), context );
6314 QgsCoordinateReferenceSystem crs = QgsProcessingParameters::parameterAsExtentCrs( extentParam, extentParam->defaultValueForGui(), context );
6315 mDefaultWidget->setCurrentExtent( rect, crs );
6316 mDefaultWidget->setOutputExtentFromCurrent();
6317 }
6318 else
6319 {
6320 mDefaultWidget->clear();
6321 }
6322 }
6323
6325
6326 vlayout->addWidget( mDefaultWidget );
6327 setLayout( vlayout );
6328}
6329
6330QgsProcessingParameterDefinition *QgsProcessingExtentParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
6331{
6332 const QString defaultVal = mDefaultWidget->isValid() ? u"%1,%2,%3,%4%5"_s.arg(
6333 QString::number( mDefaultWidget->outputExtent().xMinimum(), 'f', 9 ),
6334 QString::number( mDefaultWidget->outputExtent().xMaximum(), 'f', 9 ),
6335 QString::number( mDefaultWidget->outputExtent().yMinimum(), 'f', 9 ),
6336 QString::number( mDefaultWidget->outputExtent().yMaximum(), 'f', 9 ),
6337 mDefaultWidget->outputCrs().isValid() ? u" [%1]"_s.arg( mDefaultWidget->outputCrs().authid() ) : QString()
6338 )
6339 : QString();
6340 auto param = std::make_unique<QgsProcessingParameterExtent>( name, description, !defaultVal.isEmpty() ? QVariant( defaultVal ) : QVariant() );
6341 param->setFlags( flags );
6342 return param.release();
6343}
6344
6345
6346QgsProcessingExtentWidgetWrapper::QgsProcessingExtentWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6347 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
6348{}
6349
6350QWidget *QgsProcessingExtentWidgetWrapper::createWidget()
6351{
6352 const QgsProcessingParameterExtent *extentParam = dynamic_cast<const QgsProcessingParameterExtent *>( parameterDefinition() );
6353 if ( !extentParam )
6354 return nullptr;
6355
6356 switch ( type() )
6357 {
6361 {
6362 mExtentWidget = new QgsExtentWidget( nullptr );
6363 if ( widgetContext().mapCanvas() )
6364 mExtentWidget->setMapCanvas( widgetContext().mapCanvas() );
6365
6366 if ( extentParam->flags() & Qgis::ProcessingParameterFlag::Optional )
6367 mExtentWidget->setNullValueAllowed( true, tr( "Not set" ) );
6368
6369 mExtentWidget->setToolTip( parameterDefinition()->toolTip() );
6370
6371 connect( mExtentWidget, &QgsExtentWidget::extentChanged, this, [this] { emit widgetValueHasChanged( this ); } );
6372
6373 if ( mDialog && type() != Qgis::ProcessingMode::Modeler )
6374 setDialog( mDialog ); // setup connections to panel - dialog was previously set before the widget was created
6375
6376 return mExtentWidget;
6377 }
6378 }
6379 return nullptr;
6380}
6381
6382void QgsProcessingExtentWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
6383{
6385 if ( mExtentWidget && context.mapCanvas() && type() != Qgis::ProcessingMode::Modeler )
6386 mExtentWidget->setMapCanvas( context.mapCanvas() );
6387}
6388
6389void QgsProcessingExtentWidgetWrapper::setDialog( QWidget *dialog )
6390{
6391 mDialog = dialog;
6392 if ( mExtentWidget && mDialog && type() != Qgis::ProcessingMode::Modeler )
6393 {
6394 connect( mExtentWidget, &QgsExtentWidget::toggleDialogVisibility, mDialog, [this]( bool visible ) {
6395 if ( !visible )
6396 mDialog->showMinimized();
6397 else
6398 {
6399 mDialog->showNormal();
6400 mDialog->raise();
6401 mDialog->activateWindow();
6402 }
6403 } );
6404 }
6406}
6407
6408void QgsProcessingExtentWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
6409{
6410 if ( mExtentWidget )
6411 {
6412 if ( !value.isValid() || ( value.userType() == QMetaType::Type::QString && value.toString().isEmpty() ) )
6413 mExtentWidget->clear();
6414 else
6415 {
6416 QgsRectangle r = QgsProcessingParameters::parameterAsExtent( parameterDefinition(), value, context );
6417 QgsCoordinateReferenceSystem crs = QgsProcessingParameters::parameterAsPointCrs( parameterDefinition(), value, context );
6418 mExtentWidget->setCurrentExtent( r, crs );
6419 mExtentWidget->setOutputExtentFromUser( r, crs );
6420 }
6421 }
6422}
6423
6424QVariant QgsProcessingExtentWidgetWrapper::widgetValue() const
6425{
6426 if ( mExtentWidget )
6427 {
6428 const QString val = mExtentWidget->isValid() ? u"%1,%2,%3,%4%5"_s.arg(
6429 QString::number( mExtentWidget->outputExtent().xMinimum(), 'f', 9 ),
6430 QString::number( mExtentWidget->outputExtent().xMaximum(), 'f', 9 ),
6431 QString::number( mExtentWidget->outputExtent().yMinimum(), 'f', 9 ),
6432 QString::number( mExtentWidget->outputExtent().yMaximum(), 'f', 9 ),
6433 mExtentWidget->outputCrs().isValid() ? u" [%1]"_s.arg( mExtentWidget->outputCrs().authid() ) : QString()
6434 )
6435 : QString();
6436
6437 return val.isEmpty() ? QVariant() : QVariant( val );
6438 }
6439 else
6440 return QVariant();
6441}
6442
6443QString QgsProcessingExtentWidgetWrapper::modelerExpressionFormatString() const
6444{
6445 return tr( "string of the format 'x min,x max,y min,y max' or a geometry value (bounding box is used)" );
6446}
6447
6448QString QgsProcessingExtentWidgetWrapper::parameterType() const
6449{
6451}
6452
6453QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingExtentWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6454{
6455 return new QgsProcessingExtentWidgetWrapper( parameter, type );
6456}
6457
6458QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingExtentWidgetWrapper::createParameterDefinitionWidget(
6460)
6461{
6462 return new QgsProcessingExtentParameterDefinitionWidget( context, widgetContext, definition, algorithm );
6463}
6464
6465
6466//
6467// QgsProcessingMapLayerWidgetWrapper
6468//
6469
6470QgsProcessingMapLayerParameterDefinitionWidget::QgsProcessingMapLayerParameterDefinitionWidget(
6471 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
6472)
6473 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
6474{
6475 QVBoxLayout *vlayout = new QVBoxLayout();
6476 vlayout->setContentsMargins( 0, 0, 0, 0 );
6477
6478 vlayout->addWidget( new QLabel( tr( "Layer type" ) ) );
6479 mLayerTypeComboBox = new QgsCheckableComboBox();
6480 mLayerTypeComboBox->addItem( tr( "Any Map Layer" ), static_cast<int>( Qgis::ProcessingSourceType::MapLayer ) );
6481 mLayerTypeComboBox->addItem( tr( "Vector (Point)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) );
6482 mLayerTypeComboBox->addItem( tr( "Vector (Line)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) );
6483 mLayerTypeComboBox->addItem( tr( "Vector (Polygon)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) );
6484 mLayerTypeComboBox->addItem( tr( "Vector (Any Geometry Type)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) );
6485 mLayerTypeComboBox->addItem( tr( "Vector (No Geometry Required)" ), static_cast<int>( Qgis::ProcessingSourceType::Vector ) );
6486 mLayerTypeComboBox->addItem( tr( "Raster" ), static_cast<int>( Qgis::ProcessingSourceType::Raster ) );
6487 mLayerTypeComboBox->addItem( tr( "Mesh" ), static_cast<int>( Qgis::ProcessingSourceType::Mesh ) );
6488 mLayerTypeComboBox->addItem( tr( "Plugin" ), static_cast<int>( Qgis::ProcessingSourceType::Plugin ) );
6489 mLayerTypeComboBox->addItem( tr( "Point Cloud" ), static_cast<int>( Qgis::ProcessingSourceType::PointCloud ) );
6490 mLayerTypeComboBox->addItem( tr( "Annotation" ), static_cast<int>( Qgis::ProcessingSourceType::Annotation ) );
6491
6492 if ( const QgsProcessingParameterMapLayer *layerParam = dynamic_cast<const QgsProcessingParameterMapLayer *>( definition ) )
6493 {
6494 for ( int i : layerParam->dataTypes() )
6495 {
6496 mLayerTypeComboBox->setItemCheckState( mLayerTypeComboBox->findData( i ), Qt::Checked );
6497 }
6498 }
6499
6500 vlayout->addWidget( mLayerTypeComboBox );
6501
6503
6504 setLayout( vlayout );
6505}
6506
6507QgsProcessingParameterDefinition *QgsProcessingMapLayerParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
6508{
6509 QList<int> dataTypes;
6510 for ( const QVariant &v : mLayerTypeComboBox->checkedItemsData() )
6511 dataTypes << v.toInt();
6512
6513 auto param = std::make_unique<QgsProcessingParameterMapLayer>( name, description );
6514 param->setDataTypes( dataTypes );
6515 param->setFlags( flags );
6516 return param.release();
6517}
6518
6519QgsProcessingMapLayerWidgetWrapper::QgsProcessingMapLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6520 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
6521{}
6522
6523QWidget *QgsProcessingMapLayerWidgetWrapper::createWidget()
6524{
6525 mComboBox = new QgsProcessingMapLayerComboBox( parameterDefinition(), type() );
6526
6527 switch ( type() )
6528 {
6531 break;
6533 mComboBox->setEditable( true );
6534 break;
6535 }
6536
6537 mComboBox->setToolTip( parameterDefinition()->toolTip() );
6538
6539 connect( mComboBox, &QgsProcessingMapLayerComboBox::valueChanged, this, [this]() {
6540 if ( mBlockSignals )
6541 return;
6542
6543 emit widgetValueHasChanged( this );
6544 } );
6545
6546 setWidgetContext( widgetContext() );
6547 return mComboBox;
6548}
6549
6550void QgsProcessingMapLayerWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
6551{
6553 if ( mComboBox )
6554 {
6555 mComboBox->setWidgetContext( context );
6556
6557 if ( !( parameterDefinition()->flags() & Qgis::ProcessingParameterFlag::Optional ) )
6558 {
6559 // non optional parameter -- if no default value set, default to active layer
6560 if ( !parameterDefinition()->defaultValueForGui().isValid() )
6561 mComboBox->setLayer( context.activeLayer() );
6562 }
6563 }
6564}
6565
6566void QgsProcessingMapLayerWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
6567{
6568 if ( mComboBox )
6569 mComboBox->setValue( value, context );
6570}
6571
6572QVariant QgsProcessingMapLayerWidgetWrapper::widgetValue() const
6573{
6574 return mComboBox ? mComboBox->value() : QVariant();
6575}
6576
6577QString QgsProcessingMapLayerWidgetWrapper::modelerExpressionFormatString() const
6578{
6579 return tr( "path to a map layer" );
6580}
6581
6582Qgis::ProcessingModelChildParameterSource QgsProcessingMapLayerWidgetWrapper::defaultModelSource( const QgsProcessingParameterDefinition *parameter ) const
6583{
6584 // non-optional layer sources default to a matching model input layer, but optional layer parameters
6585 // should default to static values. We don't want all optional layer parameters to have values set by default!
6586 if ( !( parameter->flags() & Qgis::ProcessingParameterFlag::Optional ) )
6587 {
6589 }
6590 else
6591 {
6593 }
6594}
6595
6596QString QgsProcessingMapLayerWidgetWrapper::parameterType() const
6597{
6599}
6600
6601QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingMapLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6602{
6603 return new QgsProcessingMapLayerWidgetWrapper( parameter, type );
6604}
6605
6606QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingMapLayerWidgetWrapper::createParameterDefinitionWidget(
6608)
6609{
6610 return new QgsProcessingMapLayerParameterDefinitionWidget( context, widgetContext, definition, algorithm );
6611}
6612
6613
6614//
6615// QgsProcessingRasterLayerWidgetWrapper
6616//
6617
6618QgsProcessingRasterLayerWidgetWrapper::QgsProcessingRasterLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6619 : QgsProcessingMapLayerWidgetWrapper( parameter, type, parent )
6620{}
6621
6622QString QgsProcessingRasterLayerWidgetWrapper::modelerExpressionFormatString() const
6623{
6624 return tr( "path to a raster layer" );
6625}
6626
6627QString QgsProcessingRasterLayerWidgetWrapper::parameterType() const
6628{
6630}
6631
6632QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingRasterLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6633{
6634 return new QgsProcessingRasterLayerWidgetWrapper( parameter, type );
6635}
6636
6637QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingRasterLayerWidgetWrapper::createParameterDefinitionWidget(
6639)
6640{
6641 Q_UNUSED( context );
6642 Q_UNUSED( widgetContext );
6643 Q_UNUSED( definition );
6644 Q_UNUSED( algorithm );
6645
6646 return nullptr;
6647}
6648
6649
6650//
6651// QgsProcessingVectorLayerWidgetWrapper
6652//
6653
6654QgsProcessingVectorLayerParameterDefinitionWidget::QgsProcessingVectorLayerParameterDefinitionWidget(
6655 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
6656)
6657 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
6658{
6659 QVBoxLayout *vlayout = new QVBoxLayout();
6660 vlayout->setContentsMargins( 0, 0, 0, 0 );
6661
6662 vlayout->addWidget( new QLabel( tr( "Geometry type" ) ) );
6663 mGeometryTypeComboBox = new QgsCheckableComboBox();
6664 mGeometryTypeComboBox->addItem( tr( "Geometry Not Required" ), static_cast<int>( Qgis::ProcessingSourceType::Vector ) );
6665 mGeometryTypeComboBox->addItem( tr( "Point" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) );
6666 mGeometryTypeComboBox->addItem( tr( "Line" ), static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) );
6667 mGeometryTypeComboBox->addItem( tr( "Polygon" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) );
6668 mGeometryTypeComboBox->addItem( tr( "Any Geometry Type" ), static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) );
6669
6670 if ( const QgsProcessingParameterVectorLayer *vectorParam = dynamic_cast<const QgsProcessingParameterVectorLayer *>( definition ) )
6671 {
6672 for ( int i : vectorParam->dataTypes() )
6673 {
6674 mGeometryTypeComboBox->setItemCheckState( mGeometryTypeComboBox->findData( i ), Qt::Checked );
6675 }
6676 }
6677
6678 vlayout->addWidget( mGeometryTypeComboBox );
6679
6681
6682 setLayout( vlayout );
6683}
6684
6685QgsProcessingParameterDefinition *QgsProcessingVectorLayerParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
6686{
6687 QList<int> dataTypes;
6688 for ( const QVariant &v : mGeometryTypeComboBox->checkedItemsData() )
6689 dataTypes << v.toInt();
6690
6691 auto param = std::make_unique<QgsProcessingParameterVectorLayer>( name, description, dataTypes );
6692 param->setFlags( flags );
6693 return param.release();
6694}
6695
6696
6697QgsProcessingVectorLayerWidgetWrapper::QgsProcessingVectorLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6698 : QgsProcessingMapLayerWidgetWrapper( parameter, type, parent )
6699{}
6700
6701QString QgsProcessingVectorLayerWidgetWrapper::modelerExpressionFormatString() const
6702{
6703 return tr( "path to a vector layer" );
6704}
6705
6706QString QgsProcessingVectorLayerWidgetWrapper::parameterType() const
6707{
6709}
6710
6711QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingVectorLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6712{
6713 return new QgsProcessingVectorLayerWidgetWrapper( parameter, type );
6714}
6715
6716QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingVectorLayerWidgetWrapper::createParameterDefinitionWidget(
6718)
6719{
6720 return new QgsProcessingVectorLayerParameterDefinitionWidget( context, widgetContext, definition, algorithm );
6721}
6722
6723
6724//
6725// QgsProcessingFeatureSourceLayerWidgetWrapper
6726//
6727
6728QgsProcessingFeatureSourceParameterDefinitionWidget::QgsProcessingFeatureSourceParameterDefinitionWidget(
6729 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
6730)
6731 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
6732{
6733 QVBoxLayout *vlayout = new QVBoxLayout();
6734 vlayout->setContentsMargins( 0, 0, 0, 0 );
6735
6736 vlayout->addWidget( new QLabel( tr( "Geometry type" ) ) );
6737 mGeometryTypeComboBox = new QgsCheckableComboBox();
6738 mGeometryTypeComboBox->addItem( tr( "Geometry Not Required" ), static_cast<int>( Qgis::ProcessingSourceType::Vector ) );
6739 mGeometryTypeComboBox->addItem( tr( "Point" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) );
6740 mGeometryTypeComboBox->addItem( tr( "Line" ), static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) );
6741 mGeometryTypeComboBox->addItem( tr( "Polygon" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) );
6742 mGeometryTypeComboBox->addItem( tr( "Any Geometry Type" ), static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) );
6743
6744 if ( const QgsProcessingParameterFeatureSource *sourceParam = dynamic_cast<const QgsProcessingParameterFeatureSource *>( definition ) )
6745 {
6746 for ( int i : sourceParam->dataTypes() )
6747 {
6748 mGeometryTypeComboBox->setItemCheckState( mGeometryTypeComboBox->findData( i ), Qt::Checked );
6749 }
6750 }
6751 else
6752 {
6753 mGeometryTypeComboBox->setItemCheckState( mGeometryTypeComboBox->findData( static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) ), Qt::Checked );
6754 }
6755
6756 vlayout->addWidget( mGeometryTypeComboBox );
6758
6759 setLayout( vlayout );
6760}
6761
6762QgsProcessingParameterDefinition *QgsProcessingFeatureSourceParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
6763{
6764 QList<int> dataTypes;
6765 for ( const QVariant &v : mGeometryTypeComboBox->checkedItemsData() )
6766 dataTypes << v.toInt();
6767
6768 auto param = std::make_unique<QgsProcessingParameterFeatureSource>( name, description, dataTypes );
6769 param->setFlags( flags );
6770 return param.release();
6771}
6772
6773QgsProcessingFeatureSourceWidgetWrapper::QgsProcessingFeatureSourceWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6774 : QgsProcessingMapLayerWidgetWrapper( parameter, type, parent )
6775{}
6776
6777QString QgsProcessingFeatureSourceWidgetWrapper::modelerExpressionFormatString() const
6778{
6779 return tr( "path to a vector layer" );
6780}
6781
6782QString QgsProcessingFeatureSourceWidgetWrapper::parameterType() const
6783{
6785}
6786
6787QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFeatureSourceWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6788{
6789 return new QgsProcessingFeatureSourceWidgetWrapper( parameter, type );
6790}
6791
6792QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingFeatureSourceWidgetWrapper::createParameterDefinitionWidget(
6794)
6795{
6796 return new QgsProcessingFeatureSourceParameterDefinitionWidget( context, widgetContext, definition, algorithm );
6797}
6798
6799//
6800// QgsProcessingMeshLayerWidgetWrapper
6801//
6802
6803QgsProcessingMeshLayerWidgetWrapper::QgsProcessingMeshLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
6804 : QgsProcessingMapLayerWidgetWrapper( parameter, type, parent )
6805{}
6806
6807QString QgsProcessingMeshLayerWidgetWrapper::modelerExpressionFormatString() const
6808{
6809 return tr( "path to a mesh layer" );
6810}
6811
6812QString QgsProcessingMeshLayerWidgetWrapper::parameterType() const
6813{
6815}
6816
6817QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingMeshLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
6818{
6819 return new QgsProcessingMeshLayerWidgetWrapper( parameter, type );
6820}
6821
6822QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingMeshLayerWidgetWrapper::createParameterDefinitionWidget(
6824)
6825{
6826 Q_UNUSED( context );
6827 Q_UNUSED( widgetContext );
6828 Q_UNUSED( definition );
6829 Q_UNUSED( algorithm );
6830
6831 return nullptr;
6832}
6833
6834
6835//
6836// QgsProcessingRasterBandPanelWidget
6837//
6838
6839QgsProcessingRasterBandPanelWidget::QgsProcessingRasterBandPanelWidget( QWidget *parent, const QgsProcessingParameterBand *param )
6840 : QWidget( parent )
6841 , mParam( param )
6842{
6843 QHBoxLayout *hl = new QHBoxLayout();
6844 hl->setContentsMargins( 0, 0, 0, 0 );
6845
6846 mLineEdit = new QLineEdit();
6847 mLineEdit->setEnabled( false );
6848 hl->addWidget( mLineEdit, 1 );
6849
6850 mToolButton = new QToolButton();
6851 mToolButton->setText( QString( QChar( 0x2026 ) ) );
6852 hl->addWidget( mToolButton );
6853
6854 setLayout( hl );
6855
6856 if ( mParam )
6857 {
6858 mLineEdit->setText( tr( "%n band(s) selected", nullptr, 0 ) );
6859 }
6860
6861 connect( mToolButton, &QToolButton::clicked, this, &QgsProcessingRasterBandPanelWidget::showDialog );
6862}
6863
6864void QgsProcessingRasterBandPanelWidget::setBands( const QList<int> &bands )
6865{
6866 mBands = bands;
6867}
6868
6869void QgsProcessingRasterBandPanelWidget::setBandNames( const QHash<int, QString> &names )
6870{
6871 mBandNames = names;
6872}
6873
6874void QgsProcessingRasterBandPanelWidget::setValue( const QVariant &value )
6875{
6876 if ( value.isValid() )
6877 mValue = value.userType() == QMetaType::Type::QVariantList ? value.toList() : QVariantList() << value;
6878 else
6879 mValue.clear();
6880
6881 updateSummaryText();
6882 emit changed();
6883}
6884
6885void QgsProcessingRasterBandPanelWidget::showDialog()
6886{
6887 QVariantList availableOptions;
6888 availableOptions.reserve( mBands.size() );
6889 for ( int band : std::as_const( mBands ) )
6890 {
6891 availableOptions << band;
6892 }
6893
6895 if ( panel && panel->dockMode() )
6896 {
6897 QgsProcessingMultipleSelectionPanelWidget *widget = new QgsProcessingMultipleSelectionPanelWidget( availableOptions, mValue );
6898 widget->setPanelTitle( mParam->description() );
6899
6900 widget->setValueFormatter( [this]( const QVariant &v ) -> QString {
6901 int band = v.toInt();
6902 return mBandNames.contains( band ) ? mBandNames.value( band ) : v.toString();
6903 } );
6904
6905 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [this, widget]() { setValue( widget->selectedOptions() ); } );
6906 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
6907 panel->openPanel( widget );
6908 }
6909 else
6910 {
6911 QgsProcessingMultipleSelectionDialog dlg( availableOptions, mValue, this, Qt::WindowFlags() );
6912
6913 dlg.setValueFormatter( [this]( const QVariant &v ) -> QString {
6914 int band = v.toInt();
6915 return mBandNames.contains( band ) ? mBandNames.value( band ) : v.toString();
6916 } );
6917 if ( dlg.exec() )
6918 {
6919 setValue( dlg.selectedOptions() );
6920 }
6921 }
6922}
6923
6924void QgsProcessingRasterBandPanelWidget::updateSummaryText()
6925{
6926 if ( mParam )
6927 mLineEdit->setText( tr( "%n band(s) selected", nullptr, mValue.count() ) );
6928}
6929
6930
6931//
6932// QgsProcessingBandWidgetWrapper
6933//
6934
6935QgsProcessingBandParameterDefinitionWidget::QgsProcessingBandParameterDefinitionWidget(
6936 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
6937)
6938 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
6939{
6940 QVBoxLayout *vlayout = new QVBoxLayout();
6941 vlayout->setContentsMargins( 0, 0, 0, 0 );
6942
6943 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
6944
6945 mDefaultLineEdit = new QLineEdit();
6946 mDefaultLineEdit->setToolTip( tr( "Band number (separate bands with ; for multiple band parameters)" ) );
6947 if ( const QgsProcessingParameterBand *bandParam = dynamic_cast<const QgsProcessingParameterBand *>( definition ) )
6948 {
6949 const QList<int> bands = QgsProcessingParameters::parameterAsInts( bandParam, bandParam->defaultValueForGui(), context );
6950 QStringList defVal;
6951 for ( int b : bands )
6952 {
6953 defVal << QString::number( b );
6954 }
6955
6956 mDefaultLineEdit->setText( defVal.join( ';' ) );
6957 }
6958 vlayout->addWidget( mDefaultLineEdit );
6959 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
6960
6961 vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
6962 mParentLayerComboBox = new QComboBox();
6963
6964 QString initialParent;
6965 if ( const QgsProcessingParameterBand *bandParam = dynamic_cast<const QgsProcessingParameterBand *>( definition ) )
6966 initialParent = bandParam->parentLayerParameterName();
6967
6968 if ( auto *lModel = widgetContext.model() )
6969 {
6970 // populate combo box with other model input choices
6971 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
6972 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
6973 {
6974 if ( const QgsProcessingParameterRasterLayer *definition = dynamic_cast<const QgsProcessingParameterRasterLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
6975 {
6976 mParentLayerComboBox->addItem( definition->description(), definition->name() );
6977 if ( !initialParent.isEmpty() && initialParent == definition->name() )
6978 {
6979 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
6980 }
6981 }
6982 }
6983 }
6984
6985 if ( mParentLayerComboBox->count() == 0 && !initialParent.isEmpty() )
6986 {
6987 // if no parent candidates found, we just add the existing one as a placeholder
6988 mParentLayerComboBox->addItem( initialParent, initialParent );
6989 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
6990 }
6991 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
6992
6993 vlayout->addWidget( mParentLayerComboBox );
6994
6995 mAllowMultipleCheckBox = new QCheckBox( tr( "Allow multiple" ) );
6996 if ( const QgsProcessingParameterBand *bandParam = dynamic_cast<const QgsProcessingParameterBand *>( definition ) )
6997 mAllowMultipleCheckBox->setChecked( bandParam->allowMultiple() );
6998 connect( mAllowMultipleCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
6999
7000 vlayout->addWidget( mAllowMultipleCheckBox );
7001 setLayout( vlayout );
7002}
7003
7004QgsProcessingParameterDefinition *QgsProcessingBandParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
7005{
7006 auto param
7007 = std::make_unique<QgsProcessingParameterBand>( name, description, mDefaultLineEdit->text().split( ';' ), mParentLayerComboBox->currentData().toString(), false, mAllowMultipleCheckBox->isChecked() );
7008 param->setFlags( flags );
7009 return param.release();
7010}
7011
7012QgsProcessingBandWidgetWrapper::QgsProcessingBandWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
7013 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
7014{}
7015
7016QWidget *QgsProcessingBandWidgetWrapper::createWidget()
7017{
7018 const QgsProcessingParameterBand *bandParam = dynamic_cast<const QgsProcessingParameterBand *>( parameterDefinition() );
7019 if ( !bandParam )
7020 return nullptr;
7021
7022 switch ( type() )
7023 {
7026 {
7027 if ( bandParam->allowMultiple() )
7028 {
7029 mPanel = new QgsProcessingRasterBandPanelWidget( nullptr, bandParam );
7030 mPanel->setToolTip( parameterDefinition()->toolTip() );
7031 connect( mPanel, &QgsProcessingRasterBandPanelWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
7032 return mPanel;
7033 }
7034 else
7035 {
7036 mComboBox = new QgsRasterBandComboBox();
7037 mComboBox->setShowNotSetOption( bandParam->flags() & Qgis::ProcessingParameterFlag::Optional );
7038
7039 mComboBox->setToolTip( parameterDefinition()->toolTip() );
7040 connect( mComboBox, &QgsRasterBandComboBox::bandChanged, this, [this]( int ) { emit widgetValueHasChanged( this ); } );
7041 return mComboBox;
7042 }
7043 }
7044
7046 {
7047 mLineEdit = new QLineEdit();
7048 mLineEdit->setToolTip( QObject::tr( "Band number (separate bands with ; for multiple band parameters)" ) );
7049 connect( mLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
7050 return mLineEdit;
7051 }
7052 }
7053 return nullptr;
7054}
7055
7056void QgsProcessingBandWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
7057{
7059 switch ( type() )
7060 {
7063 {
7064 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
7065 {
7066 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterBand *>( parameterDefinition() )->parentLayerParameterName() )
7067 {
7068 setParentLayerWrapperValue( wrapper );
7069 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentLayerWrapperValue( wrapper ); } );
7070 break;
7071 }
7072 }
7073 break;
7074 }
7075
7077 break;
7078 }
7079}
7080
7081void QgsProcessingBandWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
7082{
7083 // evaluate value to layer
7084 QgsProcessingContext *context = nullptr;
7085 std::unique_ptr<QgsProcessingContext> tmpContext;
7086 if ( mProcessingContextGenerator )
7087 context = mProcessingContextGenerator->processingContext();
7088
7089 if ( !context )
7090 {
7091 tmpContext = std::make_unique<QgsProcessingContext>();
7092 context = tmpContext.get();
7093 }
7094
7095 QVariant value = parentWrapper->parameterValue();
7096
7097 QgsRasterLayer *layer = QgsProcessingParameters::parameterAsRasterLayer( parentWrapper->parameterDefinition(), value, *context );
7098 if ( layer && layer->isValid() )
7099 {
7100 // need to grab ownership of layer if required - otherwise layer may be deleted when context
7101 // goes out of scope
7102 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
7103 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Raster )
7104 {
7105 mParentLayer.reset( qobject_cast<QgsRasterLayer *>( ownedLayer.release() ) );
7106 layer = mParentLayer.get();
7107 }
7108 else
7109 {
7110 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
7111 }
7112
7113 if ( mComboBox )
7114 mComboBox->setLayer( layer );
7115 else if ( mPanel )
7116 {
7117 QgsRasterDataProvider *provider = layer->dataProvider();
7118 if ( provider && layer->isValid() )
7119 {
7120 //fill available bands
7121 int nBands = provider->bandCount();
7122 QList<int> bands;
7123 QHash<int, QString> bandNames;
7124 for ( int i = 1; i <= nBands; ++i )
7125 {
7126 bandNames.insert( i, QgsRasterBandComboBox::displayBandName( provider, i ) );
7127 bands << i;
7128 }
7129 mPanel->setBands( bands );
7130 mPanel->setBandNames( bandNames );
7131 }
7132 }
7133 }
7134 else
7135 {
7136 if ( mComboBox )
7137 mComboBox->setLayer( nullptr );
7138 else if ( mPanel )
7139 mPanel->setBands( QList<int>() );
7140
7141 if ( value.isValid() && widgetContext().messageBar() )
7142 {
7143 widgetContext().messageBar()->clearWidgets();
7144 widgetContext().messageBar()->pushMessage( QString(), QObject::tr( "Could not load selected layer/table. Dependent bands could not be populated" ), Qgis::MessageLevel::Info );
7145 }
7146 }
7147
7148 if ( parameterDefinition()->defaultValueForGui().isValid() )
7149 setWidgetValue( parameterDefinition()->defaultValueForGui(), *context );
7150}
7151
7152void QgsProcessingBandWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
7153{
7154 if ( mComboBox )
7155 {
7156 if ( !value.isValid() )
7157 mComboBox->setBand( -1 );
7158 else
7159 {
7160 const int v = QgsProcessingParameters::parameterAsInt( parameterDefinition(), value, context );
7161 mComboBox->setBand( v );
7162 }
7163 }
7164 else if ( mPanel )
7165 {
7166 QVariantList opts;
7167 if ( value.isValid() )
7168 {
7169 const QList<int> v = QgsProcessingParameters::parameterAsInts( parameterDefinition(), value, context );
7170 opts.reserve( v.size() );
7171 for ( int i : v )
7172 opts << i;
7173 }
7174 if ( mPanel )
7175 mPanel->setValue( value.isValid() ? opts : QVariant() );
7176 }
7177 else if ( mLineEdit )
7178 {
7179 const QgsProcessingParameterBand *bandParam = static_cast<const QgsProcessingParameterBand *>( parameterDefinition() );
7180 if ( bandParam->allowMultiple() )
7181 {
7182 const QList<int> v = QgsProcessingParameters::parameterAsInts( parameterDefinition(), value, context );
7183 QStringList opts;
7184 opts.reserve( v.size() );
7185 for ( int i : v )
7186 opts << QString::number( i );
7187 mLineEdit->setText( value.isValid() && !opts.empty() ? opts.join( ';' ) : QString() );
7188 }
7189 else
7190 {
7191 if ( value.isValid() )
7192 mLineEdit->setText( QString::number( QgsProcessingParameters::parameterAsInt( parameterDefinition(), value, context ) ) );
7193 else
7194 mLineEdit->clear();
7195 }
7196 }
7197}
7198
7199QVariant QgsProcessingBandWidgetWrapper::widgetValue() const
7200{
7201 if ( mComboBox )
7202 return mComboBox->currentBand() == -1 ? QVariant() : mComboBox->currentBand();
7203 else if ( mPanel )
7204 return !mPanel->value().toList().isEmpty() ? mPanel->value() : QVariant();
7205 else if ( mLineEdit )
7206 {
7207 const QgsProcessingParameterBand *bandParam = static_cast<const QgsProcessingParameterBand *>( parameterDefinition() );
7208 if ( bandParam->allowMultiple() )
7209 {
7210 const QStringList parts = mLineEdit->text().split( ';', Qt::SkipEmptyParts );
7211 QVariantList res;
7212 res.reserve( parts.count() );
7213 for ( const QString &s : parts )
7214 {
7215 bool ok = false;
7216 int band = s.toInt( &ok );
7217 if ( ok )
7218 res << band;
7219 }
7220 return res.isEmpty() ? QVariant() : res;
7221 }
7222 else
7223 {
7224 return mLineEdit->text().isEmpty() ? QVariant() : mLineEdit->text();
7225 }
7226 }
7227 else
7228 return QVariant();
7229}
7230
7231QString QgsProcessingBandWidgetWrapper::modelerExpressionFormatString() const
7232{
7233 return tr( "selected band numbers as an array of numbers, or semicolon separated string of options (e.g. '1;3')" );
7234}
7235
7236QString QgsProcessingBandWidgetWrapper::parameterType() const
7237{
7239}
7240
7241QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingBandWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
7242{
7243 return new QgsProcessingBandWidgetWrapper( parameter, type );
7244}
7245
7246QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingBandWidgetWrapper::createParameterDefinitionWidget(
7248)
7249{
7250 return new QgsProcessingBandParameterDefinitionWidget( context, widgetContext, definition, algorithm );
7251}
7252
7253//
7254// QgsProcessingMultipleLayerLineEdit
7255//
7256
7257QgsProcessingMultipleLayerLineEdit::QgsProcessingMultipleLayerLineEdit( QWidget *parent, const QgsProcessingParameterMultipleLayers *param )
7258 : QgsHighlightableLineEdit( parent )
7259 , mParam( param )
7260{
7261 setAcceptDrops( true );
7262}
7263
7264void QgsProcessingMultipleLayerLineEdit::dragEnterEvent( QDragEnterEvent *event )
7265{
7266 const QStringList uris = QgsProcessingMultipleInputPanelWidget::compatibleUrisFromMimeData( mParam, event->mimeData(), {} );
7267 if ( !uris.isEmpty() )
7268 {
7269 event->setDropAction( Qt::CopyAction );
7270 event->accept();
7271 setHighlighted( true );
7272 }
7273 else
7274 {
7275 event->ignore();
7276 }
7277}
7278
7279void QgsProcessingMultipleLayerLineEdit::dragLeaveEvent( QDragLeaveEvent *event )
7280{
7281 QgsHighlightableLineEdit::dragLeaveEvent( event );
7282 event->accept();
7283 setHighlighted( false );
7284}
7285
7286void QgsProcessingMultipleLayerLineEdit::dropEvent( QDropEvent *event )
7287{
7288 const QStringList uris = QgsProcessingMultipleInputPanelWidget::compatibleUrisFromMimeData( mParam, event->mimeData(), {} );
7289 if ( !uris.isEmpty() )
7290 {
7291 event->setDropAction( Qt::CopyAction );
7292 event->accept();
7293 QVariantList uriList;
7294 uriList.reserve( uris.size() );
7295 for ( const QString &uri : uris )
7296 uriList.append( QVariant( uri ) );
7297 emit layersDropped( uriList );
7298 }
7299
7300 setHighlighted( false );
7301}
7302
7303//
7304// QgsProcessingMultipleLayerPanelWidget
7305//
7306
7307QgsProcessingMultipleLayerPanelWidget::QgsProcessingMultipleLayerPanelWidget( QWidget *parent, const QgsProcessingParameterMultipleLayers *param )
7308 : QWidget( parent )
7309 , mParam( param )
7310{
7311 QHBoxLayout *hl = new QHBoxLayout();
7312 hl->setContentsMargins( 0, 0, 0, 0 );
7313
7314 mLineEdit = new QgsProcessingMultipleLayerLineEdit( nullptr, param );
7315 mLineEdit->setEnabled( true );
7316 mLineEdit->setReadOnly( true );
7317
7318 hl->addWidget( mLineEdit, 1 );
7319 connect( mLineEdit, &QgsProcessingMultipleLayerLineEdit::layersDropped, this, &QgsProcessingMultipleLayerPanelWidget::setValue );
7320
7321 mToolButton = new QToolButton();
7322 mToolButton->setText( QString( QChar( 0x2026 ) ) );
7323 hl->addWidget( mToolButton );
7324
7325 setLayout( hl );
7326
7327 if ( mParam )
7328 {
7329 mLineEdit->setText( tr( "%n input(s) selected", nullptr, 0 ) );
7330 }
7331
7332 connect( mToolButton, &QToolButton::clicked, this, &QgsProcessingMultipleLayerPanelWidget::showDialog );
7333}
7334
7335void QgsProcessingMultipleLayerPanelWidget::setValue( const QVariant &value )
7336{
7337 if ( value.isValid() )
7338 mValue = value.userType() == QMetaType::Type::QVariantList ? value.toList() : QVariantList() << value;
7339 else
7340 mValue.clear();
7341
7342 updateSummaryText();
7343 emit changed();
7344}
7345
7346void QgsProcessingMultipleLayerPanelWidget::setProject( QgsProject *project )
7347{
7348 mProject = project;
7349 if ( mProject )
7350 {
7351 connect( mProject, &QgsProject::layerRemoved, this, [&]( const QString &layerId ) {
7352 if ( mValue.removeAll( layerId ) )
7353 {
7354 updateSummaryText();
7355 emit changed();
7356 }
7357 } );
7358 }
7359}
7360
7361void QgsProcessingMultipleLayerPanelWidget::setModel( QgsProcessingModelAlgorithm *model, const QString &modelChildAlgorithmID )
7362{
7363 mModel = model;
7364 if ( !model )
7365 return;
7366
7367 switch ( mParam->layerType() )
7368 {
7370 mModelSources = model->availableSourcesForChild( modelChildAlgorithmID, QStringList() << QgsProcessingParameterFile::typeName(), QStringList() << QgsProcessingOutputFile::typeName() );
7371 break;
7372
7374 {
7375 mModelSources = model->availableSourcesForChild(
7376 modelChildAlgorithmID,
7379 );
7380 break;
7381 }
7382
7384 {
7385 mModelSources = model->availableSourcesForChild(
7386 modelChildAlgorithmID,
7389 );
7390 break;
7391 }
7392
7394 {
7395 mModelSources = model->availableSourcesForChild(
7396 modelChildAlgorithmID,
7399 );
7400 break;
7401 }
7402
7404 {
7405 mModelSources = model->availableSourcesForChild(
7406 modelChildAlgorithmID,
7409 );
7410 break;
7411 }
7412
7414 {
7415 mModelSources = model->availableSourcesForChild(
7416 modelChildAlgorithmID,
7419 );
7420 break;
7421 }
7422
7424 {
7425 mModelSources = model->availableSourcesForChild(
7426 modelChildAlgorithmID,
7429 );
7430 break;
7431 }
7432
7434 {
7435 mModelSources = model->availableSourcesForChild(
7436 modelChildAlgorithmID,
7439 );
7440 break;
7441 }
7442
7444 {
7445 mModelSources = model->availableSourcesForChild(
7446 modelChildAlgorithmID,
7447 QStringList()
7453 QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::Vector )
7454 );
7455 break;
7456 }
7457
7459 {
7460 mModelSources = model->availableSourcesForChild(
7461 modelChildAlgorithmID,
7462 QStringList()
7468 );
7469 break;
7470 }
7471
7473 {
7474 mModelSources = model->availableSourcesForChild(
7475 modelChildAlgorithmID,
7476 QStringList()
7482 QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint )
7483 );
7484 break;
7485 }
7486
7488 {
7489 mModelSources = model->availableSourcesForChild(
7490 modelChildAlgorithmID,
7491 QStringList()
7497 QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) << static_cast<int>( Qgis::ProcessingSourceType::VectorLine )
7498 );
7499 break;
7500 }
7501
7503 {
7504 mModelSources = model->availableSourcesForChild(
7505 modelChildAlgorithmID,
7506 QStringList()
7512 QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon )
7513 );
7514 break;
7515 }
7516
7518 {
7519 mModelSources = model->availableSourcesForChild(
7520 modelChildAlgorithmID,
7521 QStringList()
7528 QStringList()
7533 // << QgsProcessingOutputMeshLayer::typeName()
7535 );
7536 break;
7537 }
7538 }
7539}
7540
7541void QgsProcessingMultipleLayerPanelWidget::showDialog()
7542{
7544 if ( panel && panel->dockMode() )
7545 {
7546 QgsProcessingMultipleInputPanelWidget *widget = new QgsProcessingMultipleInputPanelWidget( mParam, mValue, mModelSources, mModel, nullptr );
7547 widget->setPanelTitle( mParam->description() );
7548 widget->setProject( mProject );
7549 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [this, widget]() { setValue( widget->selectedOptions() ); } );
7550 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
7551 panel->openPanel( widget );
7552 }
7553 else
7554 {
7555 QgsProcessingMultipleInputDialog dlg( mParam, mValue, mModelSources, mModel, this, Qt::WindowFlags() );
7556 dlg.setProject( mProject );
7557 if ( dlg.exec() )
7558 {
7559 setValue( dlg.selectedOptions() );
7560 }
7561 }
7562}
7563
7564void QgsProcessingMultipleLayerPanelWidget::updateSummaryText()
7565{
7566 if ( mParam )
7567 mLineEdit->setText( tr( "%n input(s) selected", nullptr, mValue.count() ) );
7568}
7569
7570//
7571// QgsProcessingMultipleLayerWidgetWrapper
7572//
7573
7574QgsProcessingMultipleLayerParameterDefinitionWidget::QgsProcessingMultipleLayerParameterDefinitionWidget(
7575 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
7576)
7577 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
7578{
7579 QVBoxLayout *vlayout = new QVBoxLayout();
7580 vlayout->setContentsMargins( 0, 0, 0, 0 );
7581
7582 vlayout->addWidget( new QLabel( tr( "Allowed layer type" ) ) );
7583 mLayerTypeComboBox = new QComboBox();
7584 mLayerTypeComboBox->addItem( tr( "Any Map Layer" ), static_cast<int>( Qgis::ProcessingSourceType::MapLayer ) );
7585 mLayerTypeComboBox->addItem( tr( "Vector (No Geometry Required)" ), static_cast<int>( Qgis::ProcessingSourceType::Vector ) );
7586 mLayerTypeComboBox->addItem( tr( "Vector (Point)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) );
7587 mLayerTypeComboBox->addItem( tr( "Vector (Line)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) );
7588 mLayerTypeComboBox->addItem( tr( "Vector (Polygon)" ), static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) );
7589 mLayerTypeComboBox->addItem( tr( "Any Geometry Type" ), static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) );
7590 mLayerTypeComboBox->addItem( tr( "Raster" ), static_cast<int>( Qgis::ProcessingSourceType::Raster ) );
7591 mLayerTypeComboBox->addItem( tr( "File" ), static_cast<int>( Qgis::ProcessingSourceType::File ) );
7592 mLayerTypeComboBox->addItem( tr( "Mesh" ), static_cast<int>( Qgis::ProcessingSourceType::Mesh ) );
7593 mLayerTypeComboBox->addItem( tr( "Plugin" ), static_cast<int>( Qgis::ProcessingSourceType::Plugin ) );
7594 mLayerTypeComboBox->addItem( tr( "Point Cloud" ), static_cast<int>( Qgis::ProcessingSourceType::PointCloud ) );
7595 mLayerTypeComboBox->addItem( tr( "Annotation" ), static_cast<int>( Qgis::ProcessingSourceType::Annotation ) );
7596 if ( const QgsProcessingParameterMultipleLayers *layersParam = dynamic_cast<const QgsProcessingParameterMultipleLayers *>( definition ) )
7597 mLayerTypeComboBox->setCurrentIndex( mLayerTypeComboBox->findData( static_cast<int>( layersParam->layerType() ) ) );
7598
7599 vlayout->addWidget( mLayerTypeComboBox );
7600 connect( mLayerTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
7601
7602 setLayout( vlayout );
7603}
7604
7605QgsProcessingParameterDefinition *QgsProcessingMultipleLayerParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
7606{
7607 auto param = std::make_unique<QgsProcessingParameterMultipleLayers>( name, description, static_cast<Qgis::ProcessingSourceType>( mLayerTypeComboBox->currentData().toInt() ) );
7608 param->setFlags( flags );
7609 return param.release();
7610}
7611
7612QgsProcessingMultipleLayerWidgetWrapper::QgsProcessingMultipleLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
7613 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
7614{}
7615
7616QWidget *QgsProcessingMultipleLayerWidgetWrapper::createWidget()
7617{
7618 const QgsProcessingParameterMultipleLayers *layerParam = dynamic_cast<const QgsProcessingParameterMultipleLayers *>( parameterDefinition() );
7619
7620 mPanel = new QgsProcessingMultipleLayerPanelWidget( nullptr, layerParam );
7621 mPanel->setToolTip( parameterDefinition()->toolTip() );
7622 mPanel->setProject( widgetContext().project() );
7623 if ( type() == Qgis::ProcessingMode::Modeler )
7624 mPanel->setModel( widgetContext().model(), widgetContext().modelChildAlgorithmId() );
7625 connect( mPanel, &QgsProcessingMultipleLayerPanelWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
7626 return mPanel;
7627}
7628
7629void QgsProcessingMultipleLayerWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
7630{
7632 if ( mPanel )
7633 {
7634 mPanel->setProject( context.project() );
7635 if ( type() == Qgis::ProcessingMode::Modeler )
7636 mPanel->setModel( widgetContext().model(), widgetContext().modelChildAlgorithmId() );
7637 }
7638}
7639
7640void QgsProcessingMultipleLayerWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
7641{
7642 if ( mPanel )
7643 {
7644 QVariantList opts;
7645 if ( value.isValid() )
7646 {
7647 const QList<QgsMapLayer *> v = QgsProcessingParameters::parameterAsLayerList( parameterDefinition(), value, context );
7648 opts.reserve( v.size() );
7649 for ( const QgsMapLayer *l : v )
7650 opts << l->source();
7651 }
7652
7653 for ( const QVariant &v : value.toList() )
7654 {
7655 if ( v.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
7656 {
7657 const QgsProcessingModelChildParameterSource source = v.value<QgsProcessingModelChildParameterSource>();
7658 opts << QVariant::fromValue( source );
7659 }
7660 }
7661
7662 if ( mPanel )
7663 mPanel->setValue( value.isValid() ? opts : QVariant() );
7664 }
7665}
7666
7667QVariant QgsProcessingMultipleLayerWidgetWrapper::widgetValue() const
7668{
7669 if ( mPanel )
7670 return !mPanel->value().toList().isEmpty() ? mPanel->value() : QVariant();
7671 else
7672 return QVariant();
7673}
7674
7675QString QgsProcessingMultipleLayerWidgetWrapper::modelerExpressionFormatString() const
7676{
7677 return tr( "an array of layer paths, or semicolon separated string of layer paths" );
7678}
7679
7680QString QgsProcessingMultipleLayerWidgetWrapper::parameterType() const
7681{
7683}
7684
7685QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingMultipleLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
7686{
7687 return new QgsProcessingMultipleLayerWidgetWrapper( parameter, type );
7688}
7689
7690QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingMultipleLayerWidgetWrapper::createParameterDefinitionWidget(
7692)
7693{
7694 return new QgsProcessingMultipleLayerParameterDefinitionWidget( context, widgetContext, definition, algorithm );
7695}
7696
7697
7698//
7699// QgsProcessingPointCloudLayerWidgetWrapper
7700//
7701
7702QgsProcessingPointCloudLayerWidgetWrapper::QgsProcessingPointCloudLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
7703 : QgsProcessingMapLayerWidgetWrapper( parameter, type, parent )
7704{}
7705
7706QString QgsProcessingPointCloudLayerWidgetWrapper::modelerExpressionFormatString() const
7707{
7708 return tr( "path to a point cloud layer" );
7709}
7710
7711QString QgsProcessingPointCloudLayerWidgetWrapper::parameterType() const
7712{
7714}
7715
7716QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingPointCloudLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
7717{
7718 return new QgsProcessingPointCloudLayerWidgetWrapper( parameter, type );
7719}
7720
7721QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingPointCloudLayerWidgetWrapper::createParameterDefinitionWidget(
7723)
7724{
7725 Q_UNUSED( context );
7726 Q_UNUSED( widgetContext );
7727 Q_UNUSED( definition );
7728 Q_UNUSED( algorithm );
7729
7730 return nullptr;
7731}
7732
7733
7734//
7735// QgsProcessingAnnotationLayerWidgetWrapper
7736//
7737
7738QgsProcessingAnnotationLayerWidgetWrapper::QgsProcessingAnnotationLayerWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
7739 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
7740{}
7741
7742QString QgsProcessingAnnotationLayerWidgetWrapper::modelerExpressionFormatString() const
7743{
7744 return tr( "name of an annotation layer, or \"main\" for the main annotation layer" );
7745}
7746
7747QString QgsProcessingAnnotationLayerWidgetWrapper::parameterType() const
7748{
7750}
7751
7752QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingAnnotationLayerWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
7753{
7754 return new QgsProcessingAnnotationLayerWidgetWrapper( parameter, type );
7755}
7756
7757QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingAnnotationLayerWidgetWrapper::createParameterDefinitionWidget(
7759)
7760{
7761 Q_UNUSED( context );
7762 Q_UNUSED( widgetContext );
7763 Q_UNUSED( definition );
7764 Q_UNUSED( algorithm );
7765
7766 return nullptr;
7767}
7768
7769void QgsProcessingAnnotationLayerWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
7770{
7772 if ( mComboBox )
7773 {
7774 if ( mWidgetContext.project() )
7775 mComboBox->setAdditionalLayers( { mWidgetContext.project()->mainAnnotationLayer() } );
7776 }
7777}
7778
7779QWidget *QgsProcessingAnnotationLayerWidgetWrapper::createWidget()
7780{
7781 mComboBox = new QgsMapLayerComboBox();
7782 mComboBox->setFilters( Qgis::LayerFilter::AnnotationLayer );
7783
7784 switch ( type() )
7785 {
7788 break;
7790 mComboBox->setEditable( true );
7791 break;
7792 }
7793
7794 mComboBox->setToolTip( parameterDefinition()->toolTip() );
7795
7796 if ( mWidgetContext.project() )
7797 mComboBox->setAdditionalLayers( { mWidgetContext.project()->mainAnnotationLayer() } );
7798
7799 if ( parameterDefinition()->flags() & Qgis::ProcessingParameterFlag::Optional )
7800 mComboBox->setAllowEmptyLayer( true );
7801
7802 connect( mComboBox, &QgsMapLayerComboBox::layerChanged, this, [this]() {
7803 if ( mBlockSignals )
7804 return;
7805
7806 emit widgetValueHasChanged( this );
7807 } );
7808
7809 setWidgetContext( widgetContext() );
7810 return mComboBox;
7811}
7812
7813void QgsProcessingAnnotationLayerWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
7814{
7815 if ( mComboBox )
7816 {
7817 if ( !value.isValid() && parameterDefinition()->flags() & Qgis::ProcessingParameterFlag::Optional )
7818 {
7819 mComboBox->setLayer( nullptr );
7820 return;
7821 }
7822
7823 QVariant val = value;
7824 if ( val.userType() == qMetaTypeId<QgsProperty>() )
7825 {
7826 if ( val.value<QgsProperty>().propertyType() == Qgis::PropertyType::Static )
7827 {
7828 val = val.value<QgsProperty>().staticValue();
7829 }
7830 else
7831 {
7832 val = val.value<QgsProperty>().valueAsString( context.expressionContext(), parameterDefinition()->defaultValueForGui().toString() );
7833 }
7834 }
7835
7836 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( val.value<QObject *>() );
7837 if ( !layer && val.userType() == QMetaType::Type::QString )
7838 {
7840 }
7841
7842 if ( layer )
7843 {
7844 mComboBox->setLayer( layer );
7845 }
7846 }
7847}
7848
7849QVariant QgsProcessingAnnotationLayerWidgetWrapper::widgetValue() const
7850{
7851 return mComboBox && mComboBox->currentLayer()
7852 ? ( mWidgetContext.project() ? ( mComboBox->currentLayer() == mWidgetContext.project()->mainAnnotationLayer() ? u"main"_s : mComboBox->currentLayer()->id() )
7853 : mComboBox->currentLayer()->id() )
7854 : QVariant();
7855}
7856
7857
7858//
7859// QgsProcessingPointCloudAttributePanelWidget
7860//
7861
7862QgsProcessingPointCloudAttributePanelWidget::QgsProcessingPointCloudAttributePanelWidget( QWidget *parent, const QgsProcessingParameterPointCloudAttribute *param )
7863 : QWidget( parent )
7864 , mParam( param )
7865{
7866 QHBoxLayout *hl = new QHBoxLayout();
7867 hl->setContentsMargins( 0, 0, 0, 0 );
7868
7869 mLineEdit = new QLineEdit();
7870 mLineEdit->setEnabled( false );
7871 hl->addWidget( mLineEdit, 1 );
7872
7873 mToolButton = new QToolButton();
7874 mToolButton->setText( QString( QChar( 0x2026 ) ) );
7875 hl->addWidget( mToolButton );
7876
7877 setLayout( hl );
7878
7879 if ( mParam )
7880 {
7881 mLineEdit->setText( tr( "%n attribute(s) selected", nullptr, 0 ) );
7882 }
7883
7884 connect( mToolButton, &QToolButton::clicked, this, &QgsProcessingPointCloudAttributePanelWidget::showDialog );
7885}
7886
7887void QgsProcessingPointCloudAttributePanelWidget::setAttributes( const QgsPointCloudAttributeCollection &attributes )
7888{
7889 mAttributes = attributes;
7890}
7891
7892void QgsProcessingPointCloudAttributePanelWidget::setValue( const QVariant &value )
7893{
7894 if ( value.isValid() )
7895 mValue = value.userType() == QMetaType::Type::QVariantList ? value.toList() : QVariantList() << value;
7896 else
7897 mValue.clear();
7898
7899 updateSummaryText();
7900 emit changed();
7901}
7902
7903void QgsProcessingPointCloudAttributePanelWidget::showDialog()
7904{
7905 QVariantList availableOptions;
7906 availableOptions.reserve( mAttributes.count() );
7907 const QVector<QgsPointCloudAttribute> attributes = mAttributes.attributes();
7908 for ( const QgsPointCloudAttribute &attr : attributes )
7909 {
7910 availableOptions << attr.name();
7911 }
7912
7914 if ( panel && panel->dockMode() )
7915 {
7916 QgsProcessingMultipleSelectionPanelWidget *widget = new QgsProcessingMultipleSelectionPanelWidget( availableOptions, mValue );
7917 widget->setPanelTitle( mParam->description() );
7918
7919 widget->setValueFormatter( []( const QVariant &v ) -> QString { return v.toString(); } );
7920
7921 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [this, widget]() { setValue( widget->selectedOptions() ); } );
7922 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
7923 panel->openPanel( widget );
7924 }
7925 else
7926 {
7927 QgsProcessingMultipleSelectionDialog dlg( availableOptions, mValue, this, Qt::WindowFlags() );
7928
7929 dlg.setValueFormatter( []( const QVariant &v ) -> QString { return v.toString(); } );
7930 if ( dlg.exec() )
7931 {
7932 setValue( dlg.selectedOptions() );
7933 }
7934 }
7935}
7936
7937void QgsProcessingPointCloudAttributePanelWidget::updateSummaryText()
7938{
7939 if ( !mParam )
7940 return;
7941
7942 if ( mValue.empty() )
7943 {
7944 mLineEdit->setText( tr( "%n attribute(s) selected", nullptr, 0 ) );
7945 }
7946 else
7947 {
7948 QStringList values;
7949 values.reserve( mValue.size() );
7950 for ( const QVariant &val : std::as_const( mValue ) )
7951 {
7952 values << val.toString();
7953 }
7954
7955 const QString concatenated = values.join( tr( "," ) );
7956 if ( concatenated.length() < 100 )
7957 mLineEdit->setText( concatenated );
7958 else
7959 mLineEdit->setText( tr( "%n attribute(s) selected", nullptr, mValue.count() ) );
7960 }
7961}
7962
7963
7964//
7965// QgsProcessingPointCloudAttributeWidgetWrapper
7966//
7967
7968QgsProcessingPointCloudAttributeParameterDefinitionWidget::QgsProcessingPointCloudAttributeParameterDefinitionWidget(
7969 QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent
7970)
7971 : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
7972{
7973 QVBoxLayout *vlayout = new QVBoxLayout();
7974 vlayout->setContentsMargins( 0, 0, 0, 0 );
7975
7976 vlayout->addWidget( new QLabel( tr( "Parent layer" ) ) );
7977 mParentLayerComboBox = new QComboBox();
7978
7979 QString initialParent;
7980 if ( const QgsProcessingParameterPointCloudAttribute *attrParam = dynamic_cast<const QgsProcessingParameterPointCloudAttribute *>( definition ) )
7981 initialParent = attrParam->parentLayerParameterName();
7982
7983 if ( auto *lModel = widgetContext.model() )
7984 {
7985 // populate combo box with other model input choices
7986 const QMap<QString, QgsProcessingModelParameter> components = lModel->parameterComponents();
7987 for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
7988 {
7989 if ( const QgsProcessingParameterPointCloudLayer *definition = dynamic_cast<const QgsProcessingParameterPointCloudLayer *>( lModel->parameterDefinition( it.value().parameterName() ) ) )
7990 {
7991 mParentLayerComboBox->addItem( definition->description(), definition->name() );
7992 if ( !initialParent.isEmpty() && initialParent == definition->name() )
7993 {
7994 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
7995 }
7996 }
7997 }
7998 }
7999
8000 if ( mParentLayerComboBox->count() == 0 && !initialParent.isEmpty() )
8001 {
8002 // if no parent candidates found, we just add the existing one as a placeholder
8003 mParentLayerComboBox->addItem( initialParent, initialParent );
8004 mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
8005 }
8006
8007 vlayout->addWidget( mParentLayerComboBox );
8008
8009 connect( mParentLayerComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
8010
8011 mAllowMultipleCheckBox = new QCheckBox( tr( "Accept multiple attributes" ) );
8012 if ( const QgsProcessingParameterPointCloudAttribute *attrParam = dynamic_cast<const QgsProcessingParameterPointCloudAttribute *>( definition ) )
8013 mAllowMultipleCheckBox->setChecked( attrParam->allowMultiple() );
8014
8015 vlayout->addWidget( mAllowMultipleCheckBox );
8016 connect( mAllowMultipleCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
8017
8018 mDefaultToAllCheckBox = new QCheckBox( tr( "Select all attributes by default" ) );
8019 mDefaultToAllCheckBox->setEnabled( mAllowMultipleCheckBox->isChecked() );
8020 if ( const QgsProcessingParameterPointCloudAttribute *attrParam = dynamic_cast<const QgsProcessingParameterPointCloudAttribute *>( definition ) )
8021 mDefaultToAllCheckBox->setChecked( attrParam->defaultToAllAttributes() );
8022
8023 connect( mDefaultToAllCheckBox, &QCheckBox::toggled, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
8024
8025 vlayout->addWidget( mDefaultToAllCheckBox );
8026
8027 connect( mAllowMultipleCheckBox, &QCheckBox::stateChanged, this, [this] { mDefaultToAllCheckBox->setEnabled( mAllowMultipleCheckBox->isChecked() ); } );
8028
8029 vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
8030
8031 mDefaultLineEdit = new QLineEdit();
8032 mDefaultLineEdit->setToolTip( tr( "Default attribute name, or ; separated list of attribute names for multiple attribute parameters" ) );
8033 if ( const QgsProcessingParameterPointCloudAttribute *attrParam = dynamic_cast<const QgsProcessingParameterPointCloudAttribute *>( definition ) )
8034 {
8035 const QStringList attributes = QgsProcessingParameters::parameterAsStrings( attrParam, attrParam->defaultValueForGui(), context );
8036 mDefaultLineEdit->setText( attributes.join( ';' ) );
8037 }
8038 vlayout->addWidget( mDefaultLineEdit );
8039
8040 connect( mDefaultLineEdit, &QLineEdit::textEdited, this, &QgsProcessingAbstractParameterDefinitionWidget::changed );
8041
8042 setLayout( vlayout );
8043}
8044
8045QgsProcessingParameterDefinition *QgsProcessingPointCloudAttributeParameterDefinitionWidget::createParameter( const QString &name, const QString &description, Qgis::ProcessingParameterFlags flags ) const
8046{
8047 QVariant defaultValue;
8048 if ( !mDefaultLineEdit->text().trimmed().isEmpty() )
8049 {
8050 defaultValue = mDefaultLineEdit->text();
8051 }
8052 auto param = std::make_unique<
8053 QgsProcessingParameterPointCloudAttribute>( name, description, defaultValue, mParentLayerComboBox->currentData().toString(), mAllowMultipleCheckBox->isChecked(), false, mDefaultToAllCheckBox->isChecked() );
8054 param->setFlags( flags );
8055 return param.release();
8056}
8057
8058QgsProcessingPointCloudAttributeWidgetWrapper::QgsProcessingPointCloudAttributeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8059 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
8060{}
8061
8062QWidget *QgsProcessingPointCloudAttributeWidgetWrapper::createWidget()
8063{
8064 const QgsProcessingParameterPointCloudAttribute *attrParam = dynamic_cast<const QgsProcessingParameterPointCloudAttribute *>( parameterDefinition() );
8065 if ( !attrParam )
8066 return nullptr;
8067
8068 switch ( type() )
8069 {
8072 {
8073 if ( attrParam->allowMultiple() )
8074 {
8075 mPanel = new QgsProcessingPointCloudAttributePanelWidget( nullptr, attrParam );
8076 mPanel->setToolTip( parameterDefinition()->toolTip() );
8077 connect( mPanel, &QgsProcessingPointCloudAttributePanelWidget::changed, this, [this] { emit widgetValueHasChanged( this ); } );
8078 return mPanel;
8079 }
8080 else
8081 {
8082 mComboBox = new QgsPointCloudAttributeComboBox();
8083 mComboBox->setAllowEmptyAttributeName( attrParam->flags() & Qgis::ProcessingParameterFlag::Optional );
8084 mComboBox->setToolTip( parameterDefinition()->toolTip() );
8085 connect( mComboBox, &QgsPointCloudAttributeComboBox::attributeChanged, this, [this]( const QString & ) { emit widgetValueHasChanged( this ); } );
8086 return mComboBox;
8087 }
8088 }
8089
8091 {
8092 mLineEdit = new QLineEdit();
8093 mLineEdit->setToolTip( QObject::tr( "Name of attribute (separate attribute names with ; for multiple attribute parameters)" ) );
8094 connect( mLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
8095 return mLineEdit;
8096 }
8097 }
8098 return nullptr;
8099}
8100
8101void QgsProcessingPointCloudAttributeWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
8102{
8104 switch ( type() )
8105 {
8108 {
8109 for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
8110 {
8111 if ( wrapper->parameterDefinition()->name() == static_cast<const QgsProcessingParameterPointCloudAttribute *>( parameterDefinition() )->parentLayerParameterName() )
8112 {
8113 setParentLayerWrapperValue( wrapper );
8114 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentLayerWrapperValue( wrapper ); } );
8115 break;
8116 }
8117 }
8118 break;
8119 }
8120
8122 break;
8123 }
8124}
8125
8126void QgsProcessingPointCloudAttributeWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
8127{
8128 // evaluate value to layer
8129 QgsProcessingContext *context = nullptr;
8130 std::unique_ptr<QgsProcessingContext> tmpContext;
8131 if ( mProcessingContextGenerator )
8132 context = mProcessingContextGenerator->processingContext();
8133
8134 if ( !context )
8135 {
8136 tmpContext = std::make_unique<QgsProcessingContext>();
8137 context = tmpContext.get();
8138 }
8139
8140 QVariant value = parentWrapper->parameterValue();
8141
8143 if ( layer && layer->isValid() )
8144 {
8145 // need to grab ownership of layer if required - otherwise layer may be deleted when context
8146 // goes out of scope
8147 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
8148 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::PointCloud )
8149 {
8150 mParentLayer.reset( qobject_cast<QgsPointCloudLayer *>( ownedLayer.release() ) );
8151 layer = mParentLayer.get();
8152 }
8153 else
8154 {
8155 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
8156 }
8157
8158 if ( mComboBox )
8159 mComboBox->setLayer( layer );
8160 else if ( mPanel )
8161 {
8162 mPanel->setAttributes( layer->attributes() );
8163 }
8164 }
8165 else
8166 {
8167 if ( mComboBox )
8168 {
8169 mComboBox->setLayer( nullptr );
8170 }
8171 else if ( mPanel )
8172 mPanel->setAttributes( QgsPointCloudAttributeCollection() );
8173
8174 if ( value.isValid() && widgetContext().messageBar() )
8175 {
8176 widgetContext().messageBar()->clearWidgets();
8177 widgetContext().messageBar()->pushMessage( QString(), QObject::tr( "Could not load selected layer/table. Dependent attributes could not be populated" ), Qgis::MessageLevel::Info );
8178 }
8179 }
8180
8181 const QgsProcessingParameterPointCloudAttribute *attrParam = static_cast<const QgsProcessingParameterPointCloudAttribute *>( parameterDefinition() );
8182 if ( mPanel && attrParam->defaultToAllAttributes() )
8183 {
8184 QVariantList val;
8185 val.reserve( mPanel->attributes().attributes().size() );
8186 for ( const QgsPointCloudAttribute &attr : mPanel->attributes().attributes() )
8187 val << attr.name();
8188 setWidgetValue( val, *context );
8189 }
8190 else if ( attrParam->defaultValueForGui().isValid() )
8191 setWidgetValue( parameterDefinition()->defaultValueForGui(), *context );
8192}
8193
8194void QgsProcessingPointCloudAttributeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
8195{
8196 if ( mComboBox )
8197 {
8198 if ( !value.isValid() )
8199 mComboBox->setAttribute( QString() );
8200 else
8201 {
8202 const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
8203 mComboBox->setAttribute( v );
8204 }
8205 }
8206 else if ( mPanel )
8207 {
8208 QVariantList opts;
8209 if ( value.isValid() )
8210 {
8211 const QStringList v = QgsProcessingParameters::parameterAsStrings( parameterDefinition(), value, context );
8212 opts.reserve( v.size() );
8213 for ( const QString &i : v )
8214 opts << i;
8215 }
8216 if ( mPanel )
8217 mPanel->setValue( opts );
8218 }
8219 else if ( mLineEdit )
8220 {
8221 const QgsProcessingParameterPointCloudAttribute *attrParam = static_cast<const QgsProcessingParameterPointCloudAttribute *>( parameterDefinition() );
8222 if ( attrParam->allowMultiple() )
8223 {
8224 const QStringList v = QgsProcessingParameters::parameterAsStrings( parameterDefinition(), value, context );
8225 mLineEdit->setText( v.join( ';' ) );
8226 }
8227 else
8228 {
8229 mLineEdit->setText( QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context ) );
8230 }
8231 }
8232}
8233
8234QVariant QgsProcessingPointCloudAttributeWidgetWrapper::widgetValue() const
8235{
8236 if ( mComboBox )
8237 return mComboBox->currentAttribute();
8238 else if ( mPanel )
8239 return mPanel->value();
8240 else if ( mLineEdit )
8241 {
8242 const QgsProcessingParameterPointCloudAttribute *attrParam = static_cast<const QgsProcessingParameterPointCloudAttribute *>( parameterDefinition() );
8243 if ( attrParam->allowMultiple() )
8244 {
8245 return mLineEdit->text().split( ';' );
8246 }
8247 else
8248 return mLineEdit->text();
8249 }
8250 else
8251 return QVariant();
8252}
8253
8254QString QgsProcessingPointCloudAttributeWidgetWrapper::modelerExpressionFormatString() const
8255{
8256 return tr( "selected attribute names as an array of names, or semicolon separated string of options (e.g. 'X;Intensity')" );
8257}
8258
8259QString QgsProcessingPointCloudAttributeWidgetWrapper::parameterType() const
8260{
8262}
8263
8264QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingPointCloudAttributeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8265{
8266 return new QgsProcessingPointCloudAttributeWidgetWrapper( parameter, type );
8267}
8268
8269QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingPointCloudAttributeWidgetWrapper::createParameterDefinitionWidget(
8271)
8272{
8273 return new QgsProcessingPointCloudAttributeParameterDefinitionWidget( context, widgetContext, definition, algorithm );
8274}
8275
8276
8277//
8278// QgsProcessingOutputWidgetWrapper
8279//
8280
8281QgsProcessingOutputWidgetWrapper::QgsProcessingOutputWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8282 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
8283{}
8284
8285QWidget *QgsProcessingOutputWidgetWrapper::createWidget()
8286{
8287 const QgsProcessingDestinationParameter *destParam = dynamic_cast<const QgsProcessingDestinationParameter *>( parameterDefinition() );
8288 switch ( type() )
8289 {
8292 {
8293 mOutputWidget = new QgsProcessingLayerOutputDestinationWidget( destParam, false );
8294 if ( mProcessingContextGenerator )
8295 mOutputWidget->setContext( mProcessingContextGenerator->processingContext() );
8296 if ( mParametersGenerator )
8297 mOutputWidget->registerProcessingParametersGenerator( mParametersGenerator );
8298 mOutputWidget->setToolTip( parameterDefinition()->toolTip() );
8299
8300 connect( mOutputWidget, &QgsProcessingLayerOutputDestinationWidget::destinationChanged, this, [this]() {
8301 if ( mBlockSignals )
8302 return;
8303
8304 emit widgetValueHasChanged( this );
8305 } );
8306
8307 if ( type() == Qgis::ProcessingMode::Standard
8309 mOutputWidget->addOpenAfterRunningOption();
8310
8311 return mOutputWidget;
8312 }
8314 break;
8315 }
8316
8317 return nullptr;
8318}
8319
8320
8321void QgsProcessingOutputWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
8322{
8323 if ( mOutputWidget )
8324 mOutputWidget->setValue( value );
8325}
8326
8327QVariant QgsProcessingOutputWidgetWrapper::widgetValue() const
8328{
8329 if ( mOutputWidget )
8330 return mOutputWidget->value();
8331
8332 return QVariant();
8333}
8334
8335QVariantMap QgsProcessingOutputWidgetWrapper::customProperties() const
8336{
8337 QVariantMap res;
8338 if ( mOutputWidget )
8339 res.insert( u"OPEN_AFTER_RUNNING"_s, mOutputWidget->openAfterRunning() );
8340 return res;
8341}
8342
8343//
8344// QgsProcessingFeatureSinkWidgetWrapper
8345//
8346
8347QgsProcessingFeatureSinkWidgetWrapper::QgsProcessingFeatureSinkWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8348 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8349{}
8350
8351QString QgsProcessingFeatureSinkWidgetWrapper::parameterType() const
8352{
8354}
8355
8356QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFeatureSinkWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8357{
8358 return new QgsProcessingFeatureSinkWidgetWrapper( parameter, type );
8359}
8360
8361QString QgsProcessingFeatureSinkWidgetWrapper::modelerExpressionFormatString() const
8362{
8363 return tr( "path to layer destination" );
8364}
8365
8366//
8367// QgsProcessingFeatureSinkWidgetWrapper
8368//
8369
8370QgsProcessingVectorDestinationWidgetWrapper::QgsProcessingVectorDestinationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8371 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8372{}
8373
8374QString QgsProcessingVectorDestinationWidgetWrapper::parameterType() const
8375{
8377}
8378
8379QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingVectorDestinationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8380{
8381 return new QgsProcessingVectorDestinationWidgetWrapper( parameter, type );
8382}
8383
8384QString QgsProcessingVectorDestinationWidgetWrapper::modelerExpressionFormatString() const
8385{
8386 return tr( "path to layer destination" );
8387}
8388
8389//
8390// QgsProcessingRasterDestinationWidgetWrapper
8391//
8392
8393QgsProcessingRasterDestinationWidgetWrapper::QgsProcessingRasterDestinationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8394 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8395{}
8396
8397QString QgsProcessingRasterDestinationWidgetWrapper::parameterType() const
8398{
8400}
8401
8402QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingRasterDestinationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8403{
8404 return new QgsProcessingRasterDestinationWidgetWrapper( parameter, type );
8405}
8406
8407QString QgsProcessingRasterDestinationWidgetWrapper::modelerExpressionFormatString() const
8408{
8409 return tr( "path to layer destination" );
8410}
8411
8412//
8413// QgsProcessingPointCloudDestinationWidgetWrapper
8414//
8415
8416QgsProcessingPointCloudDestinationWidgetWrapper::QgsProcessingPointCloudDestinationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8417 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8418{}
8419
8420QString QgsProcessingPointCloudDestinationWidgetWrapper::parameterType() const
8421{
8423}
8424
8425QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingPointCloudDestinationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8426{
8427 return new QgsProcessingPointCloudDestinationWidgetWrapper( parameter, type );
8428}
8429
8430QString QgsProcessingPointCloudDestinationWidgetWrapper::modelerExpressionFormatString() const
8431{
8432 return tr( "path to layer destination" );
8433}
8434
8435//
8436// QgsProcessingFileDestinationWidgetWrapper
8437//
8438
8439QgsProcessingFileDestinationWidgetWrapper::QgsProcessingFileDestinationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8440 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8441{}
8442
8443QString QgsProcessingFileDestinationWidgetWrapper::parameterType() const
8444{
8446}
8447
8448QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFileDestinationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8449{
8450 return new QgsProcessingFileDestinationWidgetWrapper( parameter, type );
8451}
8452
8453
8454QString QgsProcessingFileDestinationWidgetWrapper::modelerExpressionFormatString() const
8455{
8456 return tr( "path to file destination" );
8457}
8458
8459//
8460// QgsProcessingFolderDestinationWidgetWrapper
8461//
8462
8463QgsProcessingFolderDestinationWidgetWrapper::QgsProcessingFolderDestinationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8464 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8465{}
8466
8467QString QgsProcessingFolderDestinationWidgetWrapper::parameterType() const
8468{
8470}
8471
8472QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFolderDestinationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8473{
8474 return new QgsProcessingFolderDestinationWidgetWrapper( parameter, type );
8475}
8476
8477
8478QString QgsProcessingFolderDestinationWidgetWrapper::modelerExpressionFormatString() const
8479{
8480 return tr( "path to folder destination" );
8481}
8482
8483//
8484// QgsProcessingVectorTileDestinationWidgetWrapper
8485//
8486
8487QgsProcessingVectorTileDestinationWidgetWrapper::QgsProcessingVectorTileDestinationWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8488 : QgsProcessingOutputWidgetWrapper( parameter, type, parent )
8489{}
8490
8491QString QgsProcessingVectorTileDestinationWidgetWrapper::parameterType() const
8492{
8494}
8495
8496QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingVectorTileDestinationWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8497{
8498 return new QgsProcessingPointCloudDestinationWidgetWrapper( parameter, type );
8499}
8500
8501QString QgsProcessingVectorTileDestinationWidgetWrapper::modelerExpressionFormatString() const
8502{
8503 return tr( "path to layer destination" );
8504}
8505
8506
8507//
8508// QgsHeatmapPixelSizeWidget
8509//
8510
8511QgsHeatmapPixelSizeWidget::QgsHeatmapPixelSizeWidget( QWidget *parent )
8512 : QgsPanelWidget( parent )
8513{
8514 setupUi( this );
8515
8516 mCellXSpinBox->setShowClearButton( false );
8517 mCellYSpinBox->setShowClearButton( false );
8518 mRowsSpinBox->setShowClearButton( false );
8519 mColumnsSpinBox->setShowClearButton( false );
8520
8521 connect( mCellYSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), mCellXSpinBox, &QgsDoubleSpinBox::setValue );
8522 connect( mCellXSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsHeatmapPixelSizeWidget::pixelSizeChanged );
8523 connect( mRowsSpinBox, qOverload<int>( &QgsSpinBox::valueChanged ), this, &QgsHeatmapPixelSizeWidget::rowsChanged );
8524 connect( mColumnsSpinBox, qOverload<int>( &QgsSpinBox::valueChanged ), this, &QgsHeatmapPixelSizeWidget::columnsChanged );
8525}
8526
8527void QgsHeatmapPixelSizeWidget::setLayer( QgsVectorLayer *layer )
8528{
8529 mLayer = layer;
8530 if ( mLayer )
8531 mLayerBounds = mLayer->extent();
8532 else
8533 mLayerBounds = QgsRectangle();
8534
8535 recalculateBounds();
8536}
8537
8538QgsVectorLayer *QgsHeatmapPixelSizeWidget::layer()
8539{
8540 return mLayer;
8541}
8542
8543void QgsHeatmapPixelSizeWidget::setRadius( double radius )
8544{
8545 mRadius = radius;
8546 recalculateBounds();
8547}
8548
8549void QgsHeatmapPixelSizeWidget::setRadiusField( const QString &radiusField )
8550{
8551 mRadiusField = radiusField;
8552 recalculateBounds();
8553}
8554
8555void QgsHeatmapPixelSizeWidget::recalculateBounds()
8556{
8557 mRasterBounds = mLayerBounds;
8558 if ( !mLayer )
8559 return;
8560
8561 double maxRadius = mRadius;
8562 if ( !mRadiusField.isEmpty() )
8563 {
8564 const int idx = mLayer->fields().lookupField( mRadiusField );
8565 if ( idx >= 0 )
8566 maxRadius = mLayer->maximumValue( idx ).toDouble();
8567 }
8568
8569 mRasterBounds.setXMinimum( mRasterBounds.xMinimum() - maxRadius );
8570 mRasterBounds.setYMinimum( mRasterBounds.yMinimum() - maxRadius );
8571 mRasterBounds.setXMaximum( mRasterBounds.xMaximum() + maxRadius );
8572 mRasterBounds.setYMaximum( mRasterBounds.yMaximum() + maxRadius );
8573
8574 pixelSizeChanged();
8575}
8576
8577void QgsHeatmapPixelSizeWidget::pixelSizeChanged()
8578{
8579 const double prevValue = value();
8580 const double cellSize = mCellXSpinBox->value();
8581 if ( cellSize <= 0 || mRasterBounds.isNull() )
8582 {
8583 emit valueChanged();
8584 return;
8585 }
8586
8587 whileBlocking( mCellYSpinBox )->setValue( cellSize );
8588
8589 const int rows = std::max( static_cast<int>( std::round( mRasterBounds.height() / cellSize ) ) + 1, 1 );
8590 const int cols = std::max( static_cast<int>( std::round( mRasterBounds.width() / cellSize ) ) + 1, 1 );
8591
8592 whileBlocking( mRowsSpinBox )->setValue( rows );
8593 whileBlocking( mColumnsSpinBox )->setValue( cols );
8594
8595 if ( !qgsDoubleNear( prevValue, value() ) )
8596 {
8597 emit valueChanged();
8598 }
8599}
8600
8601void QgsHeatmapPixelSizeWidget::rowsChanged()
8602{
8603 const int rows = mRowsSpinBox->value();
8604 if ( rows <= 0 || mRasterBounds.isNull() )
8605 return;
8606
8607 const double cellSize = mRasterBounds.height() / rows;
8608 if ( cellSize == 0 )
8609 return;
8610
8611 const int cols = std::max( static_cast<int>( std::round( mRasterBounds.width() / cellSize ) ) + 1, 1 );
8612
8613 whileBlocking( mColumnsSpinBox )->setValue( cols );
8614 whileBlocking( mCellXSpinBox )->setValue( cellSize );
8615 whileBlocking( mCellYSpinBox )->setValue( cellSize );
8616
8617 emit valueChanged();
8618}
8619
8620void QgsHeatmapPixelSizeWidget::columnsChanged()
8621{
8622 const int cols = mColumnsSpinBox->value();
8623 if ( cols < 2 || mRasterBounds.isNull() )
8624 return;
8625
8626 const double cellSize = mRasterBounds.width() / ( cols - 1 );
8627 if ( cellSize == 0 )
8628 return;
8629
8630 const int rows = std::max( static_cast<int>( std::round( mRasterBounds.height() / cellSize ) ), 1 );
8631
8632 whileBlocking( mRowsSpinBox )->setValue( rows );
8633 whileBlocking( mCellXSpinBox )->setValue( cellSize );
8634 whileBlocking( mCellYSpinBox )->setValue( cellSize );
8635
8636 emit valueChanged();
8637}
8638
8639double QgsHeatmapPixelSizeWidget::value() const
8640{
8641 return mCellXSpinBox->value();
8642}
8643
8644void QgsHeatmapPixelSizeWidget::setValue( double value )
8645{
8646 mCellXSpinBox->setValue( value );
8647 mCellYSpinBox->setValue( value );
8648}
8649
8650//
8651// QgsProcessingHeatmapPixelSizeWidgetWrapper
8652//
8653
8654QgsProcessingHeatmapPixelSizeWidgetWrapper::QgsProcessingHeatmapPixelSizeWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
8655 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
8656{}
8657
8658QString QgsProcessingHeatmapPixelSizeWidgetWrapper::parameterType() const
8659{
8661}
8662
8663QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingHeatmapPixelSizeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
8664{
8665 return new QgsProcessingHeatmapPixelSizeWidgetWrapper( parameter, type );
8666}
8667
8668QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingHeatmapPixelSizeWidgetWrapper::createParameterDefinitionWidget(
8670)
8671{
8672 return nullptr;
8673}
8674
8675QWidget *QgsProcessingHeatmapPixelSizeWidgetWrapper::createWidget()
8676{
8677 switch ( type() )
8678 {
8680 {
8681 mWidget = new QgsHeatmapPixelSizeWidget();
8682 connect( mWidget, &QgsHeatmapPixelSizeWidget::valueChanged, this, [this] { emit widgetValueHasChanged( this ); } );
8683 return mWidget;
8684 }
8685
8688 {
8689 mFallbackSpinBox = new QgsDoubleSpinBox();
8690 mFallbackSpinBox->setShowClearButton( false );
8691 mFallbackSpinBox->setMinimum( 0.0 );
8692 mFallbackSpinBox->setMaximum( 99999999999.0 );
8693 mFallbackSpinBox->setDecimals( 6 );
8694 mFallbackSpinBox->setToolTip( tr( "Resolution of each pixel in output raster, in layer units" ) );
8695 connect( mFallbackSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, [this] { emit widgetValueHasChanged( this ); } );
8696 return mFallbackSpinBox;
8697 }
8698 }
8699 return nullptr;
8700}
8701
8702void QgsProcessingHeatmapPixelSizeWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
8703{
8705
8706 switch ( type() )
8707 {
8709 {
8710 auto param = dynamic_cast<const QgsProcessingParameterHeatmapPixelSize *>( parameterDefinition() );
8711 if ( !param || !mWidget )
8712 return;
8713
8714 for ( QgsAbstractProcessingParameterWidgetWrapper *wrapper : std::as_const( wrappers ) )
8715 {
8716 if ( wrapper->parameterDefinition()->name() == param->parentLayerParameter() )
8717 {
8718 setParentLayerWrapperValue( wrapper );
8719 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentLayerWrapperValue( wrapper ); } );
8720 }
8721 else if ( wrapper->parameterDefinition()->name() == param->radiusParameter() )
8722 {
8723 mWidget->setRadius( wrapper->parameterValue().toDouble() );
8724 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, &QgsProcessingHeatmapPixelSizeWidgetWrapper::radiusChanged );
8725 }
8726 else if ( wrapper->parameterDefinition()->name() == param->radiusFieldParameter() )
8727 {
8728 mWidget->setRadiusField( wrapper->parameterValue().toString() );
8729 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, &QgsProcessingHeatmapPixelSizeWidgetWrapper::radiusFieldChanged );
8730 }
8731 }
8732 break;
8733 }
8736 break;
8737 }
8738}
8739
8740void QgsProcessingHeatmapPixelSizeWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
8741{
8742 if ( !mWidget )
8743 return;
8744
8745 // evaluate value to layer
8746 QgsProcessingContext *context = nullptr;
8747 std::unique_ptr<QgsProcessingContext> tmpContext;
8748 if ( mProcessingContextGenerator )
8749 context = mProcessingContextGenerator->processingContext();
8750
8751 if ( !context )
8752 {
8753 tmpContext = std::make_unique<QgsProcessingContext>();
8754 context = tmpContext.get();
8755 }
8756
8757 QVariant value = parentWrapper->parameterValue();
8758 if ( value.userType() == qMetaTypeId<QgsProcessingFeatureSourceDefinition>() )
8759 {
8760 // input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
8761 QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( value );
8762 value = fromVar.source;
8763 }
8764
8765 QgsVectorLayer *layer = QgsProcessingParameters::parameterAsVectorLayer( parentWrapper->parameterDefinition(), value, *context );
8766 if ( !layer )
8767 {
8768 mWidget->setLayer( nullptr );
8769 return;
8770 }
8771
8772 // need to grab ownership of layer if required - otherwise layer may be deleted when context
8773 // goes out of scope
8774 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
8775 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Vector )
8776 {
8777 mParentLayer = std::move( ownedLayer );
8778 layer = static_cast<QgsVectorLayer *>( mParentLayer.get() );
8779 }
8780 else
8781 {
8782 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
8783 }
8784
8785 mWidget->setLayer( layer );
8786}
8787
8788void QgsProcessingHeatmapPixelSizeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
8789{
8790 if ( mWidget )
8791 mWidget->setValue( value.toDouble() );
8792 else if ( mFallbackSpinBox )
8793 mFallbackSpinBox->setValue( value.toDouble() );
8794}
8795
8796QVariant QgsProcessingHeatmapPixelSizeWidgetWrapper::widgetValue() const
8797{
8798 if ( mWidget )
8799 return mWidget->value();
8800 else if ( mFallbackSpinBox )
8801 return mFallbackSpinBox->value();
8802 return QVariant();
8803}
8804
8805const QgsVectorLayer *QgsProcessingHeatmapPixelSizeWidgetWrapper::linkedVectorLayer() const
8806{
8807 if ( mWidget && mWidget->layer() )
8808 return mWidget->layer();
8809
8811}
8812
8813void QgsProcessingHeatmapPixelSizeWidgetWrapper::radiusChanged( QgsAbstractProcessingParameterWidgetWrapper *wrapper )
8814{
8815 if ( mWidget )
8816 mWidget->setRadius( wrapper->parameterValue().toDouble() );
8817}
8818
8819void QgsProcessingHeatmapPixelSizeWidgetWrapper::radiusFieldChanged( QgsAbstractProcessingParameterWidgetWrapper *wrapper )
8820{
8821 if ( mWidget )
8822 mWidget->setRadiusField( wrapper->parameterValue().toString() );
8823}
8824
8825//
8826// QgsReliefColorsWidget
8827//
8828
8829QgsReliefColorsWidget::QgsReliefColorsWidget( QWidget *parent )
8830 : QgsPanelWidget( parent )
8831{
8832 setupUi( this );
8833
8834 connect( mButtonAdd, &QToolButton::clicked, this, &QgsReliefColorsWidget::addClicked );
8835 connect( mButtonRemove, &QToolButton::clicked, this, &QgsReliefColorsWidget::removeClicked );
8836 connect( mButtonUp, &QToolButton::clicked, this, &QgsReliefColorsWidget::upClicked );
8837 connect( mButtonDown, &QToolButton::clicked, this, &QgsReliefColorsWidget::downClicked );
8838 connect( mButtonLoad, &QToolButton::clicked, this, &QgsReliefColorsWidget::loadClicked );
8839 connect( mButtonSave, &QToolButton::clicked, this, &QgsReliefColorsWidget::saveClicked );
8840 connect( mButtonAuto, &QToolButton::clicked, this, &QgsReliefColorsWidget::autoCalculate );
8841 connect( mReliefColorsWidget, &QTreeWidget::itemDoubleClicked, this, &QgsReliefColorsWidget::itemDoubleClicked );
8842}
8843
8844void QgsReliefColorsWidget::setLayer( QgsRasterLayer *layer )
8845{
8846 mLayer = layer;
8847}
8848
8849QgsRasterLayer *QgsReliefColorsWidget::layer()
8850{
8851 return mLayer.data();
8852}
8853
8854void QgsReliefColorsWidget::addClicked()
8855{
8856 auto item = new QTreeWidgetItem();
8857 item->setText( 0, u"0.00"_s );
8858 item->setText( 1, u"0.00"_s );
8859 item->setBackground( 2, QBrush( QColor( 127, 127, 127 ) ) );
8860 mReliefColorsWidget->addTopLevelItem( item );
8861 emit valueChanged();
8862}
8863
8864void QgsReliefColorsWidget::removeClicked()
8865{
8866 const QList<QTreeWidgetItem *> selectedItems = mReliefColorsWidget->selectedItems();
8867 for ( QTreeWidgetItem *item : std::as_const( selectedItems ) )
8868 {
8869 delete item;
8870 }
8871 emit valueChanged();
8872}
8873
8874void QgsReliefColorsWidget::downClicked()
8875{
8876 const QList<QTreeWidgetItem *> selectedItems = mReliefColorsWidget->selectedItems();
8877 for ( QTreeWidgetItem *item : std::as_const( selectedItems ) )
8878 {
8879 const int currentIndex = mReliefColorsWidget->indexOfTopLevelItem( item );
8880 if ( currentIndex >= 0 && currentIndex < mReliefColorsWidget->topLevelItemCount() - 1 )
8881 {
8882 mReliefColorsWidget->takeTopLevelItem( currentIndex );
8883 mReliefColorsWidget->insertTopLevelItem( currentIndex + 1, item );
8884 mReliefColorsWidget->setCurrentItem( item );
8885 }
8886 }
8887 emit valueChanged();
8888}
8889
8890void QgsReliefColorsWidget::upClicked()
8891{
8892 const QList<QTreeWidgetItem *> selectedItems = mReliefColorsWidget->selectedItems();
8893 for ( QTreeWidgetItem *item : std::as_const( selectedItems ) )
8894 {
8895 const int currentIndex = mReliefColorsWidget->indexOfTopLevelItem( item );
8896 if ( currentIndex > 0 )
8897 {
8898 mReliefColorsWidget->takeTopLevelItem( currentIndex );
8899 mReliefColorsWidget->insertTopLevelItem( currentIndex - 1, item );
8900 mReliefColorsWidget->setCurrentItem( item );
8901 }
8902 }
8903 emit valueChanged();
8904}
8905
8906void QgsReliefColorsWidget::loadClicked()
8907{
8908 const QString fileName = QFileDialog::getOpenFileName( this, tr( "Import Colors from XML" ), QDir::homePath(), tr( "XML files (*.xml *.XML)" ) );
8909 if ( fileName.isEmpty() )
8910 return;
8911
8912 QFile file( fileName );
8913 if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) )
8914 return;
8915
8916 QDomDocument doc;
8917 if ( !doc.setContent( &file ) )
8918 {
8919 QMessageBox::critical( this, tr( "Import Colors from XML" ), tr( "The XML file could not be loaded" ) );
8920 return;
8921 }
8922
8923 mReliefColorsWidget->clear();
8924 const QDomNodeList reliefColorList = doc.elementsByTagName( u"ReliefColor"_s );
8925 for ( int i = 0; i < reliefColorList.length(); ++i )
8926 {
8927 const QDomElement elem = reliefColorList.at( i ).toElement();
8928 auto item = new QTreeWidgetItem();
8929 item->setText( 0, elem.attribute( u"MinElevation"_s ) );
8930 item->setText( 1, elem.attribute( u"MaxElevation"_s ) );
8931 item->setBackground( 2, QBrush( QColor( elem.attribute( u"red"_s ).toInt(), elem.attribute( u"green"_s ).toInt(), elem.attribute( u"blue"_s ).toInt() ) ) );
8932 mReliefColorsWidget->addTopLevelItem( item );
8933 }
8934 emit valueChanged();
8935}
8936
8937void QgsReliefColorsWidget::saveClicked()
8938{
8939 QString fileName = QFileDialog::getSaveFileName( this, tr( "Export Colors as XML" ), QDir::homePath(), tr( "XML files (*.xml *.XML)" ) );
8940 if ( fileName.isEmpty() )
8941 return;
8942
8943 fileName = QgsFileUtils::ensureFileNameHasExtension( fileName, { u"xml"_s } );
8944
8945 QDomDocument doc;
8946 QDomElement colorsElem = doc.createElement( u"ReliefColors"_s );
8947 doc.appendChild( colorsElem );
8948
8949 const QList<QgsRasterReliefColor> currentColors = colors();
8950 for ( const QgsRasterReliefColor &c : currentColors )
8951 {
8952 QDomElement elem = doc.createElement( u"ReliefColor"_s );
8953 elem.setAttribute( u"MinElevation"_s, qgsDoubleToString( c.minElevation ) );
8954 elem.setAttribute( u"MaxElevation"_s, qgsDoubleToString( c.maxElevation ) );
8955 elem.setAttribute( u"red"_s, QString::number( c.color.red() ) );
8956 elem.setAttribute( u"green"_s, QString::number( c.color.green() ) );
8957 elem.setAttribute( u"blue"_s, QString::number( c.color.blue() ) );
8958 colorsElem.appendChild( elem );
8959 }
8960
8961 QFile file( fileName );
8962 if ( file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
8963 {
8964 QTextStream out( &file );
8965 out << doc.toString( 2 );
8966 }
8967}
8968
8969void QgsReliefColorsWidget::autoCalculate()
8970{
8971 if ( !mLayer || !mLayer->isValid() )
8972 return;
8973
8974 const QList< QgsRasterReliefColor > autoColors = QgsRasterLayerUtils::calculateOptimizedReliefClasses( mLayer->dataProvider(), 1 );
8975 setColors( autoColors );
8976 emit valueChanged();
8977}
8978
8979void QgsReliefColorsWidget::itemDoubleClicked( QTreeWidgetItem *item, int column )
8980{
8981 if ( !item )
8982 return;
8983
8984 bool ok = false;
8985 if ( column == 0 )
8986 {
8987 const double d = QInputDialog::getDouble( this, tr( "Enter lower elevation class bound" ), tr( "Elevation" ), item->text( 0 ).toDouble(), -2147483647, 2147483647, 2, &ok );
8988 if ( ok )
8989 {
8990 item->setText( 0, QString::number( d ) );
8991 emit valueChanged();
8992 }
8993 }
8994 else if ( column == 1 )
8995 {
8996 const double d = QInputDialog::getDouble( this, tr( "Enter upper elevation class bound" ), tr( "Elevation" ), item->text( 1 ).toDouble(), -2147483647, 2147483647, 2, &ok );
8997 if ( ok )
8998 {
8999 item->setText( 1, QString::number( d ) );
9000 emit valueChanged();
9001 }
9002 }
9003 else if ( column == 2 )
9004 {
9005 const QColor c = QColorDialog::getColor( item->background( 2 ).color(), this, tr( "Select color for relief class" ) );
9006 if ( c.isValid() )
9007 {
9008 item->setBackground( 2, QBrush( c ) );
9009 emit valueChanged();
9010 }
9011 }
9012}
9013
9014QList<QgsRasterReliefColor> QgsReliefColorsWidget::colors() const
9015{
9016 QList<QgsRasterReliefColor> colors;
9017 for ( int i = 0; i < mReliefColorsWidget->topLevelItemCount(); ++i )
9018 {
9019 if ( QTreeWidgetItem *item = mReliefColorsWidget->topLevelItem( i ) )
9020 {
9021 colors.append( QgsRasterReliefColor( item->background( 2 ).color(), item->text( 0 ).toDouble(), item->text( 1 ).toDouble() ) );
9022 }
9023 }
9024 return colors;
9025}
9026
9027void QgsReliefColorsWidget::setColors( const QList<QgsRasterReliefColor> &colors )
9028{
9029 mReliefColorsWidget->clear();
9030 for ( const QgsRasterReliefColor &c : colors )
9031 {
9032 auto item = new QTreeWidgetItem();
9033 item->setText( 0, qgsDoubleToString( c.minElevation ) );
9034 item->setText( 1, qgsDoubleToString( c.maxElevation ) );
9035 item->setBackground( 2, QBrush( c.color ) );
9036 mReliefColorsWidget->addTopLevelItem( item );
9037 }
9038 emit valueChanged();
9039}
9040
9041//
9042// QgsProcessingReliefColorsWidgetWrapper
9043//
9044
9045QgsProcessingReliefColorsWidgetWrapper::QgsProcessingReliefColorsWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type, QWidget *parent )
9046 : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
9047{}
9048
9049QString QgsProcessingReliefColorsWidgetWrapper::parameterType() const
9050{
9052}
9053
9054QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingReliefColorsWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, Qgis::ProcessingMode type )
9055{
9056 return new QgsProcessingReliefColorsWidgetWrapper( parameter, type );
9057}
9058
9059QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingReliefColorsWidgetWrapper::createParameterDefinitionWidget(
9061)
9062{
9063 return nullptr;
9064}
9065
9066QWidget *QgsProcessingReliefColorsWidgetWrapper::createWidget()
9067{
9068 switch ( type() )
9069 {
9071 {
9072 mWidget = new QgsReliefColorsWidget();
9073 connect( mWidget, &QgsReliefColorsWidget::valueChanged, this, [this] { emit widgetValueHasChanged( this ); } );
9074 return mWidget;
9075 }
9076
9079 {
9080 mFallbackLineEdit = new QLineEdit();
9081 mFallbackLineEdit->setPlaceholderText( tr( "Relief colors definition" ) );
9082 connect( mFallbackLineEdit, &QLineEdit::textChanged, this, [this] { emit widgetValueHasChanged( this ); } );
9083 return mFallbackLineEdit;
9084 }
9085 }
9086 return nullptr;
9087}
9088
9089void QgsProcessingReliefColorsWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
9090{
9092
9093 switch ( type() )
9094 {
9096 {
9097 auto param = dynamic_cast<const QgsProcessingParameterReliefColors *>( parameterDefinition() );
9098 if ( !param || !mWidget )
9099 return;
9100
9101 for ( QgsAbstractProcessingParameterWidgetWrapper *wrapper : std::as_const( wrappers ) )
9102 {
9103 if ( wrapper->parameterDefinition()->name() == param->parentLayerParameter() )
9104 {
9105 setParentLayerWrapperValue( wrapper );
9106 connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [this, wrapper] { setParentLayerWrapperValue( wrapper ); } );
9107 }
9108 }
9109 break;
9110 }
9113 break;
9114 }
9115}
9116
9117void QgsProcessingReliefColorsWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
9118{
9119 if ( !mWidget )
9120 return;
9121
9122 // evaluate value to layer
9123 QgsProcessingContext *context = nullptr;
9124 std::unique_ptr<QgsProcessingContext> tmpContext;
9125 if ( mProcessingContextGenerator )
9126 context = mProcessingContextGenerator->processingContext();
9127
9128 if ( !context )
9129 {
9130 tmpContext = std::make_unique<QgsProcessingContext>();
9131 context = tmpContext.get();
9132 }
9133
9134 QVariant value = parentWrapper->parameterValue();
9135 QgsRasterLayer *layer = QgsProcessingParameters::parameterAsRasterLayer( parentWrapper->parameterDefinition(), value, *context );
9136 if ( layer && layer->isValid() )
9137 {
9138 // need to grab ownership of layer if required - otherwise layer may be deleted when context
9139 // goes out of scope
9140 std::unique_ptr<QgsMapLayer> ownedLayer( context->takeResultLayer( layer->id() ) );
9141 if ( ownedLayer && ownedLayer->type() == Qgis::LayerType::Raster )
9142 {
9143 mParentLayer.reset( qobject_cast<QgsRasterLayer *>( ownedLayer.release() ) );
9144 layer = mParentLayer.get();
9145 }
9146 else
9147 {
9148 // don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
9149 }
9150
9151 if ( mWidget )
9152 mWidget->setLayer( layer );
9153 }
9154 else
9155 {
9156 if ( mWidget )
9157 mWidget->setLayer( nullptr );
9158 }
9159}
9160
9161void QgsProcessingReliefColorsWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
9162{
9163 auto param = dynamic_cast<const QgsProcessingParameterReliefColors *>( parameterDefinition() );
9164
9165 if ( mWidget && param )
9166 {
9167 mWidget->setColors( param->valueAsReliefColors( value, context ) );
9168 }
9169 else if ( mFallbackLineEdit )
9170 mFallbackLineEdit->setText( value.toString() );
9171}
9172
9173QVariant QgsProcessingReliefColorsWidgetWrapper::widgetValue() const
9174{
9175 if ( mWidget )
9176 return QgsProcessingParameterReliefColors::colorsAsVariant( mWidget->colors() );
9177 else if ( mFallbackLineEdit )
9178 return mFallbackLineEdit->text();
9179 return QVariant();
9180}
9181
9182QString QgsProcessingReliefColorsWidgetWrapper::modelerExpressionFormatString() const
9183{
9184 return tr( "string of the format 'min,max,red,green,blue' for each relief color, joined by a ; delimiter" );
9185}
9186
9187
@ Standard
Unit is a standard measurement unit.
Definition qgis.h:5527
ProcessingSourceType
Processing data source types.
Definition qgis.h:3726
@ File
Files (i.e. non map layer sources, such as text files).
Definition qgis.h:3733
@ Plugin
Plugin layers.
Definition qgis.h:3736
@ TiledScene
Tiled scene layers.
Definition qgis.h:3740
@ Annotation
Annotation layers.
Definition qgis.h:3738
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition qgis.h:3734
@ VectorTile
Vector tile layers.
Definition qgis.h:3739
@ MapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer).
Definition qgis.h:3727
@ Mesh
Mesh layers.
Definition qgis.h:3735
@ Raster
Raster layers.
Definition qgis.h:3732
@ VectorAnyGeometry
Any vector layer with geometry.
Definition qgis.h:3728
@ VectorPoint
Vector point layers.
Definition qgis.h:3729
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3731
@ VectorLine
Vector line layers.
Definition qgis.h:3730
@ PointCloud
Point cloud layers.
Definition qgis.h:3737
ProcessingFileParameterBehavior
Flags which dictate the behavior of QgsProcessingParameterFile.
Definition qgis.h:3986
@ File
Parameter is a single file.
Definition qgis.h:3987
@ Folder
Parameter is a folder.
Definition qgis.h:3988
ExpressionType
Expression types.
Definition qgis.h:6192
@ RasterCalculator
Raster calculator expression.
Definition qgis.h:6195
@ Qgis
Native QGIS expression.
Definition qgis.h:6193
@ PointCloud
Point cloud expression.
Definition qgis.h:6194
ProcessingMode
Types of modes which Processing widgets can be created for.
Definition qgis.h:3867
@ Batch
Batch processing mode.
Definition qgis.h:3869
@ Modeler
Modeler mode.
Definition qgis.h:3870
@ Standard
Standard (single-run) algorithm mode.
Definition qgis.h:3868
DistanceUnit
Units of distance.
Definition qgis.h:5464
@ Feet
Imperial feet.
Definition qgis.h:5467
@ Centimeters
Centimeters.
Definition qgis.h:5472
@ Millimeters
Millimeters.
Definition qgis.h:5473
@ Miles
Terrestrial miles.
Definition qgis.h:5470
@ Meters
Meters.
Definition qgis.h:5465
@ Unknown
Unknown distance unit.
Definition qgis.h:5514
@ Yards
Imperial yards.
Definition qgis.h:5469
@ Degrees
Degrees, for planar geographic CRS distance measurements.
Definition qgis.h:5471
@ Inches
Inches.
Definition qgis.h:5474
@ NauticalMiles
Nautical miles.
Definition qgis.h:5468
@ Kilometers
Kilometers.
Definition qgis.h:5466
ProcessingFieldParameterDataType
Processing field parameter data types.
Definition qgis.h:4014
@ String
Accepts string fields.
Definition qgis.h:4017
@ Boolean
Accepts boolean fields, since QGIS 3.34.
Definition qgis.h:4020
@ Binary
Accepts binary fields, since QGIS 3.34.
Definition qgis.h:4019
@ Numeric
Accepts numeric fields.
Definition qgis.h:4016
@ DateTime
Accepts datetime fields.
Definition qgis.h:4018
AreaUnit
Units of area.
Definition qgis.h:5541
@ Acres
Acres.
Definition qgis.h:5548
@ SquareFeet
Square feet.
Definition qgis.h:5544
@ SquareCentimeters
Square centimeters.
Definition qgis.h:5551
@ SquareInches
Square inches.
Definition qgis.h:5553
@ SquareNauticalMiles
Square nautical miles.
Definition qgis.h:5549
@ SquareMillimeters
Square millimeters.
Definition qgis.h:5552
@ SquareYards
Square yards.
Definition qgis.h:5545
@ Hectares
Hectares.
Definition qgis.h:5547
@ SquareKilometers
Square kilometers.
Definition qgis.h:5543
@ SquareMeters
Square meters.
Definition qgis.h:5542
@ Unknown
Unknown areal unit.
Definition qgis.h:5554
@ SquareDegrees
Square degrees, for planar geographic CRS area measurements.
Definition qgis.h:5550
@ SquareMiles
Square miles.
Definition qgis.h:5546
@ Info
Information message.
Definition qgis.h:161
@ Static
Static property.
Definition qgis.h:725
@ AnnotationLayer
QgsAnnotationLayer.
Definition qgis.h:238
TemporalUnit
Temporal units.
Definition qgis.h:5610
@ Milliseconds
Milliseconds.
Definition qgis.h:5611
@ Hours
Hours.
Definition qgis.h:5614
@ Centuries
Centuries.
Definition qgis.h:5620
@ Seconds
Seconds.
Definition qgis.h:5612
@ Weeks
Weeks.
Definition qgis.h:5616
@ Years
Years.
Definition qgis.h:5618
@ Decades
Decades.
Definition qgis.h:5619
@ Minutes
Minutes.
Definition qgis.h:5613
@ Point
Points.
Definition qgis.h:380
@ Vector
Vector layer.
Definition qgis.h:207
@ Raster
Raster layer.
Definition qgis.h:208
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:213
QFlags< ProcessingParameterFlag > ProcessingParameterFlags
Flags which dictate the behavior of Processing parameters.
Definition qgis.h:3975
VolumeUnit
Units of volume.
Definition qgis.h:5566
@ CubicMeters
Cubic meters.
Definition qgis.h:5567
@ Barrel
Barrels.
Definition qgis.h:5570
@ CubicYards
Cubic yards.
Definition qgis.h:5569
@ CubicFeet
Cubic feet.
Definition qgis.h:5568
@ CubicDegrees
Cubic degrees, for planar geographic CRS volume measurements.
Definition qgis.h:5576
@ CubicDecimeter
Cubic decimeters.
Definition qgis.h:5571
@ Unknown
Unknown volume unit.
Definition qgis.h:5577
@ CubicInch
Cubic inches.
Definition qgis.h:5574
@ GallonUS
US Gallons.
Definition qgis.h:5573
@ Liters
Litres.
Definition qgis.h:5572
@ CubicCentimeter
Cubic Centimeters.
Definition qgis.h:5575
ProcessingModelChildParameterSource
Processing model child parameter sources.
Definition qgis.h:4045
@ ModelParameter
Parameter value is taken from a parent model parameter.
Definition qgis.h:4046
@ StaticValue
Parameter value is a static value.
Definition qgis.h:4048
@ Optional
Parameter is optional.
Definition qgis.h:3963
ProcessingDateTimeParameterDataType
Processing date time parameter data types.
Definition qgis.h:4032
ProcessingNumberParameterType
Processing numeric parameter data types.
Definition qgis.h:4000
@ Double
Double/float values.
Definition qgis.h:4002
A widget wrapper for Processing parameter value widgets.
QVariant parameterValue() const
Returns the current value of the parameter.
virtual void setDialog(QWidget *dialog)
Sets the parent dialog (or widget) in which the wrapper is shown.
virtual QLabel * createLabel()
Creates a new label to accompany widgets created by the wrapper.
virtual void registerProcessingContextGenerator(QgsProcessingContextGenerator *generator)
Registers a Processing context generator class that will be used to retrieve a Processing context for...
void widgetValueHasChanged(QgsAbstractProcessingParameterWidgetWrapper *wrapper)
Emitted whenever the parameter value (as defined by the wrapped widget) is changed.
virtual void postInitialize(const QList< QgsAbstractProcessingParameterWidgetWrapper * > &wrappers)
Called after all wrappers have been created within a particular dialog or context,...
virtual const QgsVectorLayer * linkedVectorLayer() const
Returns the optional vector layer associated with this widget wrapper, or nullptr if no vector layer ...
const QgsProcessingParameterDefinition * parameterDefinition() const
Returns the parameter definition associated with this wrapper.
virtual void setWidgetContext(const QgsProcessingParameterWidgetContext &context)
Sets the context in which the Processing parameter widget is shown, e.g., the parent model algorithm,...
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
@ CapturePoint
Select and capture a point or a feature.
Selector widget for authentication configs.
void selectedConfigIdChanged(const QString &authcfg)
Emitted when authentication config is changed or missing.
QComboBox subclass which allows selecting multiple items.
void checkedItemsChanged(const QStringList &items)
Emitted whenever the checked items list changed.
A cross platform button subclass for selecting colors.
void colorChanged(const QColor &color)
Emitted whenever a new color is set for the button.
A widget for selecting the coordinate operation to use when transforming between a source and destina...
void operationChanged()
Emitted when the operation selected in the dialog is changed.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
A combo box which displays the list of schemas for a specific database connection.
A combobox which displays the list of tables for a specific database connection.
A QDateEdit widget with the capability of setting/reading null dates.
void dateValueChanged(const QDate &date)
Signal emitted whenever the date changes.
A QDateTimeEdit with the capability of setting/reading null date/times.
void setAllowNull(bool allowNull)
Determines if the widget allows setting null date/time.
void setNullRepresentation(const QString &null)
Sets the widget's null representation, which defaults to QgsApplication::nullRepresentation().
void valueChanged(const QDateTime &date)
Signal emitted whenever the value changes.
A dialog for configuring transform operations between coordinate reference systems.
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value.
static double toDouble(const QString &input, bool *ok)
Converts input string to double value.
A reusable widget that can be used to build an expression string.
void expressionParsed(bool isValid)
Emitted when the user changes the expression in the widget.
A widget which includes a line edit for entering expressions together with a button to open the expre...
void expressionChanged(const QString &expression)
Emitted when the expression is changed.
A widget for configuration of a map extent.
void toggleDialogVisibility(bool visible)
Emitted when the parent dialog visibility must be changed (e.g.
void extentChanged(const QgsRectangle &r)
Emitted when the widget's extent is changed.
A combobox which displays the list of fields of a given layer.
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
A widget for selection of layer fields or expression creation.
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
@ DateTime
Datetime fields.
@ Date
Date or datetime fields.
@ Binary
Binary fields, since QGIS 3.34.
@ String
String fields.
@ Boolean
Boolean fields, since QGIS 3.34.
@ Numeric
All numeric fields.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QString name
Definition qgsfield.h:65
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:75
int count
Definition qgsfields.h:50
bool isEmpty
Definition qgsfields.h:49
void remove(int fieldIdx)
Removes the field with the given index.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
A widget for selecting a file or a folder.
@ GetFile
Select a single file.
@ GetDirectory
Select a directory.
void fileChanged(const QString &path)
Emitted whenever the current file or directory path is changed.
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
A widget for storing and interacting with a QgsGeometry object.
void geometryValueChanged(const QgsReferencedGeometry &value)
Emitted whenever the geometry value of the widget is changed.
A geometry is the spatial representation of a feature.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
A QgsFilterLineEdit subclass with the ability to "highlight" the edges of the widget.
A combobox which displays available layouts from a QgsLayoutManager.
void layoutChanged(QgsMasterLayoutInterface *layout)
Emitted whenever the currently selected layout changes.
A combo box which displays items of a matching type from a layout.
void itemChanged(QgsLayoutItem *item)
Emitted whenever the currently selected item changes.
Base class for graphical items within a QgsLayout.
virtual QString uuid() const
Returns the item identification string.
@ FilterPrintLayouts
Includes print layouts.
QList< QgsPrintLayout * > printLayouts() const
Returns a list of all print layouts contained in the manager.
Map canvas is a class for displaying all GIS data types on a canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
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 name
Definition qgsmaplayer.h:87
QString id
Definition qgsmaplayer.h:86
A mouse event which is the result of a user interaction with a QgsMapCanvas.
QgsPointLocator::Match mapPointMatch() const
Returns the matching data from the most recently snapped point.
QgsPointXY snapPoint()
snapPoint will snap the points using the map canvas snapping utils configuration
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
Abstract base class for all map tools.
Definition qgsmaptool.h:74
virtual void deactivate()
called when map tool is being deactivated
Interface for master layout type objects, such as print layouts and reports.
virtual QString name() const =0
Returns the layout's name.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
bool clearWidgets()
Removes all items from the bar.
Base class for any widget that can be shown as an inline panel.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
bool dockMode() const
Returns the dock mode state.
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.
A collection of point cloud attributes.
A combobox which displays the list of attributes of a given point cloud layer.
void attributeChanged(const QString &name)
Emitted when the currently selected attribute changes.
Attribute for point cloud data pair of name and size in bytes.
Represents a map layer supporting display of point clouds.
QgsPointCloudAttributeCollection attributes() const
Returns the attributes available from the layer.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
Print layout, a QgsLayout subclass for static or atlas-based layouts.
Abstract base class for widgets which allow users to specify the properties of a Processing parameter...
void changed()
Emitted whenever the definition of the parameter is changed in the widget.
Abstract base class for processing algorithms.
An interface for objects which can create Processing contexts.
Contains information about the context in which a processing algorithm is executed.
QgsExpressionContext & expressionContext()
Returns the expression context.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsMapLayer * takeResultLayer(const QString &id)
Takes the result map layer with matching id from the context and transfers ownership of it back to th...
Base class for all parameter definitions which represent file or layer destinations,...
Encapsulates settings relating to a feature source input to a processing algorithm.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the output class.
static QString typeName()
Returns the type name for the parameter class.
A double numeric parameter for area values.
Qgis::AreaUnit defaultUnit() const
Returns the default area unit for the parameter.
static QString typeName()
Returns the type name for the parameter class.
static QString typeName()
Returns the type name for the parameter class.
A raster band parameter for Processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
bool allowMultiple() const
Returns whether multiple band selections are permitted.
A boolean parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A color parameter for processing algorithms.
bool opacityEnabled() const
Returns true if the parameter allows opacity control.
static QString typeName()
Returns the type name for the parameter class.
A coordinate operation parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
QVariant sourceCrs() const
Returns the static source CRS, or an invalid value if this is not set.
QVariant destinationCrs() const
Returns the static destination CRS, or an invalid value if this is not set.
A coordinate reference system parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A database schema parameter for processing algorithms, allowing users to select from existing schemas...
static QString typeName()
Returns the type name for the parameter class.
QString parentConnectionParameterName() const
Returns the name of the parent connection parameter, or an empty string if this is not set.
A database table name parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
QString parentConnectionParameterName() const
Returns the name of the parent connection parameter, or an empty string if this is not set.
QString parentSchemaParameterName() const
Returns the name of the parent schema parameter, or an empty string if this is not set.
bool allowNewTableNames() const
Returns true if the parameter allows users to enter names for a new (non-existing) tables.
A datetime (or pure date or time) parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
Qgis::ProcessingDateTimeParameterDataType dataType() const
Returns the acceptable data type for the parameter.
Base class for the definition of processing parameters.
void setFlags(Qgis::ProcessingParameterFlags flags)
Sets the flags associated with the parameter.
QVariantMap metadata() const
Returns the parameter's freeform metadata.
QString description() const
Returns the description for the parameter.
QVariant defaultValueForGui() const
Returns the default value to use for the parameter in a GUI.
virtual QString type() const =0
Unique parameter type name.
QString name() const
Returns the name of the parameter.
Qgis::ProcessingParameterFlags flags() const
Returns any flags associated with the parameter.
A double numeric parameter for distance values.
static QString typeName()
Returns the type name for the parameter class.
Qgis::DistanceUnit defaultUnit() const
Returns the default distance unit for the parameter.
A double numeric parameter for duration values.
Qgis::TemporalUnit defaultUnit() const
Returns the default duration unit for the parameter.
static QString typeName()
Returns the type name for the parameter class.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
bool allowMultiple() const
Returns true if the parameter allows multiple selected values.
QStringList options() const
Returns the list of acceptable options for the parameter.
bool usesStaticStrings() const
Returns true if the parameter uses static (non-translated) string values for its enumeration choice l...
static QString typeName()
Returns the type name for the parameter class.
An expression parameter for processing algorithms.
QString parentLayerParameterName() const
Returns the name of the parent layer parameter, or an empty string if this is not set.
static QString typeName()
Returns the type name for the parameter class.
Qgis::ExpressionType expressionType() const
Returns the parameter's expression type.
A rectangular map extent 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.
An input feature source (such as vector layers) parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A vector layer or feature source field parameter for processing algorithms.
Qgis::ProcessingFieldParameterDataType dataType() const
Returns the acceptable data type for the field.
bool allowMultiple() const
Returns whether multiple field selections are permitted.
bool defaultToAllFields() const
Returns whether a parameter which allows multiple selections (see allowMultiple()) should automatical...
static QString typeName()
Returns the type name for the parameter class.
void setDataType(Qgis::ProcessingFieldParameterDataType type)
Sets the acceptable data type for the field.
static QString typeName()
Returns the type name for the parameter class.
An input file or folder parameter for processing algorithms.
QString extension() const
Returns any specified file extension for the parameter.
static QString typeName()
Returns the type name for the parameter class.
QString fileFilter() const
Returns the file filter string for file destinations compatible with this parameter.
Qgis::ProcessingFileParameterBehavior behavior() const
Returns the parameter behavior (e.g.
static QString typeName()
Returns the type name for the parameter class.
A geometry parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A parameter for the heatmap algorithm pixel size parameter.
static QString typeName()
Returns the type name for the parameter class.
A print layout item parameter, allowing users to select a particular item from a print layout.
static QString typeName()
Returns the type name for the parameter class.
int itemType() const
Returns the acceptable item type, or -1 if any item type is allowed.
A print layout parameter, allowing users to select a print layout.
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.
A map theme parameter for processing algorithms, allowing users to select an existing map theme from ...
static QString typeName()
Returns the type name for the parameter class.
A table (matrix) 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.
A parameter for processing algorithms which accepts multiple map layers.
static QString typeName()
Returns the type name for the parameter class.
A numeric parameter for processing algorithms.
double minimum() const
Returns the minimum value acceptable by the parameter.
double maximum() const
Returns the maximum value acceptable by the parameter.
Qgis::ProcessingNumberParameterType dataType() const
Returns the acceptable data type for the parameter.
static QString typeName()
Returns the type name for the parameter class.
A point cloud layer attribute parameter for Processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
bool allowMultiple() const
Returns whether multiple field selections are permitted.
bool defaultToAllAttributes() const
Returns whether a parameter which allows multiple selections (see allowMultiple()) should automatical...
static QString typeName()
Returns the type name for the parameter class.
A point cloud layer parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A point parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
A data provider connection parameter for processing algorithms, allowing users to select from availab...
static QString typeName()
Returns the type name for the parameter class.
QString providerId() const
Returns the ID of the provider associated with the connections.
A numeric range parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
Qgis::ProcessingNumberParameterType dataType() const
Returns the acceptable data type for the range.
static QString typeName()
Returns the type name for the parameter class.
A raster layer parameter for processing algorithms.
static QString typeName()
Returns the type name for the parameter class.
static QVariant colorsAsVariant(const QList< QgsRasterReliefColor > &colors)
Returns a list of raster relief colors encoded as a variant value.
static QString typeName()
Returns the type name for the parameter class.
A double numeric parameter for map scale values.
static QString typeName()
Returns the type name for the parameter class.
A string 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.
A vector layer (with or without geometry) 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.
A double numeric parameter for volume values.
static QString typeName()
Returns the type name for the parameter class.
Qgis::VolumeUnit defaultUnit() const
Returns the default volume unit for the parameter.
Contains settings which reflect the context in which a Processing parameter widget is shown.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
QgsProject * project() const
Returns the project associated with the widget.
QgsMessageBar * messageBar() const
Returns the message bar associated with the widget.
QgsProcessingModelAlgorithm * model() const
Returns the model which the parameter widget is associated with.
QgsMapLayer * activeLayer() const
Returns the current active layer.
static int parameterAsEnum(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a enum value.
static double parameterAsDouble(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static double value.
static QgsPointXY parameterAsPoint(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem())
Evaluates the parameter with matching definition to a point.
static QgsPrintLayout * parameterAsLayout(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a print layout.
static QList< QgsMapLayer * > parameterAsLayerList(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Evaluates the parameter with matching definition to a list of map layers.
static QTime parameterAsTime(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static time value.
static QgsRectangle parameterAsExtent(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem())
Evaluates the parameter with matching definition to a rectangular extent.
static QString parameterAsEnumString(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static enum string.
static QList< double > parameterAsRange(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a range of values.
static QStringList parameterAsStrings(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a list of strings (e.g.
static QList< int > parameterAsInts(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a list of integer values.
static QString parameterAsConnectionName(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a connection name string.
static QgsProcessingFeatureSource * parameterAsSource(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a feature source.
static QgsPointCloudLayer * parameterAsPointCloudLayer(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Evaluates the parameter with matching definition to a point cloud layer.
static QgsCoordinateReferenceSystem parameterAsPointCrs(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Returns the coordinate reference system associated with an point parameter value.
static QgsLayoutItem * parameterAsLayoutItem(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, QgsPrintLayout *layout)
Evaluates the parameter with matching definition to a print layout item, taken from the specified lay...
static bool parameterAsBool(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static boolean value.
static QColor parameterAsColor(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Returns the color associated with an point parameter value, or an invalid color if the parameter was ...
static QgsVectorLayer * parameterAsVectorLayer(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a vector layer.
static int parameterAsInt(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static integer value.
static QString parameterAsDatabaseTableName(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a database table name.
static QString parameterAsSchema(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a database schema name.
static QgsGeometry parameterAsGeometry(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem())
Evaluates the parameter with matching definition to a geometry.
static QString parameterAsExpression(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to an expression.
static QString parameterAsString(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static string value.
static QgsRasterLayer * parameterAsRasterLayer(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a raster layer.
static QList< int > parameterAsEnums(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to list of enum values.
static QStringList parameterAsEnumStrings(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to list of static enum strings.
static QgsCoordinateReferenceSystem parameterAsExtentCrs(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Returns the coordinate reference system associated with an extent parameter value.
static QDateTime parameterAsDateTime(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static datetime value.
static QDate parameterAsDate(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context)
Evaluates the parameter with matching definition to a static date value.
static QVariantList parameterAsMatrix(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a matrix/table of values.
static QgsCoordinateReferenceSystem parameterAsCrs(const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context)
Evaluates the parameter with matching definition to a coordinate reference system.
Utility functions for use with processing classes.
@ Annotation
Annotation layer type, since QGIS 3.22.
static QgsCoordinateReferenceSystem variantToCrs(const QVariant &value, QgsProcessingContext &context, const QVariant &fallbackValue=QVariant())
Converts a variant value to a coordinate reference system.
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.
@ SkipIndexGeneration
Do not generate index when creating a layer. Makes sense only for point cloud layers.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsMapThemeCollection * mapThemeCollection
Definition qgsproject.h:123
const QgsLayoutManager * layoutManager() const
Returns the project's layout manager, which manages print layouts, atlases and reports within the pro...
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the registry.
A widget for selecting a projection.
@ CrsNotSet
Not set (hidden by default).
void crsChanged(const QgsCoordinateReferenceSystem &crs)
Emitted when the selected CRS is changed.
A store for object properties.
Qgis::PropertyType propertyType() const
Returns the property type.
A combo box which displays the list of connections registered for a given provider.
A combobox widget which displays the bands present in a raster layer.
void bandChanged(int band)
Emitted when the currently selected band changes.
static QString displayBandName(QgsRasterDataProvider *provider, int band)
Returns a user-friendly band name for the specified band.
Base class for raster data providers.
virtual int bandCount() const =0
Gets number of bands.
static QList< QgsRasterReliefColor > calculateOptimizedReliefClasses(QgsRasterDataProvider *provider, int band)
Calculates optimized relief class breaks according with the method of Buenzli (2011) using an iterati...
Represents a raster layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Defines elevation range and color for raster relief coloring.
A rectangle specified with double values.
A QgsGeometry with associated coordinate reference system.
@ ICON_X
A cross is used to highlight points (x).
A combobox which lets the user select map scale from predefined list and highlights nearest to curren...
void scaleChanged(double scale)
Emitted when user has finished editing/selecting a new scale.
Stores settings for use within QGIS.
Definition qgssettings.h:68
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
A spin box with a clear button that will set the value to the defined clear value.
Definition qgsspinbox.h:45
A QTimeEdit widget with the capability of setting/reading null date/times.
void timeValueChanged(const QTime &time)
Signal emitted whenever the time changes.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
static Q_INVOKABLE double fromUnitToUnitFactor(Qgis::DistanceUnit fromUnit, Qgis::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
static Q_INVOKABLE Qgis::DistanceUnitType unitType(Qgis::DistanceUnit unit)
Returns the type for a distance unit.
Represents a vector layer which manages a vector based dataset.
QgsRectangle extent() const final
Returns the extent of the layer.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:7288
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:7475
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7381
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:7275
constexpr QObjectUniquePtr< Tp > make_qobject_unique(Args &&...args)
Create an object owned by a QObjectUniquePtr.
QString proj
Proj coordinate operation description, for Proj >= 6.0 builds only.