QGIS API Documentation  3.21.0-Master (5b68dc587e)
qgsprocessingalgorithmdialogbase.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 #include "qgssettings.h"
18 #include "qgshelp.h"
19 #include "qgsmessagebar.h"
20 #include "qgsgui.h"
23 #include "qgstaskmanager.h"
25 #include "qgsstringutils.h"
26 #include "qgsapplication.h"
27 #include "qgspanelwidget.h"
28 #include <QToolButton>
29 #include <QDesktopServices>
30 #include <QScrollBar>
31 #include <QApplication>
32 #include <QClipboard>
33 #include <QFileDialog>
34 #include <QMimeData>
35 
36 
38 
39 QgsProcessingAlgorithmDialogFeedback::QgsProcessingAlgorithmDialogFeedback()
40  : QgsProcessingFeedback( false )
41 {}
42 
43 void QgsProcessingAlgorithmDialogFeedback::setProgressText( const QString &text )
44 {
46  emit progressTextChanged( text );
47 }
48 
49 void QgsProcessingAlgorithmDialogFeedback::reportError( const QString &error, bool fatalError )
50 {
51  QgsProcessingFeedback::reportError( error, fatalError );
52  emit errorReported( error, fatalError );
53 }
54 
55 void QgsProcessingAlgorithmDialogFeedback::pushWarning( const QString &warning )
56 {
58  emit warningPushed( warning );
59 }
60 
61 void QgsProcessingAlgorithmDialogFeedback::pushInfo( const QString &info )
62 {
64  emit infoPushed( info );
65 }
66 
67 void QgsProcessingAlgorithmDialogFeedback::pushCommandInfo( const QString &info )
68 {
70  emit commandInfoPushed( info );
71 }
72 
73 void QgsProcessingAlgorithmDialogFeedback::pushDebugInfo( const QString &info )
74 {
76  emit debugInfoPushed( info );
77 }
78 
79 void QgsProcessingAlgorithmDialogFeedback::pushConsoleInfo( const QString &info )
80 {
82  emit consoleInfoPushed( info );
83 }
84 
85 //
86 // QgsProcessingAlgorithmDialogBase
87 //
88 
89 QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *parent, Qt::WindowFlags flags )
90  : QDialog( parent, flags )
91 {
92  setupUi( this );
93 
94  //don't collapse parameters panel
95  splitter->setCollapsible( 0, false );
96 
97  // add collapse button to splitter
98  QSplitterHandle *splitterHandle = splitter->handle( 1 );
99  QVBoxLayout *handleLayout = new QVBoxLayout();
100  handleLayout->setContentsMargins( 0, 0, 0, 0 );
101  mButtonCollapse = new QToolButton( splitterHandle );
102  mButtonCollapse->setAutoRaise( true );
103  mButtonCollapse->setFixedSize( 12, 12 );
104  mButtonCollapse->setCursor( Qt::ArrowCursor );
105  handleLayout->addWidget( mButtonCollapse );
106  handleLayout->addStretch();
107  splitterHandle->setLayout( handleLayout );
108 
110 
111  const QgsSettings settings;
112  splitter->restoreState( settings.value( QStringLiteral( "/Processing/dialogBaseSplitter" ), QByteArray() ).toByteArray() );
113  mSplitterState = splitter->saveState();
114  splitterChanged( 0, 0 );
115 
116  // Rename OK button to Run
117  mButtonRun = mButtonBox->button( QDialogButtonBox::Ok );
118  mButtonRun->setText( tr( "Run" ) );
119 
120  // Rename Yes button. Yes is used to ensure same position of Run and Change Parameters with respect to Close button.
121  mButtonChangeParameters = mButtonBox->button( QDialogButtonBox::Yes );
122  mButtonChangeParameters->setText( tr( "Change Parameters" ) );
123 
124  buttonCancel->setEnabled( false );
125  mButtonClose = mButtonBox->button( QDialogButtonBox::Close );
126 
127  connect( mButtonRun, &QPushButton::clicked, this, &QgsProcessingAlgorithmDialogBase::runAlgorithm );
128  connect( mButtonChangeParameters, &QPushButton::clicked, this, &QgsProcessingAlgorithmDialogBase::showParameters );
129  connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsProcessingAlgorithmDialogBase::closeClicked );
130  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsProcessingAlgorithmDialogBase::openHelp );
131  connect( mButtonCollapse, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::toggleCollapsed );
132  connect( splitter, &QSplitter::splitterMoved, this, &QgsProcessingAlgorithmDialogBase::splitterChanged );
133 
134  connect( mButtonSaveLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::saveLog );
135  connect( mButtonCopyLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::copyLogToClipboard );
136  connect( mButtonClearLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::clearLog );
137 
138  connect( mTabWidget, &QTabWidget::currentChanged, this, &QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged );
139 
140  mMessageBar = new QgsMessageBar();
141  mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
142  verticalLayout->insertWidget( 0, mMessageBar );
143 
144  connect( QgsApplication::taskManager(), &QgsTaskManager::taskTriggered, this, &QgsProcessingAlgorithmDialogBase::taskTriggered );
145 }
146 
147 QgsProcessingAlgorithmDialogBase::~QgsProcessingAlgorithmDialogBase() = default;
148 
149 void QgsProcessingAlgorithmDialogBase::setAlgorithm( QgsProcessingAlgorithm *algorithm )
150 {
151  mAlgorithm.reset( algorithm );
152  QString title;
154  {
155  title = QgsStringUtils::capitalize( mAlgorithm->displayName(), QgsStringUtils::TitleCase );
156  }
157  else
158  {
159  title = mAlgorithm->displayName();
160  }
161  setWindowTitle( title );
162 
163  const QString algHelp = formatHelp( algorithm );
164  if ( algHelp.isEmpty() )
165  textShortHelp->hide();
166  else
167  {
168  textShortHelp->document()->setDefaultStyleSheet( QStringLiteral( ".summary { margin-left: 10px; margin-right: 10px; }\n"
169  "h2 { color: #555555; padding-bottom: 15px; }\n"
170  "a { text - decoration: none; color: #3498db; font-weight: bold; }\n"
171  "p { color: #666666; }\n"
172  "b { color: #333333; }\n"
173  "dl dd { margin - bottom: 5px; }" ) );
174  textShortHelp->setHtml( algHelp );
175  connect( textShortHelp, &QTextBrowser::anchorClicked, this, &QgsProcessingAlgorithmDialogBase::linkClicked );
176  }
177 
178  if ( algorithm->helpUrl().isEmpty() && algorithm->provider()->helpId().isEmpty() )
179  {
180  mButtonBox->removeButton( mButtonBox->button( QDialogButtonBox::Help ) );
181  }
182 
183  const QString warning = algorithm->provider()->warningMessage();
184  if ( !warning.isEmpty() )
185  {
186  mMessageBar->pushMessage( warning, Qgis::MessageLevel::Warning );
187  }
188 }
189 
191 {
192  return mAlgorithm.get();
193 }
194 
195 void QgsProcessingAlgorithmDialogBase::setMainWidget( QgsPanelWidget *widget )
196 {
197  if ( mMainWidget )
198  {
199  mMainWidget->deleteLater();
200  }
201 
202  mPanelStack->setMainPanel( widget );
203  widget->setDockMode( true );
204 
205  mMainWidget = widget;
206  connect( mMainWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
207 }
208 
209 QgsPanelWidget *QgsProcessingAlgorithmDialogBase::mainWidget()
210 {
211  return mMainWidget;
212 }
213 
214 void QgsProcessingAlgorithmDialogBase::saveLogToFile( const QString &path, const LogFormat format )
215 {
216  QFile logFile( path );
217  if ( !logFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
218  {
219  return;
220  }
221  QTextStream fout( &logFile );
222 
223  switch ( format )
224  {
225  case FormatPlainText:
226  fout << txtLog->toPlainText();
227  break;
228 
229  case FormatHtml:
230  fout << txtLog->toHtml();
231  break;
232  }
233 }
234 
235 QgsProcessingFeedback *QgsProcessingAlgorithmDialogBase::createFeedback()
236 {
237  auto feedback = std::make_unique< QgsProcessingAlgorithmDialogFeedback >();
238  connect( feedback.get(), &QgsProcessingFeedback::progressChanged, this, &QgsProcessingAlgorithmDialogBase::setPercentage );
239  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::commandInfoPushed, this, &QgsProcessingAlgorithmDialogBase::pushCommandInfo );
240  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::consoleInfoPushed, this, &QgsProcessingAlgorithmDialogBase::pushConsoleInfo );
241  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::debugInfoPushed, this, &QgsProcessingAlgorithmDialogBase::pushDebugInfo );
242  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::errorReported, this, &QgsProcessingAlgorithmDialogBase::reportError );
243  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::warningPushed, this, &QgsProcessingAlgorithmDialogBase::pushWarning );
244  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::infoPushed, this, &QgsProcessingAlgorithmDialogBase::pushInfo );
245  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::progressTextChanged, this, &QgsProcessingAlgorithmDialogBase::setProgressText );
246  connect( buttonCancel, &QPushButton::clicked, feedback.get(), &QgsProcessingFeedback::cancel );
247  return feedback.release();
248 }
249 
250 QDialogButtonBox *QgsProcessingAlgorithmDialogBase::buttonBox()
251 {
252  return mButtonBox;
253 }
254 
255 QTabWidget *QgsProcessingAlgorithmDialogBase::tabWidget()
256 {
257  return mTabWidget;
258 }
259 
260 void QgsProcessingAlgorithmDialogBase::showLog()
261 {
262  mTabWidget->setCurrentIndex( 1 );
263 }
264 
265 void QgsProcessingAlgorithmDialogBase::showParameters()
266 {
267  mTabWidget->setCurrentIndex( 0 );
268 }
269 
270 QPushButton *QgsProcessingAlgorithmDialogBase::runButton()
271 {
272  return mButtonRun;
273 }
274 
275 QPushButton *QgsProcessingAlgorithmDialogBase::cancelButton()
276 {
277  return buttonCancel;
278 }
279 
280 QPushButton *QgsProcessingAlgorithmDialogBase::changeParametersButton()
281 {
282  return mButtonChangeParameters;
283 }
284 
285 void QgsProcessingAlgorithmDialogBase::clearProgress()
286 {
287  progressBar->setMaximum( 0 );
288 }
289 
290 void QgsProcessingAlgorithmDialogBase::setExecuted( bool executed )
291 {
292  mExecuted = executed;
293 }
294 
295 void QgsProcessingAlgorithmDialogBase::setExecutedAnyResult( bool executedAnyResult )
296 {
297  mExecutedAnyResult = executedAnyResult;
298 }
299 
300 void QgsProcessingAlgorithmDialogBase::setResults( const QVariantMap &results )
301 {
302  mResults = results;
303 }
304 
305 void QgsProcessingAlgorithmDialogBase::finished( bool, const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
306 {
307 
308 }
309 
310 void QgsProcessingAlgorithmDialogBase::openHelp()
311 {
312  QUrl algHelp = mAlgorithm->helpUrl();
313  if ( algHelp.isEmpty() )
314  {
315  algHelp = QgsHelp::helpUrl( QStringLiteral( "processing_algs/%1/%2.html#%3" ).arg( mAlgorithm->provider()->helpId(), mAlgorithm->groupId(), QStringLiteral( "%1%2" ).arg( mAlgorithm->provider()->helpId() ).arg( mAlgorithm->name() ) ) );
316  }
317 
318  if ( !algHelp.isEmpty() )
319  QDesktopServices::openUrl( algHelp );
320 }
321 
322 void QgsProcessingAlgorithmDialogBase::toggleCollapsed()
323 {
324  if ( mHelpCollapsed )
325  {
326  splitter->restoreState( mSplitterState );
327  mButtonCollapse->setArrowType( Qt::RightArrow );
328  }
329  else
330  {
331  mSplitterState = splitter->saveState();
332  splitter->setSizes( QList<int>() << 1 << 0 );
333  mButtonCollapse->setArrowType( Qt::LeftArrow );
334  }
335  mHelpCollapsed = !mHelpCollapsed;
336 }
337 
338 void QgsProcessingAlgorithmDialogBase::splitterChanged( int, int )
339 {
340  if ( splitter->sizes().at( 1 ) == 0 )
341  {
342  mHelpCollapsed = true;
343  mButtonCollapse->setArrowType( Qt::LeftArrow );
344  }
345  else
346  {
347  mHelpCollapsed = false;
348  mButtonCollapse->setArrowType( Qt::RightArrow );
349  }
350 }
351 
352 void QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged( int )
353 {
354  updateRunButtonVisibility();
355 }
356 
357 void QgsProcessingAlgorithmDialogBase::linkClicked( const QUrl &url )
358 {
359  QDesktopServices::openUrl( url.toString() );
360 }
361 
362 void QgsProcessingAlgorithmDialogBase::algExecuted( bool successful, const QVariantMap & )
363 {
364  mAlgorithmTask = nullptr;
365 
366  if ( !successful )
367  {
368  // show dialog to display errors
369  show();
370  raise();
371  setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
372  activateWindow();
373  showLog();
374  }
375  else
376  {
377  // delete dialog if closed
378  if ( !isVisible() )
379  {
380  deleteLater();
381  }
382  }
383 }
384 
385 void QgsProcessingAlgorithmDialogBase::taskTriggered( QgsTask *task )
386 {
387  if ( task == mAlgorithmTask )
388  {
389  show();
390  raise();
391  setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
392  activateWindow();
393  showLog();
394  }
395 }
396 
397 void QgsProcessingAlgorithmDialogBase::closeClicked()
398 {
399  reject();
400  close();
401 }
402 
403 QgsProcessingContext::LogLevel QgsProcessingAlgorithmDialogBase::logLevel() const
404 {
405  return mLogLevel;
406 }
407 
408 void QgsProcessingAlgorithmDialogBase::setLogLevel( QgsProcessingContext::LogLevel level )
409 {
410  mLogLevel = level;
411 }
412 
413 void QgsProcessingAlgorithmDialogBase::reportError( const QString &error, bool fatalError )
414 {
415  setInfo( error, true );
416  if ( fatalError )
417  resetGui();
418  showLog();
419  processEvents();
420 }
421 
422 void QgsProcessingAlgorithmDialogBase::pushWarning( const QString &warning )
423 {
424  setInfo( warning, false, true, true );
425  processEvents();
426 }
427 
428 void QgsProcessingAlgorithmDialogBase::pushInfo( const QString &info )
429 {
430  setInfo( info );
431  processEvents();
432 }
433 
434 void QgsProcessingAlgorithmDialogBase::pushCommandInfo( const QString &command )
435 {
436  txtLog->append( QStringLiteral( "<code>%1<code>" ).arg( formatStringForLog( command.toHtmlEscaped() ) ) );
437  scrollToBottomOfLog();
438  processEvents();
439 }
440 
441 void QgsProcessingAlgorithmDialogBase::pushDebugInfo( const QString &message )
442 {
443  txtLog->append( QStringLiteral( "<span style=\"color:#777\">%1</span>" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
444  scrollToBottomOfLog();
445  processEvents();
446 }
447 
448 void QgsProcessingAlgorithmDialogBase::pushConsoleInfo( const QString &info )
449 {
450  txtLog->append( QStringLiteral( "<code style=\"color:#777\">%1</code>" ).arg( formatStringForLog( info.toHtmlEscaped() ) ) );
451  scrollToBottomOfLog();
452  processEvents();
453 }
454 
455 QDialog *QgsProcessingAlgorithmDialogBase::createProgressDialog()
456 {
457  QgsProcessingAlgorithmProgressDialog *dialog = new QgsProcessingAlgorithmProgressDialog( this );
458  dialog->setWindowModality( Qt::ApplicationModal );
459  dialog->setWindowTitle( windowTitle() );
460  dialog->setGeometry( geometry() ); // match size/position to this dialog
461  connect( progressBar, &QProgressBar::valueChanged, dialog->progressBar(), &QProgressBar::setValue );
462  connect( dialog->cancelButton(), &QPushButton::clicked, buttonCancel, &QPushButton::click );
463  dialog->logTextEdit()->setHtml( txtLog->toHtml() );
464  connect( txtLog, &QTextEdit::textChanged, dialog, [this, dialog]()
465  {
466  dialog->logTextEdit()->setHtml( txtLog->toHtml() );
467  QScrollBar *sb = dialog->logTextEdit()->verticalScrollBar();
468  sb->setValue( sb->maximum() );
469  } );
470  return dialog;
471 }
472 
473 void QgsProcessingAlgorithmDialogBase::clearLog()
474 {
475  txtLog->clear();
476 }
477 
478 void QgsProcessingAlgorithmDialogBase::saveLog()
479 {
480  QgsSettings settings;
481  const QString lastUsedDir = settings.value( QStringLiteral( "/Processing/lastUsedLogDirectory" ), QDir::homePath() ).toString();
482 
483  QString filter;
484  const QString txtExt = tr( "Text files" ) + QStringLiteral( " (*.txt *.TXT)" );
485  const QString htmlExt = tr( "HTML files" ) + QStringLiteral( " (*.html *.HTML)" );
486 
487  const QString path = QFileDialog::getSaveFileName( this, tr( "Save Log to File" ), lastUsedDir, txtExt + ";;" + htmlExt, &filter );
488  if ( path.isEmpty() )
489  {
490  return;
491  }
492 
493  settings.setValue( QStringLiteral( "/Processing/lastUsedLogDirectory" ), QFileInfo( path ).path() );
494 
495  LogFormat format = FormatPlainText;
496  if ( filter == htmlExt )
497  {
498  format = FormatHtml;
499  }
500  saveLogToFile( path, format );
501 }
502 
503 void QgsProcessingAlgorithmDialogBase::copyLogToClipboard()
504 {
505  QMimeData *m = new QMimeData();
506  m->setText( txtLog->toPlainText() );
507  m->setHtml( txtLog->toHtml() );
508  QClipboard *cb = QApplication::clipboard();
509 
510 #ifdef Q_OS_LINUX
511  cb->setMimeData( m, QClipboard::Selection );
512 #endif
513  cb->setMimeData( m, QClipboard::Clipboard );
514 }
515 
516 void QgsProcessingAlgorithmDialogBase::closeEvent( QCloseEvent *e )
517 {
518  if ( !mHelpCollapsed )
519  {
520  QgsSettings settings;
521  settings.setValue( QStringLiteral( "/Processing/dialogBaseSplitter" ), splitter->saveState() );
522  }
523 
524  QDialog::closeEvent( e );
525 
526  if ( !mAlgorithmTask )
527  {
528  // when running a background task, the dialog is kept around and deleted only when the task
529  // completes. But if not running a task, we auto cleanup (later - gotta give callers a chance
530  // to retrieve results and execution status).
531  deleteLater();
532  }
533 }
534 
535 void QgsProcessingAlgorithmDialogBase::runAlgorithm()
536 {
537 
538 }
539 
540 void QgsProcessingAlgorithmDialogBase::setPercentage( double percent )
541 {
542  // delay setting maximum progress value until we know algorithm reports progress
543  if ( progressBar->maximum() == 0 )
544  progressBar->setMaximum( 100 );
545  progressBar->setValue( percent );
546  processEvents();
547 }
548 
549 void QgsProcessingAlgorithmDialogBase::setProgressText( const QString &text )
550 {
551  lblProgress->setText( text );
552  setInfo( text, false );
553  scrollToBottomOfLog();
554  processEvents();
555 }
556 
557 QString QgsProcessingAlgorithmDialogBase::formatHelp( QgsProcessingAlgorithm *algorithm )
558 {
559  const QString text = algorithm->shortHelpString();
560  if ( !text.isEmpty() )
561  {
562  const QStringList paragraphs = text.split( '\n' );
563  QString help;
564  for ( const QString &paragraph : paragraphs )
565  {
566  help += QStringLiteral( "<p>%1</p>" ).arg( paragraph );
567  }
568  return QStringLiteral( "<h2>%1</h2>%2" ).arg( algorithm->displayName(), help );
569  }
570  else if ( !algorithm->shortDescription().isEmpty() )
571  {
572  return QStringLiteral( "<h2>%1</h2><p>%2</p>" ).arg( algorithm->displayName(), algorithm->shortDescription() );
573  }
574  else
575  return QString();
576 }
577 
578 void QgsProcessingAlgorithmDialogBase::processEvents()
579 {
580  if ( mAlgorithmTask )
581  {
582  // no need to call this - the algorithm is running in a thread.
583  // in fact, calling it causes a crash on Windows when the algorithm
584  // is running in a background thread... unfortunately we need something
585  // like this for non-threadable algorithms, otherwise there's no chance
586  // for users to hit cancel or see progress updates...
587  return;
588  }
589 
590  // So that we get a chance of hitting the Abort button
591 #ifdef Q_OS_LINUX
592  // One iteration is actually enough on Windows to get good interactivity
593  // whereas on Linux we must allow for far more iterations.
594  // For safety limit the number of iterations
595  int nIters = 0;
596  while ( ++nIters < 100 )
597 #endif
598  {
599  QCoreApplication::processEvents();
600  }
601 }
602 
603 void QgsProcessingAlgorithmDialogBase::scrollToBottomOfLog()
604 {
605  QScrollBar *sb = txtLog->verticalScrollBar();
606  sb->setValue( sb->maximum() );
607 }
608 
609 void QgsProcessingAlgorithmDialogBase::resetGui()
610 {
611  lblProgress->clear();
612  progressBar->setMaximum( 100 );
613  progressBar->setValue( 0 );
614  mButtonRun->setEnabled( true );
615  mButtonChangeParameters->setEnabled( true );
616  mButtonClose->setEnabled( true );
617  if ( mMainWidget )
618  {
619  mMainWidget->setEnabled( true );
620  }
621  updateRunButtonVisibility();
622  resetAdditionalGui();
623 }
624 
625 void QgsProcessingAlgorithmDialogBase::updateRunButtonVisibility()
626 {
627  // Activate run button if current tab is Parameters
628  const bool runButtonVisible = mTabWidget->currentIndex() == 0;
629  mButtonRun->setVisible( runButtonVisible );
630  mButtonChangeParameters->setVisible( !runButtonVisible && mExecutedAnyResult && mButtonChangeParameters->isEnabled() );
631 }
632 
633 void QgsProcessingAlgorithmDialogBase::resetAdditionalGui()
634 {
635 
636 }
637 
638 void QgsProcessingAlgorithmDialogBase::blockControlsWhileRunning()
639 {
640  mButtonRun->setEnabled( false );
641  mButtonChangeParameters->setEnabled( false );
642  if ( mMainWidget )
643  {
644  mMainWidget->setEnabled( false );
645  }
646  blockAdditionalControlsWhileRunning();
647 }
648 
649 void QgsProcessingAlgorithmDialogBase::blockAdditionalControlsWhileRunning()
650 {
651 
652 }
653 
654 QgsMessageBar *QgsProcessingAlgorithmDialogBase::messageBar()
655 {
656  return mMessageBar;
657 }
658 
659 void QgsProcessingAlgorithmDialogBase::hideShortHelp()
660 {
661  textShortHelp->setVisible( false );
662 }
663 
664 void QgsProcessingAlgorithmDialogBase::setCurrentTask( QgsProcessingAlgRunnerTask *task )
665 {
666  mAlgorithmTask = task;
667  connect( mAlgorithmTask, &QgsProcessingAlgRunnerTask::executed, this, &QgsProcessingAlgorithmDialogBase::algExecuted );
668  QgsApplication::taskManager()->addTask( mAlgorithmTask );
669 }
670 
671 QString QgsProcessingAlgorithmDialogBase::formatStringForLog( const QString &string )
672 {
673  QString s = string;
674  s.replace( '\n', QLatin1String( "<br>" ) );
675  return s;
676 }
677 
678 void QgsProcessingAlgorithmDialogBase::setInfo( const QString &message, bool isError, bool escapeHtml, bool isWarning )
679 {
680  constexpr int MESSAGE_COUNT_LIMIT = 10000;
681  // Avoid logging too many messages, which might blow memory.
682  if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
683  return;
684  ++mMessageLoggedCount;
685 
686  // note -- we have to wrap the message in a span block, or QTextEdit::append sometimes gets confused
687  // and varies between treating it as a HTML string or a plain text string! (see https://github.com/qgis/QGIS/issues/37934)
688  if ( mMessageLoggedCount == MESSAGE_COUNT_LIMIT )
689  txtLog->append( QStringLiteral( "<span style=\"color:red\">%1</span>" ).arg( tr( "Message log truncated" ) ) );
690  else if ( isError || isWarning )
691  txtLog->append( QStringLiteral( "<span style=\"color:%1\">%2</span>" ).arg( isError ? QStringLiteral( "red" ) : QStringLiteral( "#b85a20" ), escapeHtml ? formatStringForLog( message.toHtmlEscaped() ) : formatStringForLog( message ) ) );
692  else if ( escapeHtml )
693  txtLog->append( QStringLiteral( "<span>%1</span" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
694  else
695  txtLog->append( QStringLiteral( "<span>%1</span>" ).arg( formatStringForLog( message ) ) );
696  scrollToBottomOfLog();
697  processEvents();
698 }
699 
700 void QgsProcessingAlgorithmDialogBase::reject()
701 {
702  if ( !mAlgorithmTask )
703  {
704  setAttribute( Qt::WA_DeleteOnClose );
705  }
706  QDialog::reject();
707 }
708 
709 //
710 // QgsProcessingAlgorithmProgressDialog
711 //
712 
713 QgsProcessingAlgorithmProgressDialog::QgsProcessingAlgorithmProgressDialog( QWidget *parent )
714  : QDialog( parent )
715 {
716  setupUi( this );
717 }
718 
719 QProgressBar *QgsProcessingAlgorithmProgressDialog::progressBar()
720 {
721  return mProgressBar;
722 }
723 
724 QPushButton *QgsProcessingAlgorithmProgressDialog::cancelButton()
725 {
726  return mButtonBox->button( QDialogButtonBox::Cancel );
727 }
728 
729 QTextEdit *QgsProcessingAlgorithmProgressDialog::logTextEdit()
730 {
731  return mTxtLog;
732 }
733 
734 void QgsProcessingAlgorithmProgressDialog::reject()
735 {
736 
737 }
738 
739 
740 
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 ...
Definition: qgsfeedback.h:85
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:168
@ HigDialogTitleIsTitleCase
Dialog titles should be title case.
Definition: qgsgui.h:220
static QgsGui::HigFlags higFlags()
Returns the platform's HIG flags.
Definition: qgsgui.cpp:187
static QUrl helpUrl(const QString &key)
Returns URI of the help topic for the given key.
Definition: qgshelp.cpp:41
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:61
Base class for any widget that can be shown as a inline panel.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
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.
virtual QString helpUrl() const
Returns a url pointing to the algorithm's help page.
virtual QString shortHelpString() const
Returns a localised short helper string for the algorithm.
virtual QString shortDescription() const
Returns an optional translated short description of the algorithm.
@ FlagDisplayNameIsLiteral
Algorithm's display name is a static literal string, and should not be translated or automatically fo...
virtual QString displayName() const =0
Returns the translated algorithm name, which should be used for any user-visible display of the algor...
QgsProcessingProvider * provider() const
Returns the provider to which this algorithm belongs.
Contains information about the context in which a processing algorithm is executed.
LogLevel
Logging level for algorithms to use when pushing feedback messages.
QgsProcessingAlgorithm::Flags flags() const override
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
Base class for providing feedback from a processing algorithm.
virtual void pushCommandInfo(const QString &info)
Pushes an informational message containing a command from the algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
virtual void pushDebugInfo(const QString &info)
Pushes an informational message containing debugging helpers from the algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
virtual void pushConsoleInfo(const QString &info)
Pushes a console feedback message from the algorithm.
virtual void setProgressText(const QString &text)
Sets a progress report text string.
virtual QString helpId() const
Returns the provider help id string, used for creating QgsHelp urls for algorithms belong to this pro...
virtual QString warningMessage() const
Returns an optional warning message to show users when running algorithms from this provider.
static QString capitalize(const QString &string, Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
@ TitleCase
Simple title case conversion - does not fully grammatically parse the text and uses simple rules only...
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.
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