QGIS API Documentation 4.3.0-Master (0cfde48c85b)
Loading...
Searching...
No Matches
qgsprocessingalgorithmwidgetbase.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprocessingalgorithmdialogbase.cpp
3 ------------------------------------
4 Date : November 2017
5 Copyright : (C) 2017 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include <nlohmann/json.hpp>
19
24#include "qgsapplication.h"
26#include "qgsgui.h"
27#include "qgshelp.h"
28#include "qgsjsonutils.h"
29#include "qgsmessagebar.h"
30#include "qgsnative.h"
31#include "qgspanelwidget.h"
34#include "qgssettings.h"
35#include "qgsstringutils.h"
36#include "qgstaskmanager.h"
37#include "qgsunittypes.h"
38
39#include <QApplication>
40#include <QClipboard>
41#include <QDesktopServices>
42#include <QFileDialog>
43#include <QMainWindow>
44#include <QMenu>
45#include <QMimeData>
46#include <QScrollBar>
47#include <QString>
48#include <QToolButton>
49
50#include "moc_qgsprocessingalgorithmwidgetbase.cpp"
51
52using namespace Qt::StringLiterals;
53
55
56//
57// QgsProcessingFeedbackGenerator
58//
59
60QgsProcessingFeedbackGenerator::~QgsProcessingFeedbackGenerator()
61{}
62
63//
64// QgsProcessingAlgorithmWidgetBase
65//
66
67QgsProcessingAlgorithmWidgetBase::QgsProcessingAlgorithmWidgetBase(
68 QMainWindow *parentWindow, WidgetMode mode, QgsProcessingAlgorithmWidgetBase::WidgetFlags flags, Qgis::DockableWidgetInitialState initialState
69)
70 : QWidget()
71 , mMode( mode )
72{
73 setupUi( this );
74
75 //don't collapse parameters panel
76 splitter->setCollapsible( 0, false );
77
78 // add collapse button to splitter
79 QSplitterHandle *splitterHandle = splitter->handle( 1 );
80 QVBoxLayout *handleLayout = new QVBoxLayout();
81 handleLayout->setContentsMargins( 0, 0, 0, 0 );
82 mButtonCollapse = new QToolButton( splitterHandle );
83 mButtonCollapse->setAutoRaise( true );
84 mButtonCollapse->setFixedSize( 12, 12 );
85 mButtonCollapse->setCursor( Qt::ArrowCursor );
86 handleLayout->addWidget( mButtonCollapse );
87 handleLayout->addStretch();
88 splitterHandle->setLayout( handleLayout );
89
91
92 txtLog->setOpenLinks( false );
93 connect( txtLog, &QTextBrowser::anchorClicked, this, &QgsProcessingAlgorithmWidgetBase::urlClicked );
94
95 const QgsSettings settings;
96 splitter->restoreState( settings.value( u"/Processing/dialogBaseSplitter"_s, QByteArray() ).toByteArray() );
97 mSplitterState = splitter->saveState();
98 splitterChanged( 0, 0 );
99
100 // Rename OK button to Run
101 mButtonRun = mButtonBox->button( QDialogButtonBox::Ok );
102 mButtonRun->setText( tr( "Run" ) );
103
104 // Rename Yes button. Yes is used to ensure same position of Run and Change Parameters with respect to Close button.
105 mButtonChangeParameters = mButtonBox->button( QDialogButtonBox::Yes );
106 mButtonChangeParameters->setText( tr( "Change Parameters" ) );
107
108 connect( buttonCancel, &QPushButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::cancel );
109 buttonCancel->setEnabled( false );
110 mButtonClose = mButtonBox->button( QDialogButtonBox::Close );
111
112 if ( !parentWindow )
113 parentWindow = qobject_cast< QMainWindow * >( QApplication::activeWindow() );
114
115 bool defaultIsDocked = false;
116 QString dockId = u"ProcessingAlgorithm"_s;
117 if ( flags.testFlags( QgsProcessingAlgorithmWidgetBase::WidgetFlag::NoDocking ) )
118 {
120 dockId = u"ProcessingAlgorithmNonDockable"_s;
121 }
122 else if ( initialState == Qgis::DockableWidgetInitialState::ForceDocked )
123 {
124 dockId = u"ProcessingAlgorithmForceDocked"_s;
125 }
126
127 mDockableWidgetHelper
128 = new QgsDockableWidgetHelper( tr( "Processing" ), this, parentWindow, dockId, QStringList(), initialState, defaultIsDocked, Qt::DockWidgetArea::RightDockWidgetArea, QgsDockableWidgetHelper::Option::RaiseTab );
129 connect( mDockableWidgetHelper, &QgsDockableWidgetHelper::closed, this, &QgsProcessingAlgorithmWidgetBase::closeClicked );
130
131 switch ( mMode )
132 {
133 case QgsProcessingAlgorithmWidgetBase::WidgetMode::Single:
134 {
135 mAdvancedButton = new QPushButton( tr( "Advanced" ) );
136 mAdvancedMenu = new QMenu( this );
137 mAdvancedButton->setMenu( mAdvancedMenu );
138
139 mContextSettingsAction = new QAction( tr( "Algorithm Settings…" ), mAdvancedMenu );
140 mContextSettingsAction->setIcon( QgsApplication::getThemeIcon( u"/propertyicons/settings.svg"_s ) );
141 mAdvancedMenu->addAction( mContextSettingsAction );
142
143 connect( mContextSettingsAction, &QAction::triggered, this, [this] {
144 if ( QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( mMainWidget ) )
145 {
146 mTabWidget->setCurrentIndex( 0 );
147
148 if ( !mContextOptionsWidget )
149 {
150 mContextOptionsWidget = new QgsProcessingContextOptionsWidget();
151 mContextOptionsWidget->setFromContext( processingContext() );
152 mContextOptionsWidget->setLogLevel( mLogLevel );
153 panel->openPanel( mContextOptionsWidget );
154
155 connect( mContextOptionsWidget, &QgsPanelWidget::changed, this, [this] {
156 mOverrideDefaultContextSettings = true;
157 mGeometryCheck = mContextOptionsWidget->invalidGeometryCheck();
158 mDistanceUnits = mContextOptionsWidget->distanceUnit();
159 mAreaUnits = mContextOptionsWidget->areaUnit();
160 mTemporaryFolderOverride = mContextOptionsWidget->temporaryFolder();
161 mMaximumThreads = mContextOptionsWidget->maximumThreads();
162 mLogLevel = mContextOptionsWidget->logLevel();
163 } );
164 }
165 }
166 } );
167 mAdvancedMenu->addSeparator();
168
169 QAction *copyAsPythonCommand = new QAction( tr( "Copy as Python Command" ), mAdvancedMenu );
170 copyAsPythonCommand->setIcon( QgsApplication::getThemeIcon( u"mIconPythonFile.svg"_s ) );
171
172 mAdvancedMenu->addAction( copyAsPythonCommand );
173 connect( copyAsPythonCommand, &QAction::triggered, this, [this] {
174 if ( const QgsProcessingAlgorithm *alg = algorithm() )
175 {
176 QgsProcessingContext *context = processingContext();
177 if ( !context )
178 return;
179
180 const QString command = alg->asPythonCommand( createProcessingParameters(), *context );
181 QMimeData *m = new QMimeData();
182 m->setText( command );
183 QClipboard *cb = QApplication::clipboard();
184
185 if ( cb->supportsSelection() )
186 {
187 cb->setMimeData( m, QClipboard::Selection );
188 }
189 cb->setMimeData( m, QClipboard::Clipboard );
190 }
191 } );
192
193 mCopyAsQgisProcessCommand = new QAction( tr( "Copy as qgis_process Command" ), mAdvancedMenu );
194 mCopyAsQgisProcessCommand->setIcon( QgsApplication::getThemeIcon( u"mActionTerminal.svg"_s ) );
195 mAdvancedMenu->addAction( mCopyAsQgisProcessCommand );
196
197 connect( mCopyAsQgisProcessCommand, &QAction::triggered, this, [this] {
198 if ( const QgsProcessingAlgorithm *alg = algorithm() )
199 {
200 QgsProcessingContext *context = processingContext();
201 if ( !context )
202 return;
203
204 bool ok = false;
205 const QString command = alg->asQgisProcessCommand( createProcessingParameters(), *context, ok );
206 if ( !ok )
207 {
208 mMessageBar->pushMessage( tr( "Current settings cannot be specified as arguments to qgis_process (Pipe parameters as JSON to qgis_process instead)" ), Qgis::MessageLevel::Warning );
209 }
210 else
211 {
212 QMimeData *m = new QMimeData();
213 m->setText( command );
214 QClipboard *cb = QApplication::clipboard();
215
216 if ( cb->supportsSelection() )
217 {
218 cb->setMimeData( m, QClipboard::Selection );
219 }
220 cb->setMimeData( m, QClipboard::Clipboard );
221 }
222 }
223 } );
224
225 mAdvancedMenu->addSeparator();
226
227 QAction *copyAsJson = new QAction( tr( "Copy as JSON" ), mAdvancedMenu );
228 copyAsJson->setIcon( QgsApplication::getThemeIcon( u"mActionEditCopy.svg"_s ) );
229
230 mAdvancedMenu->addAction( copyAsJson );
231 connect( copyAsJson, &QAction::triggered, this, [this] {
232 if ( const QgsProcessingAlgorithm *alg = algorithm() )
233 {
234 QgsProcessingContext *context = processingContext();
235 if ( !context )
236 return;
237
238 const QVariantMap properties = alg->asMap( createProcessingParameters(), *context );
239 const QString json = QString::fromStdString( QgsJsonUtils::jsonFromVariant( properties ).dump( 2 ) );
240
241 QMimeData *m = new QMimeData();
242 m->setText( json );
243 QClipboard *cb = QApplication::clipboard();
244
245 if ( cb->supportsSelection() )
246 {
247 cb->setMimeData( m, QClipboard::Selection );
248 }
249 cb->setMimeData( m, QClipboard::Clipboard );
250 }
251 } );
252
253 mPasteJsonAction = new QAction( tr( "Paste Settings" ), mAdvancedMenu );
254 mPasteJsonAction->setIcon( QgsApplication::getThemeIcon( u"mActionEditPaste.svg"_s ) );
255
256 mAdvancedMenu->addAction( mPasteJsonAction );
257 connect( mPasteJsonAction, &QAction::triggered, this, [this] {
258 const QString text = QApplication::clipboard()->text();
259 if ( text.isEmpty() )
260 return;
261
262 const QVariantMap parameterValues = QgsJsonUtils::parseJson( text ).toMap().value( u"inputs"_s ).toMap();
263 if ( parameterValues.isEmpty() )
264 return;
265
266 bool ok = false;
267 QString error;
268 const QVariantMap preparedValues = QgsProcessingUtils::preprocessQgisProcessParameters( parameterValues, ok, error );
269
270 setParameters( preparedValues );
271 } );
272
273 mButtonBox->addButton( mAdvancedButton, QDialogButtonBox::ResetRole );
274 break;
275 }
276
277 case QgsProcessingAlgorithmWidgetBase::WidgetMode::Batch:
278 break;
279 }
280
281 if ( mAdvancedMenu )
282 {
283 connect( mAdvancedMenu, &QMenu::aboutToShow, this, [this] {
284 mCopyAsQgisProcessCommand->setEnabled( algorithm() && !( algorithm()->flags() & Qgis::ProcessingAlgorithmFlag::NotAvailableInStandaloneTool ) );
285 mPasteJsonAction->setEnabled( !QApplication::clipboard()->text().isEmpty() );
286 } );
287 }
288
289 connect( mButtonRun, &QPushButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::runAlgorithm );
290 connect( mButtonChangeParameters, &QPushButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::showParameters );
291 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsProcessingAlgorithmWidgetBase::closeClicked );
292 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsProcessingAlgorithmWidgetBase::openHelp );
293 connect( mButtonCollapse, &QToolButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::toggleCollapsed );
294 connect( splitter, &QSplitter::splitterMoved, this, &QgsProcessingAlgorithmWidgetBase::splitterChanged );
295
296 connect( mButtonSaveLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::saveLog );
297 connect( mButtonCopyLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::copyLogToClipboard );
298 connect( mButtonClearLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::clearLog );
299
300 connect( mTabWidget, &QTabWidget::currentChanged, this, &QgsProcessingAlgorithmWidgetBase::mTabWidget_currentChanged );
301
302 mMessageBar = new QgsMessageBar();
303 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
304 verticalLayout->insertWidget( 0, mMessageBar );
305
306 connect( QgsApplication::taskManager(), &QgsTaskManager::taskTriggered, this, &QgsProcessingAlgorithmWidgetBase::taskTriggered );
307}
308
309QgsProcessingAlgorithmWidgetBase::~QgsProcessingAlgorithmWidgetBase()
310{
311 delete mDockableWidgetHelper;
312}
313
314void QgsProcessingAlgorithmWidgetBase::setParameters( const QVariantMap & )
315{}
316
317void QgsProcessingAlgorithmWidgetBase::setTitle( const QString &title )
318{
319 mDockableWidgetHelper->setWindowTitle( title );
320}
321
322void QgsProcessingAlgorithmWidgetBase::exec()
323{
324 // when forcing the widget to show as a dialog, we use a distinct setting key
325 // to prevent the setting for freely dockable algorithm widgets from getting
326 // overridden, which would otherwise reset that setting so that the widgets
327 // are ALWAYS opened as dialogs
328 mDockableWidgetHelper->setSettingKeyDockId( u"ProcessingAlgorithmNonDockable"_s );
329 mDockableWidgetHelper->toggleDockMode( false );
330 mDockableWidgetHelper->dialog()->exec();
331}
332
333void QgsProcessingAlgorithmWidgetBase::setAlgorithm( QgsProcessingAlgorithm *algorithm )
334{
335 mAlgorithm.reset( algorithm );
336 QString title;
338 {
339 title = mAlgorithm->group().isEmpty()
340 ? QgsStringUtils::capitalize( mAlgorithm->displayName(), Qgis::Capitalization::TitleCase )
341 : u"%1 - %2"_s.arg( QgsStringUtils::capitalize( mAlgorithm->group(), Qgis::Capitalization::TitleCase ), QgsStringUtils::capitalize( mAlgorithm->displayName(), Qgis::Capitalization::TitleCase ) );
342 }
343 else
344 {
345 title = mAlgorithm->group().isEmpty() ? mAlgorithm->displayName() : u"%1 - %2"_s.arg( mAlgorithm->group(), mAlgorithm->displayName() );
346 }
347 mDockableWidgetHelper->setWindowTitle( title );
348
349 const QString algHelp = formatHelp( algorithm );
350 if ( algHelp.isEmpty() )
351 textShortHelp->hide();
352 else
353 {
354 textShortHelp->document()->setDefaultStyleSheet( QStringLiteral(
355 ".summary { margin-left: 10px; margin-right: 10px; }\n"
356 "h2 { color: #555555; padding-bottom: 15px; }\n"
357 "a { text - decoration: none; color: #3498db; font-weight: bold; }\n"
358 "p, ul, li { color: #666666; }\n"
359 "b { color: #333333; }\n"
360 "dl dd { margin - bottom: 5px; }"
361 ) );
362 textShortHelp->setHtml( algHelp );
363 connect( textShortHelp, &QTextBrowser::anchorClicked, this, &QgsProcessingAlgorithmWidgetBase::linkClicked );
364 textShortHelp->show();
365 }
366
367 if ( algorithm->helpUrl().isEmpty() && ( !algorithm->provider() || algorithm->provider()->helpId().isEmpty() ) )
368 {
369 mButtonBox->removeButton( mButtonBox->button( QDialogButtonBox::Help ) );
370 }
371
372 const QString warning = algorithm->provider() ? algorithm->provider()->warningMessage() : QString();
373 if ( !warning.isEmpty() )
374 {
375 mMessageBar->pushMessage( warning, Qgis::MessageLevel::Warning );
376 }
377}
378
379QgsProcessingAlgorithm *QgsProcessingAlgorithmWidgetBase::algorithm()
380{
381 return mAlgorithm.get();
382}
383
384void QgsProcessingAlgorithmWidgetBase::setMainWidget( QgsPanelWidget *widget )
385{
386 if ( mMainWidget )
387 {
388 mMainWidget->deleteLater();
389 }
390
391 mPanelStack->setMainPanel( widget );
392 widget->setDockMode( true );
393
394 mMainWidget = widget;
395 connect( mMainWidget, &QgsPanelWidget::panelAccepted, mDockableWidgetHelper, &QgsDockableWidgetHelper::reject );
396}
397
398QgsPanelWidget *QgsProcessingAlgorithmWidgetBase::mainWidget()
399{
400 return mMainWidget;
401}
402
403void QgsProcessingAlgorithmWidgetBase::saveLogToFile( const QString &path, const LogFormat format )
404{
405 QFile logFile( path );
406 if ( !logFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
407 {
408 return;
409 }
410 QTextStream fout( &logFile );
411
412 switch ( format )
413 {
414 case QgsProcessingAlgorithmWidgetBase::LogFormat::FormatPlainText:
415 fout << txtLog->toPlainText();
416 break;
417
418 case QgsProcessingAlgorithmWidgetBase::LogFormat::FormatHtml:
419 fout << txtLog->toHtml();
420 break;
421 }
422}
423
424void QgsProcessingAlgorithmWidgetBase::registerProcessingFeedbackGenerator( QgsProcessingFeedbackGenerator *factory )
425{
426 mFeedbackFactory = factory;
427}
428
429QgsProcessingFeedback *QgsProcessingAlgorithmWidgetBase::createFeedback()
430{
431 std::unique_ptr< QgsProcessingFeedback > feedback;
432 if ( mFeedbackFactory )
433 {
434 feedback.reset( mFeedbackFactory->createFeedback() );
435 }
436 if ( !feedback )
437 {
438 feedback = std::make_unique< QgsProcessingFeedback >();
439 }
440 connect( feedback.get(), &QgsProcessingFeedback::progressChanged, this, &QgsProcessingAlgorithmWidgetBase::setPercentage );
441 connect( feedback.get(), &QgsProcessingFeedback::commandInfoPushed, this, &QgsProcessingAlgorithmWidgetBase::pushCommandInfo );
442 connect( feedback.get(), &QgsProcessingFeedback::consoleInfoPushed, this, &QgsProcessingAlgorithmWidgetBase::pushConsoleInfo );
443 connect( feedback.get(), &QgsProcessingFeedback::debugInfoPushed, this, &QgsProcessingAlgorithmWidgetBase::pushDebugInfo );
444 connect( feedback.get(), &QgsProcessingFeedback::errorReported, this, &QgsProcessingAlgorithmWidgetBase::reportError );
445 connect( feedback.get(), &QgsProcessingFeedback::warningPushed, this, &QgsProcessingAlgorithmWidgetBase::pushWarning );
446 connect( feedback.get(), &QgsProcessingFeedback::infoPushed, this, &QgsProcessingAlgorithmWidgetBase::pushInfo );
447 connect( feedback.get(), &QgsProcessingFeedback::formattedMessagePushed, this, &QgsProcessingAlgorithmWidgetBase::pushFormattedMessage );
448 connect( feedback.get(), &QgsProcessingFeedback::progressTextChanged, this, &QgsProcessingAlgorithmWidgetBase::setProgressText );
449 connect( this, &QgsProcessingAlgorithmWidgetBase::cancelRequested, feedback.get(), &QgsProcessingFeedback::cancel );
450 return feedback.release();
451}
452
453QDialogButtonBox *QgsProcessingAlgorithmWidgetBase::buttonBox()
454{
455 return mButtonBox;
456}
457
458QTabWidget *QgsProcessingAlgorithmWidgetBase::tabWidget()
459{
460 return mTabWidget;
461}
462
463void QgsProcessingAlgorithmWidgetBase::showLog()
464{
465 mTabWidget->setCurrentIndex( 1 );
466}
467
468void QgsProcessingAlgorithmWidgetBase::showParameters()
469{
470 mTabWidget->setCurrentIndex( 0 );
471}
472
473QPushButton *QgsProcessingAlgorithmWidgetBase::runButton()
474{
475 return mButtonRun;
476}
477
478QPushButton *QgsProcessingAlgorithmWidgetBase::cancelButton()
479{
480 return buttonCancel;
481}
482
483QPushButton *QgsProcessingAlgorithmWidgetBase::changeParametersButton()
484{
485 return mButtonChangeParameters;
486}
487
488void QgsProcessingAlgorithmWidgetBase::clearProgress()
489{
490 progressBar->setMaximum( 0 );
491}
492
493void QgsProcessingAlgorithmWidgetBase::setExecuted( bool executed )
494{
495 mExecuted = executed;
496}
497
498void QgsProcessingAlgorithmWidgetBase::setExecutedAnyResult( bool executedAnyResult )
499{
500 mExecutedAnyResult = executedAnyResult;
501}
502
503void QgsProcessingAlgorithmWidgetBase::setResults( const QVariantMap &results )
504{
505 mResults = results;
506}
507
508void QgsProcessingAlgorithmWidgetBase::finished( bool, const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
509{}
510
511void QgsProcessingAlgorithmWidgetBase::openHelp()
512{
513 QUrl algHelp = mAlgorithm->helpUrl();
514 if ( algHelp.isEmpty() && mAlgorithm->provider() && !mAlgorithm->provider()->helpId().isEmpty() )
515 {
516 algHelp = QgsHelp::helpUrl(
517 u"processing_algs/%1/%2.html#%3"_s.arg( mAlgorithm->provider()->helpId(), mAlgorithm->groupId(), u"%1%2"_s.arg( mAlgorithm->provider()->helpId() ).arg( mAlgorithm->name().replace( "_", "-" ) ) )
518 );
519 }
520
521 if ( !algHelp.isEmpty() )
522 QDesktopServices::openUrl( algHelp );
523}
524
525void QgsProcessingAlgorithmWidgetBase::toggleCollapsed()
526{
527 if ( mHelpCollapsed )
528 {
529 splitter->restoreState( mSplitterState );
530 mButtonCollapse->setArrowType( Qt::RightArrow );
531 }
532 else
533 {
534 mSplitterState = splitter->saveState();
535 splitter->setSizes( QList<int>() << 1 << 0 );
536 mButtonCollapse->setArrowType( Qt::LeftArrow );
537 }
538 mHelpCollapsed = !mHelpCollapsed;
539}
540
541void QgsProcessingAlgorithmWidgetBase::splitterChanged( int, int )
542{
543 if ( splitter->sizes().at( 1 ) == 0 )
544 {
545 mHelpCollapsed = true;
546 mButtonCollapse->setArrowType( Qt::LeftArrow );
547 }
548 else
549 {
550 mHelpCollapsed = false;
551 mButtonCollapse->setArrowType( Qt::RightArrow );
552 }
553}
554
555void QgsProcessingAlgorithmWidgetBase::mTabWidget_currentChanged( int )
556{
557 updateRunButtonVisibility();
558}
559
560void QgsProcessingAlgorithmWidgetBase::linkClicked( const QUrl &url )
561{
562 if ( url.toString() == "#help"_L1 )
563 {
564 openHelp();
565 return;
566 }
567
568 QDesktopServices::openUrl( url.toString() );
569}
570
571void QgsProcessingAlgorithmWidgetBase::algExecuted( bool successful, const QVariantMap & )
572{
573 mAlgorithmTask = nullptr;
574
575 if ( !successful )
576 {
577 // show widget to display errors
578 showWidget();
579 showLog();
580 }
581 else
582 {
583 if ( isFinalized() && successful )
584 {
585 progressBar->setFormat( tr( "Complete" ) );
586 }
587
588 // delete widget if closed
589 if ( isFinalized() && !isVisible() )
590 {
591 deleteLater();
592 }
593 }
594}
595
596void QgsProcessingAlgorithmWidgetBase::taskTriggered( QgsTask *task )
597{
598 if ( task == mAlgorithmTask )
599 {
600 showWidget();
601 showLog();
602 }
603}
604
605void QgsProcessingAlgorithmWidgetBase::showWidget()
606{
607 mDockableWidgetHelper->setUserVisible( true );
608}
609
610void QgsProcessingAlgorithmWidgetBase::closeClicked()
611{
612 disconnect( mDockableWidgetHelper, &QgsDockableWidgetHelper::closed, this, &QgsProcessingAlgorithmWidgetBase::closeClicked );
613
614 if ( isRunning() )
615 {
616 mDockableWidgetHelper->setUserVisible( false );
617 }
618 else
619 {
620 reject();
621 close();
622 }
623}
624
625void QgsProcessingAlgorithmWidgetBase::urlClicked( const QUrl &url )
626{
627 const QFileInfo file( url.toLocalFile() );
628 if ( file.exists() && !file.isDir() )
629 {
630 if ( file.suffix().compare( "html"_L1, Qt::CaseInsensitive ) == 0 )
631 {
632 QDesktopServices::openUrl( url );
633 }
634 else
635 {
636 QgsGui::nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
637 }
638 }
639 else
640 QDesktopServices::openUrl( url );
641}
642
643Qgis::ProcessingLogLevel QgsProcessingAlgorithmWidgetBase::logLevel() const
644{
645 return mLogLevel;
646}
647
648void QgsProcessingAlgorithmWidgetBase::setLogLevel( Qgis::ProcessingLogLevel level )
649{
650 mLogLevel = level;
651}
652
653void QgsProcessingAlgorithmWidgetBase::reportError( const QString &error, bool fatalError )
654{
655 setInfo( error, true );
656 if ( fatalError )
657 resetGui();
658 showLog();
659 processEvents();
660}
661
662void QgsProcessingAlgorithmWidgetBase::pushWarning( const QString &warning )
663{
664 setInfo( warning, false, true, true );
665 processEvents();
666}
667
668void QgsProcessingAlgorithmWidgetBase::pushInfo( const QString &info )
669{
670 setInfo( info );
671 processEvents();
672}
673
674void QgsProcessingAlgorithmWidgetBase::pushFormattedMessage( const QString &html )
675{
676 setInfo( html, false, false );
677 processEvents();
678}
679
680void QgsProcessingAlgorithmWidgetBase::pushCommandInfo( const QString &command )
681{
682 txtLog->append( u"<code>%1<code>"_s.arg( formatStringForLog( command.toHtmlEscaped() ) ) );
683 scrollToBottomOfLog();
684 processEvents();
685}
686
687void QgsProcessingAlgorithmWidgetBase::pushDebugInfo( const QString &message )
688{
689 txtLog->append( u"<span style=\"color:#777\">%1</span>"_s.arg( formatStringForLog( message.toHtmlEscaped() ) ) );
690 scrollToBottomOfLog();
691 processEvents();
692}
693
694void QgsProcessingAlgorithmWidgetBase::pushConsoleInfo( const QString &info )
695{
696 txtLog->append( u"<code style=\"color:#777\">%1</code>"_s.arg( formatStringForLog( info.toHtmlEscaped() ) ) );
697 scrollToBottomOfLog();
698 processEvents();
699}
700
701QDialog *QgsProcessingAlgorithmWidgetBase::createProgressDialog()
702{
703 QgsProcessingAlgorithmProgressDialog *dialog = new QgsProcessingAlgorithmProgressDialog( this );
704 dialog->setWindowModality( Qt::ApplicationModal );
705 dialog->setWindowTitle( windowTitle() );
706 dialog->setGeometry( geometry() ); // match size/position to this dialog
707 connect( progressBar, &QProgressBar::valueChanged, dialog->progressBar(), &QProgressBar::setValue );
708 connect( dialog->cancelButton(), &QPushButton::clicked, this, &QgsProcessingAlgorithmWidgetBase::cancel );
709 dialog->logTextEdit()->setHtml( txtLog->toHtml() );
710 connect( txtLog, &QTextEdit::textChanged, dialog, [this, dialog]() {
711 dialog->logTextEdit()->setHtml( txtLog->toHtml() );
712 QScrollBar *sb = dialog->logTextEdit()->verticalScrollBar();
713 sb->setValue( sb->maximum() );
714 } );
715 return dialog;
716}
717
718void QgsProcessingAlgorithmWidgetBase::clearLog()
719{
720 txtLog->clear();
721}
722
723void QgsProcessingAlgorithmWidgetBase::saveLog()
724{
725 QgsSettings settings;
726 const QString lastUsedDir = settings.value( u"/Processing/lastUsedLogDirectory"_s, QDir::homePath() ).toString();
727
728 QString filter;
729 const QString txtExt = tr( "Text files" ) + u" (*.txt *.TXT)"_s;
730 const QString htmlExt = tr( "HTML files" ) + u" (*.html *.HTML)"_s;
731
732 const QString path = QFileDialog::getSaveFileName( this, tr( "Save Log to File" ), lastUsedDir, txtExt + ";;" + htmlExt, &filter );
733 // return dialog focus on Mac
734 activateWindow();
735 raise();
736 if ( path.isEmpty() )
737 {
738 return;
739 }
740
741 settings.setValue( u"/Processing/lastUsedLogDirectory"_s, QFileInfo( path ).path() );
742
743 LogFormat format = QgsProcessingAlgorithmWidgetBase::LogFormat::FormatPlainText;
744 if ( filter == htmlExt )
745 {
746 format = QgsProcessingAlgorithmWidgetBase::LogFormat::FormatHtml;
747 }
748 saveLogToFile( path, format );
749}
750
751void QgsProcessingAlgorithmWidgetBase::copyLogToClipboard()
752{
753 QMimeData *m = new QMimeData();
754 m->setText( txtLog->toPlainText() );
755 m->setHtml( txtLog->toHtml() );
756 QClipboard *cb = QApplication::clipboard();
757
758 if ( cb->supportsSelection() )
759 {
760 cb->setMimeData( m, QClipboard::Selection );
761 }
762 cb->setMimeData( m, QClipboard::Clipboard );
763}
764
765void QgsProcessingAlgorithmWidgetBase::closeEvent( QCloseEvent *e )
766{
767 if ( !mHelpCollapsed )
768 {
769 QgsSettings settings;
770 settings.setValue( u"/Processing/dialogBaseSplitter"_s, splitter->saveState() );
771 }
772
773 QWidget::closeEvent( e );
774
775 if ( !mAlgorithmTask && isFinalized() )
776 {
777 // when running a background task, the dialog is kept around and deleted only when the task
778 // completes. But if not running a task, we auto cleanup (later - gotta give callers a chance
779 // to retrieve results and execution status).
780 deleteLater();
781 }
782}
783
784void QgsProcessingAlgorithmWidgetBase::runAlgorithm()
785{}
786
787void QgsProcessingAlgorithmWidgetBase::setPercentage( double percent )
788{
789 // delay setting maximum progress value until we know algorithm reports progress
790 if ( progressBar->maximum() == 0 )
791 progressBar->setMaximum( 100 );
792 progressBar->setValue( percent );
793 processEvents();
794}
795
796void QgsProcessingAlgorithmWidgetBase::setProgressText( const QString &text )
797{
798 lblProgress->setText( text );
799 setInfo( text, false );
800 scrollToBottomOfLog();
801 processEvents();
802}
803
804QString QgsProcessingAlgorithmWidgetBase::formatHelp( QgsProcessingAlgorithm *algorithm )
805{
806 QString result;
807 const QString text = algorithm->shortHelpString();
808 if ( !text.isEmpty() )
809 {
810 const QStringList paragraphs = text.split( '\n' );
811 QString help;
812 for ( const QString &paragraph : paragraphs )
813 {
814 help += u"<p>%1</p>"_s.arg( paragraph );
815 }
816 result = u"<h2>%1</h2>%2"_s.arg( algorithm->displayName(), help );
817 }
818 else if ( !algorithm->shortDescription().isEmpty() )
819 {
820 result = u"<h2>%1</h2><p>%2</p>"_s.arg( algorithm->displayName(), algorithm->shortDescription() );
821 }
822
823 const QList< QgsAcademicReference > references = algorithm->academicReferences();
824 if ( !references.empty() )
825 {
826 QStringList referenceStrings;
827 for ( const QgsAcademicReference &reference : references )
828 {
829 referenceStrings << reference.asHtml();
830 }
831 result += u"<h4>%1</h4>"_s.arg( tr( "References" ) );
832 result += u"<ul><li>%1</li></ul>"_s.arg( referenceStrings.join( "</li><li>"_L1 ) );
833 }
834
835 if ( algorithm->documentationFlags() != Qgis::ProcessingAlgorithmDocumentationFlags() )
836 {
837 QStringList flags;
839 {
840 if ( algorithm->documentationFlags() & flag )
841 {
843 }
844 }
845 result += u"<ul><li><i>%1</i></li></ul>"_s.arg( flags.join( "</i></li><li><i>"_L1 ) );
846 }
848 {
849 result += u"<p><b>%1</b></p>"_s.arg( tr( "Warning: This algorithm is a potential security risk if executed with unchecked inputs, and may result in system damage or data leaks." ) );
850 }
852 {
853 result += u"<p><b>%1</b></p>"_s.arg( tr( "Warning: This algorithm has known issues. The results must be carefully validated by the user." ) );
854 }
855
856 QStringList links;
857 if ( !algorithm->helpUrl().isEmpty() || ( algorithm->provider() && !algorithm->provider()->helpId().isEmpty() ) )
858 {
859 // DO NOT resolve the help url here using QgsHelp::helpUrl -- that is VERY slow as it triggers a network
860 // request. Defer this until the link is actually clicked.
861 const QString linkHtml = QStringLiteral( R"(<a href="#help">%1</a>)" ).arg( tr( "Algorithm documentation" ) );
862 links << linkHtml;
863 }
864
865 const QString implementationSourceUri = algorithm->implementationSourceUri();
866 if ( !implementationSourceUri.isEmpty() )
867 {
868 const QString linkHtml = QStringLiteral( R"(<a href="%1">%2</a>)" ).arg( implementationSourceUri, tr( "Algorithm source code" ) );
869 links << linkHtml;
870 }
871
872 const QList< QgsProcessingAlgorithm::ExternalLink > externalLinks = algorithm->externalLinks();
873 for ( const QgsProcessingAlgorithm::ExternalLink &link : externalLinks )
874 {
875 const QString linkHtml = QStringLiteral( R"(<a href="%1">%2</a>)" ).arg( link.url, link.description );
876 links << linkHtml;
877 }
878
879 if ( !links.empty() )
880 {
881 result += u"<h4>%1</h4>"_s.arg( tr( "Links" ) );
882 result += u"<ul><li>%1</li></ul>"_s.arg( links.join( "</li><li>"_L1 ) );
883 }
884
885 return result;
886}
887
888void QgsProcessingAlgorithmWidgetBase::processEvents()
889{
890 if ( mAlgorithmTask )
891 {
892 // no need to call this - the algorithm is running in a thread.
893 // in fact, calling it causes a crash on Windows when the algorithm
894 // is running in a background thread... unfortunately we need something
895 // like this for non-threadable algorithms, otherwise there's no chance
896 // for users to hit cancel or see progress updates...
897 return;
898 }
899
900 // So that we get a chance of hitting the Abort button
901#ifdef Q_OS_LINUX
902 // One iteration is actually enough on Windows to get good interactivity
903 // whereas on Linux we must allow for far more iterations.
904 // For safety limit the number of iterations
905 int nIters = 0;
906 while ( ++nIters < 100 )
907#endif
908 {
909 QCoreApplication::processEvents();
910 }
911}
912
913void QgsProcessingAlgorithmWidgetBase::scrollToBottomOfLog()
914{
915 QScrollBar *sb = txtLog->verticalScrollBar();
916 sb->setValue( sb->maximum() );
917}
918
919void QgsProcessingAlgorithmWidgetBase::resetGui()
920{
921 lblProgress->clear();
922 progressBar->setMaximum( 100 );
923 progressBar->setValue( 0 );
924 mButtonRun->setEnabled( true );
925 mButtonChangeParameters->setEnabled( true );
926 mButtonClose->setEnabled( true );
927 if ( mMainWidget )
928 {
929 mMainWidget->setEnabled( true );
930 }
931 updateRunButtonVisibility();
932 resetAdditionalGui();
933}
934
935void QgsProcessingAlgorithmWidgetBase::updateRunButtonVisibility()
936{
937 // Activate run button if current tab is Parameters
938 const bool runButtonVisible = mTabWidget->currentIndex() == 0;
939 mButtonRun->setVisible( runButtonVisible );
940 if ( runButtonVisible )
941 progressBar->resetFormat();
942 mButtonChangeParameters->setVisible( !runButtonVisible && mExecutedAnyResult && mButtonChangeParameters->isEnabled() );
943}
944
945void QgsProcessingAlgorithmWidgetBase::resetAdditionalGui()
946{}
947
948void QgsProcessingAlgorithmWidgetBase::blockControlsWhileRunning()
949{
950 mButtonRun->setEnabled( false );
951 mButtonChangeParameters->setEnabled( false );
952 if ( mMainWidget )
953 {
954 mMainWidget->setEnabled( false );
955 }
956 blockAdditionalControlsWhileRunning();
957}
958
959void QgsProcessingAlgorithmWidgetBase::blockAdditionalControlsWhileRunning()
960{}
961
962QgsMessageBar *QgsProcessingAlgorithmWidgetBase::messageBar()
963{
964 return mMessageBar;
965}
966
967void QgsProcessingAlgorithmWidgetBase::hideShortHelp()
968{
969 textShortHelp->setVisible( false );
970}
971
972void QgsProcessingAlgorithmWidgetBase::setCurrentTask( QgsProcessingAlgRunnerTask *task )
973{
974 mAlgorithmTask = task;
975 connect( mAlgorithmTask, &QgsProcessingAlgRunnerTask::executed, this, &QgsProcessingAlgorithmWidgetBase::algExecuted );
976 QgsApplication::taskManager()->addTask( mAlgorithmTask );
977}
978
979void QgsProcessingAlgorithmWidgetBase::disconnectCurrentTask()
980{
981 if ( mAlgorithmTask )
982 {
983 disconnect( mAlgorithmTask, &QgsProcessingAlgRunnerTask::executed, this, &QgsProcessingAlgorithmWidgetBase::algExecuted );
984 mAlgorithmTask = nullptr;
985 }
986}
987
988QString QgsProcessingAlgorithmWidgetBase::formatStringForLog( const QString &string )
989{
990 QString s = string;
991 s.replace( '\n', "<br>"_L1 );
992 return s;
993}
994
995bool QgsProcessingAlgorithmWidgetBase::isFinalized()
996{
997 return true;
998}
999
1000bool QgsProcessingAlgorithmWidgetBase::isRunning()
1001{
1002 return false;
1003}
1004
1005void QgsProcessingAlgorithmWidgetBase::cancel()
1006{
1007 emit cancelRequested();
1008}
1009
1010void QgsProcessingAlgorithmWidgetBase::applyContextOverrides( QgsProcessingContext *context )
1011{
1012 if ( !context )
1013 return;
1014
1015 context->setLogLevel( logLevel() );
1016
1017 if ( mOverrideDefaultContextSettings )
1018 {
1019 context->setInvalidGeometryCheck( mGeometryCheck );
1020 context->setDistanceUnit( mDistanceUnits );
1021 context->setAreaUnit( mAreaUnits );
1022 context->setTemporaryFolder( mTemporaryFolderOverride );
1023 context->setMaximumThreads( mMaximumThreads );
1024 }
1025}
1026
1027void QgsProcessingAlgorithmWidgetBase::setInfo( const QString &message, bool isError, bool escapeHtml, bool isWarning )
1028{
1029 constexpr int MESSAGE_COUNT_LIMIT = 10000;
1030 // Avoid logging too many messages, which might blow memory.
1031 if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
1032 return;
1033 ++mMessageLoggedCount;
1034
1035 // note -- we have to wrap the message in a span block, or QTextEdit::append sometimes gets confused
1036 // and varies between treating it as a HTML string or a plain text string! (see https://github.com/qgis/QGIS/issues/37934)
1037 if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
1038 txtLog->append( u"<span style=\"color:red\">%1</span>"_s.arg( tr( "Message log truncated" ) ) );
1039 else if ( isError || isWarning )
1040 txtLog->append( u"<span style=\"color:%1\">%2</span>"_s.arg( isError ? u"red"_s : u"#b85a20"_s, escapeHtml ? formatStringForLog( message.toHtmlEscaped() ) : formatStringForLog( message ) ) );
1041 else if ( escapeHtml )
1042 txtLog->append( u"<span>%1</span"_s.arg( formatStringForLog( message.toHtmlEscaped() ) ) );
1043 else
1044 txtLog->append( u"<span>%1</span>"_s.arg( formatStringForLog( message ) ) );
1045 scrollToBottomOfLog();
1046 processEvents();
1047}
1048
1049void QgsProcessingAlgorithmWidgetBase::reject()
1050{
1051 if ( !mAlgorithmTask && isFinalized() )
1052 {
1053 setAttribute( Qt::WA_DeleteOnClose );
1054 }
1055
1056 mDockableWidgetHelper->reject();
1057}
1058
1059void QgsProcessingAlgorithmWidgetBase::forceClose()
1060{
1061 disconnectCurrentTask();
1062 reject();
1063}
1064
1065//
1066// QgsProcessingAlgorithmProgressDialog
1067//
1068
1069QgsProcessingAlgorithmProgressDialog::QgsProcessingAlgorithmProgressDialog( QWidget *parent )
1070 : QDialog( parent )
1071{
1072 setupUi( this );
1073}
1074
1075QProgressBar *QgsProcessingAlgorithmProgressDialog::progressBar()
1076{
1077 return mProgressBar;
1078}
1079
1080QPushButton *QgsProcessingAlgorithmProgressDialog::cancelButton()
1081{
1082 return mButtonBox->button( QDialogButtonBox::Cancel );
1083}
1084
1085QTextEdit *QgsProcessingAlgorithmProgressDialog::logTextEdit()
1086{
1087 return mTxtLog;
1088}
1089
1090void QgsProcessingAlgorithmProgressDialog::reject()
1091{}
1092
1093
1094//
1095// QgsProcessingContextOptionsWidget
1096//
1097
1098QgsProcessingContextOptionsWidget::QgsProcessingContextOptionsWidget( QWidget *parent )
1099 : QgsPanelWidget( parent )
1100{
1101 setupUi( this );
1102 setPanelTitle( tr( "Algorithm Settings" ) );
1103
1104 mComboInvalidFeatureFiltering->addItem( tr( "Do not Filter (Better Performance)" ), QVariant::fromValue( Qgis::InvalidGeometryCheck::NoCheck ) );
1105 mComboInvalidFeatureFiltering->addItem( tr( "Skip (Ignore) Features with Invalid Geometries" ), QVariant::fromValue( Qgis::InvalidGeometryCheck::SkipInvalid ) );
1106 mComboInvalidFeatureFiltering->addItem( tr( "Stop Algorithm Execution When a Geometry is Invalid" ), QVariant::fromValue( Qgis::InvalidGeometryCheck::AbortOnInvalid ) );
1107
1108 mTemporaryFolderWidget->setDialogTitle( tr( "Select Temporary Directory" ) );
1109 mTemporaryFolderWidget->setStorageMode( QgsFileWidget::GetDirectory );
1110 mTemporaryFolderWidget->lineEdit()->setPlaceholderText( tr( "Default" ) );
1111
1112 mLogLevelComboBox->addItem( tr( "Default" ), static_cast<int>( Qgis::ProcessingLogLevel::DefaultLevel ) );
1113 mLogLevelComboBox->addItem( tr( "Verbose" ), static_cast<int>( Qgis::ProcessingLogLevel::Verbose ) );
1114 mLogLevelComboBox->addItem( tr( "Verbose (Model Debugging)" ), static_cast<int>( Qgis::ProcessingLogLevel::ModelDebug ) );
1115
1116 mDistanceUnitsCombo->addItem( tr( "Default" ), QVariant::fromValue( Qgis::DistanceUnit::Unknown ) );
1117 for ( Qgis::DistanceUnit unit : {
1128 } )
1129 {
1130 QString title;
1132 {
1134 }
1135 else
1136 {
1137 title = QgsUnitTypes::toString( unit );
1138 }
1139
1140 mDistanceUnitsCombo->addItem( title, QVariant::fromValue( unit ) );
1141 }
1142
1143 mAreaUnitsCombo->addItem( tr( "Default" ), QVariant::fromValue( Qgis::AreaUnit::Unknown ) );
1144 for ( Qgis::AreaUnit unit : {
1157 } )
1158 {
1159 QString title;
1161 {
1163 }
1164 else
1165 {
1166 title = QgsUnitTypes::toString( unit );
1167 }
1168
1169 mAreaUnitsCombo->addItem( title, QVariant::fromValue( unit ) );
1170 }
1171
1172 mThreadsSpinBox->setRange( 1, QThread::idealThreadCount() );
1173
1174 connect( mLogLevelComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::changed );
1175 connect( mComboInvalidFeatureFiltering, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::changed );
1176 connect( mDistanceUnitsCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::changed );
1177 connect( mAreaUnitsCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::changed );
1178 connect( mTemporaryFolderWidget, &QgsFileWidget::fileChanged, this, &QgsPanelWidget::changed );
1179 connect( mThreadsSpinBox, qOverload<int>( &QSpinBox::valueChanged ), this, &QgsPanelWidget::changed );
1180}
1181
1182void QgsProcessingContextOptionsWidget::setFromContext( const QgsProcessingContext *context )
1183{
1184 whileBlocking( mComboInvalidFeatureFiltering )->setCurrentIndex( mComboInvalidFeatureFiltering->findData( QVariant::fromValue( context->invalidGeometryCheck() ) ) );
1185 whileBlocking( mDistanceUnitsCombo )->setCurrentIndex( mDistanceUnitsCombo->findData( QVariant::fromValue( context->distanceUnit() ) ) );
1186 whileBlocking( mAreaUnitsCombo )->setCurrentIndex( mAreaUnitsCombo->findData( QVariant::fromValue( context->areaUnit() ) ) );
1187 whileBlocking( mTemporaryFolderWidget )->setFilePath( context->temporaryFolder() );
1188 whileBlocking( mThreadsSpinBox )->setValue( context->maximumThreads() );
1189 whileBlocking( mLogLevelComboBox )->setCurrentIndex( mLogLevelComboBox->findData( static_cast<int>( context->logLevel() ) ) );
1190}
1191
1192Qgis::InvalidGeometryCheck QgsProcessingContextOptionsWidget::invalidGeometryCheck() const
1193{
1194 return mComboInvalidFeatureFiltering->currentData().value<Qgis::InvalidGeometryCheck>();
1195}
1196
1197Qgis::DistanceUnit QgsProcessingContextOptionsWidget::distanceUnit() const
1198{
1199 return mDistanceUnitsCombo->currentData().value<Qgis::DistanceUnit>();
1200}
1201
1202Qgis::AreaUnit QgsProcessingContextOptionsWidget::areaUnit() const
1203{
1204 return mAreaUnitsCombo->currentData().value<Qgis::AreaUnit>();
1205}
1206
1207QString QgsProcessingContextOptionsWidget::temporaryFolder()
1208{
1209 return mTemporaryFolderWidget->filePath();
1210}
1211
1212int QgsProcessingContextOptionsWidget::maximumThreads() const
1213{
1214 return mThreadsSpinBox->value();
1215}
1216
1217void QgsProcessingContextOptionsWidget::setLogLevel( Qgis::ProcessingLogLevel level )
1218{
1219 whileBlocking( mLogLevelComboBox )->setCurrentIndex( mLogLevelComboBox->findData( static_cast<int>( level ) ) );
1220}
1221
1222Qgis::ProcessingLogLevel QgsProcessingContextOptionsWidget::logLevel() const
1223{
1224 return static_cast<Qgis::ProcessingLogLevel>( mLogLevelComboBox->currentData().toInt() );
1225}
1226
1227
1228//
1229// QgsProcessingAlgorithmWidget
1230//
1231
1232QgsProcessingAlgorithmWidget::QgsProcessingAlgorithmWidget( QgsProcessingAlgorithm *algorithm, bool inPlace, QMainWindow *parentWindow, WidgetFlags flags, Qgis::DockableWidgetInitialState initialState )
1233 : QgsProcessingAlgorithmWidgetBase( parentWindow, WidgetMode::Single, flags, initialState )
1234 , mInPlace( inPlace )
1235{
1236 setAlgorithm( algorithm );
1237
1238 connect( buttonBox(), &QDialogButtonBox::accepted, this, [this] {
1239 for ( QWidget *widget : std::as_const( mWidgetsToResetPalette ) )
1240 {
1241 widget->setPalette( QPalette() );
1242 }
1243 mWidgetsToResetPalette.clear();
1244 } );
1245}
1246
1247QgsProcessingParametersWidget *QgsProcessingAlgorithmWidget::createParametersPanel( bool inPlaceMode, QgsVectorLayer *activeLayer, QgsMessageBar *messageBar )
1248{
1249 auto widget = new QgsProcessingParametersWidget( algorithm(), inPlaceMode, activeLayer, messageBar );
1250 widget->registerProcessingContextGenerator( this );
1251 widget->initWidgets();
1252 return widget;
1253}
1254
1255QgsProcessingParametersWidget *QgsProcessingAlgorithmWidget::parametersWidget()
1256{
1257 return qobject_cast< QgsProcessingParametersWidget * >( mainWidget() );
1258}
1259
1260void QgsProcessingAlgorithmWidget::flagInvalidParameterValue( const QString &message, QWidget *widget )
1261{
1262 if ( !widget )
1263 return;
1264
1265 QPalette palette = widget->palette();
1266 palette.setColor( QPalette::ColorRole::Base, QColor( 255, 255, 0 ) );
1267 widget->setPalette( palette );
1268 mWidgetsToResetPalette.append( widget );
1269
1270 messageBar()->clearWidgets();
1271 messageBar()->pushMessage( QString(), tr( "Wrong or missing parameter value: %1" ).arg( message ), Qgis::MessageLevel::Warning, 5 );
1272}
1273
1274void QgsProcessingAlgorithmWidget::flagInvalidOutputExtension( const QString &message, QWidget *widget )
1275{
1276 if ( !widget )
1277 return;
1278
1279 QPalette palette = widget->palette();
1280 palette.setColor( QPalette::ColorRole::Base, QColor( 255, 255, 0 ) );
1281 widget->setPalette( palette );
1282 mWidgetsToResetPalette.append( widget );
1283
1284 messageBar()->clearWidgets();
1285 messageBar()->pushMessage( QString(), message, Qgis::MessageLevel::Warning, 5 );
1286}
1287
1288bool QgsProcessingAlgorithmWidget::handleValidationResults( const QList<ParameterValidationResult> &results )
1289{
1290 bool allValid = true;
1291 QgsProcessingParametersWidget *widget = parametersWidget();
1292 for ( const ParameterValidationResult &result : results )
1293 {
1294 QgsAbstractProcessingParameterWidgetWrapper *wrapper = widget ? widget->wrapper( result.parameterName ) : nullptr;
1295 QWidget *widget = wrapper ? wrapper->wrappedWidget() : nullptr;
1296
1297 const QgsProcessingParameterDefinition *parameter = algorithm()->parameterDefinition( result.parameterName );
1298 switch ( result.result )
1299 {
1301 break;
1303 flagInvalidOutputExtension( result.message, widget );
1304 allValid = false;
1305 break;
1307 flagInvalidParameterValue( parameter ? parameter->description() : QString(), widget );
1308 allValid = false;
1309 break;
1310 }
1311 }
1312
1313 return allValid;
1314}
1315
1316bool QgsProcessingAlgorithmWidget::inPlace() const
1317{
1318 return mInPlace;
1319}
1320
1321void QgsProcessingAlgorithmWidget::setParameters( const QVariantMap &values )
1322{
1323 if ( QgsProcessingParametersWidget *widget = parametersWidget() )
1324 {
1325 widget->setParameters( values );
1326 }
1327}
1328
1329QVariantMap QgsProcessingAlgorithmWidget::createProcessingParameters( Flags flags )
1330{
1331 QgsProcessingParametersWidget *widget = parametersWidget();
1332 if ( !widget )
1333 return {};
1334
1335 QList<ParameterValidationResult> validationResults;
1336 const QVariantMap parameters = widget->createAndValidateParameters( flags, validationResults );
1337
1338 const bool allValid = handleValidationResults( validationResults );
1339
1340 return allValid ? parameters : QVariantMap();
1341}
1342
1343QgsProcessingContext *QgsProcessingAlgorithmWidget::processingContext() const
1344{
1345 if ( !mContext )
1346 {
1347 mContext.reset( QgsGui::processingGuiRegistry()->contextFactory() ? QgsGui::processingGuiRegistry()->contextFactory()->createContext() : new QgsProcessingContext() );
1348 }
1349
1350 if ( !mFeedback )
1351 {
1352 mFeedback.reset( const_cast< QgsProcessingAlgorithmWidget * >( this )->createFeedback() );
1353 }
1354 mContext->setFeedback( mFeedback.get() );
1355
1356 const_cast< QgsProcessingAlgorithmWidget * >( this )->applyContextOverrides( mContext.get() );
1357 return mContext.get();
1358}
1359
1360
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:62
DistanceUnit
Units of distance.
Definition qgis.h:5568
@ Feet
Imperial feet.
Definition qgis.h:5571
@ Centimeters
Centimeters.
Definition qgis.h:5576
@ Millimeters
Millimeters.
Definition qgis.h:5577
@ Miles
Terrestrial miles.
Definition qgis.h:5574
@ Meters
Meters.
Definition qgis.h:5569
@ Unknown
Unknown distance unit.
Definition qgis.h:5618
@ Yards
Imperial yards.
Definition qgis.h:5573
@ Degrees
Degrees, for planar geographic CRS distance measurements.
Definition qgis.h:5575
@ Inches
Inches.
Definition qgis.h:5578
@ NauticalMiles
Nautical miles.
Definition qgis.h:5572
@ Kilometers
Kilometers.
Definition qgis.h:5570
AreaUnit
Units of area.
Definition qgis.h:5645
@ Acres
Acres.
Definition qgis.h:5652
@ SquareFeet
Square feet.
Definition qgis.h:5648
@ SquareCentimeters
Square centimeters.
Definition qgis.h:5655
@ SquareInches
Square inches.
Definition qgis.h:5657
@ SquareNauticalMiles
Square nautical miles.
Definition qgis.h:5653
@ SquareMillimeters
Square millimeters.
Definition qgis.h:5656
@ SquareYards
Square yards.
Definition qgis.h:5649
@ Hectares
Hectares.
Definition qgis.h:5651
@ SquareKilometers
Square kilometers.
Definition qgis.h:5647
@ SquareMeters
Square meters.
Definition qgis.h:5646
@ Unknown
Unknown areal unit.
Definition qgis.h:5658
@ SquareDegrees
Square degrees, for planar geographic CRS area measurements.
Definition qgis.h:5654
@ SquareMiles
Square miles.
Definition qgis.h:5650
@ Warning
Warning message.
Definition qgis.h:162
@ TitleCase
Simple title case conversion - does not fully grammatically parse the text and uses simple rules only...
Definition qgis.h:3633
ProcessingAlgorithmDocumentationFlag
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3857
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3869
InvalidGeometryCheck
Methods for handling of features with invalid geometries.
Definition qgis.h:2423
@ NoCheck
No invalid geometry checking.
Definition qgis.h:2424
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
Definition qgis.h:2426
@ SkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
Definition qgis.h:2425
@ NotAvailableInStandaloneTool
Algorithm should not be available from the standalone "qgis_process" tool. Used to flag algorithms wh...
Definition qgis.h:3834
@ SecurityRisk
The algorithm represents a potential security risk if executed with untrusted inputs.
Definition qgis.h:3836
@ DisplayNameIsLiteral
Algorithm's display name is a static literal string, and should not be translated or automatically fo...
Definition qgis.h:3828
@ KnownIssues
Algorithm has known issues.
Definition qgis.h:3830
DockableWidgetInitialState
Dockable widget initial states.
Definition qgis.h:7103
@ ForceDocked
Force the widget to be docked.
Definition qgis.h:7105
@ ForceDialog
Force the widget to be shown in a dialog.
Definition qgis.h:7106
ProcessingLogLevel
Logging level for algorithms to use when pushing feedback messages.
Definition qgis.h:3895
@ DefaultLevel
Default logging level.
Definition qgis.h:3896
@ Verbose
Verbose logging.
Definition qgis.h:3897
@ ModelDebug
Model debug level logging. Includes verbose logging and other outputs useful for debugging models.
Definition qgis.h:3898
A widget wrapper for Processing parameter value widgets.
QWidget * wrappedWidget()
Returns the current wrapped widget, if any.
Encapsulates an academic reference and formats it according to style guidelines.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling.
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
@ GetDirectory
Select a directory.
void fileChanged(const QString &path)
Emitted whenever the current file or directory path is changed.
static QgsProcessingGuiRegistry * processingGuiRegistry()
Returns the global processing gui registry, used for registering the GUI behavior of processing algor...
Definition qgsgui.cpp:170
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition qgsgui.cpp:225
@ HigDialogTitleIsTitleCase
Dialog titles should be title case.
Definition qgsgui.h:284
static QgsNative * nativePlatformInterface()
Returns the global native interface, which offers abstraction to the host OS's underlying public inte...
Definition qgsgui.cpp:100
static QgsGui::HigFlags higFlags()
Returns the platform's HIG flags.
Definition qgsgui.cpp:254
static QUrl helpUrl(const QString &key)
Returns URI of the help topic for the given key.
Definition qgshelp.cpp:46
static QVariant parseJson(const std::string &jsonString)
Converts JSON jsonString to a QVariant, in case of parsing error an invalid QVariant is returned and ...
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
A bar for displaying non-blocking messages to the user.
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 changed()
Emitted when the widget state changes.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
QgsTask task which runs a QgsProcessingAlgorithm in a background task.
void executed(bool successful, const QVariantMap &results)
Emitted when the algorithm has finished execution.
Abstract base class for processing algorithms.
const QgsProcessingParameterDefinition * parameterDefinition(const QString &name) const
Returns a matching parameter by name.
Contains information about the context in which a processing algorithm is executed.
Qgis::AreaUnit areaUnit() const
Returns the area unit to use for area calculations.
void setLogLevel(Qgis::ProcessingLogLevel level)
Sets the logging level for algorithms to use when pushing feedback messages to users.
void setFeedback(QgsProcessingFeedback *feedback)
Sets an associated feedback object.
void setMaximumThreads(int threads)
Sets the (optional) number of threads to use when running algorithms.
void setDistanceUnit(Qgis::DistanceUnit unit)
Sets the unit to use for distance calculations.
void setInvalidGeometryCheck(Qgis::InvalidGeometryCheck check)
Sets the behavior used for checking invalid geometries in input layers.
void setAreaUnit(Qgis::AreaUnit areaUnit)
Sets the unit to use for area calculations.
Qgis::DistanceUnit distanceUnit() const
Returns the distance unit to use for distance calculations.
Qgis::ProcessingLogLevel logLevel() const
Returns the logging level for algorithms to use when pushing feedback messages to users.
Qgis::InvalidGeometryCheck invalidGeometryCheck() const
Returns the behavior used for checking invalid geometries in input layers.
void setTemporaryFolder(const QString &folder)
Sets the (optional) temporary folder to use when running algorithms.
QString temporaryFolder() const
Returns the (optional) temporary folder to use when running algorithms.
int maximumThreads() const
Returns the (optional) number of threads to use when running algorithms.
Base class for providing feedback from a processing algorithm.
void warningPushed(const QString &text)
Emitted when an warning is pushed.
void infoPushed(const QString &text)
Emitted when information text is pushed.
void errorReported(const QString &text, bool fatalError)
Emitted when an error is reported.
void progressTextChanged(const QString &text)
Emitted when the progress text is changed.
void debugInfoPushed(const QString &text)
Emitted when debug information text is pushed.
void formattedMessagePushed(const QString &html)
Emitted when a formatted html message is pushed.
void commandInfoPushed(const QString &text)
Emitted when command information text is pushed.
void consoleInfoPushed(const QString &text)
Emitted when console information text is pushed.
Base class for the definition of processing parameters.
QString description() const
Returns the description for the parameter.
@ InvalidValue
Generic result for invalid parameter values.
@ InvalidOutputExtension
Output parameter extension is invalid.
static QVariantMap preprocessQgisProcessParameters(const QVariantMap &parameters, bool &ok, QString &error)
Pre-processes a set of parameter values for the qgis_process command.
static QString documentationFlagToString(Qgis::ProcessingAlgorithmDocumentationFlag flag)
Converts a documentation flag to a translated string.
Stores settings for use within QGIS.
Definition qgssettings.h:68
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Utility functions for working with strings.
static QString capitalize(const QString &string, Qgis::Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
void taskTriggered(QgsTask *task)
Emitted when a task is triggered.
Abstract base class for long running background tasks.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
Represents a vector layer which manages a vector based dataset.
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
const QList< T > qgsEnumList()
Returns a list all enum entries.
Definition qgis.h:7965
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:7619