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