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