17 #include "qgssettings.h" 
   28 #include <QToolButton> 
   29 #include <QDesktopServices> 
   31 #include <QApplication> 
   33 #include <QFileDialog> 
   39 QgsProcessingAlgorithmDialogFeedback::QgsProcessingAlgorithmDialogFeedback()
 
   43 void QgsProcessingAlgorithmDialogFeedback::setProgressText( 
const QString &text )
 
   46   emit progressTextChanged( text );
 
   49 void QgsProcessingAlgorithmDialogFeedback::reportError( 
const QString &error, 
bool fatalError )
 
   52   emit errorReported( error, fatalError );
 
   55 void QgsProcessingAlgorithmDialogFeedback::pushWarning( 
const QString &warning )
 
   58   emit warningPushed( warning );
 
   61 void QgsProcessingAlgorithmDialogFeedback::pushInfo( 
const QString &info )
 
   64   emit infoPushed( info );
 
   67 void QgsProcessingAlgorithmDialogFeedback::pushCommandInfo( 
const QString &info )
 
   70   emit commandInfoPushed( info );
 
   73 void QgsProcessingAlgorithmDialogFeedback::pushDebugInfo( 
const QString &info )
 
   76   emit debugInfoPushed( info );
 
   79 void QgsProcessingAlgorithmDialogFeedback::pushConsoleInfo( 
const QString &info )
 
   82   emit consoleInfoPushed( info );
 
   89 QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *parent, Qt::WindowFlags flags )
 
   90   : QDialog( parent, flags )
 
   95   splitter->setCollapsible( 0, 
false );
 
   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 );
 
  111   QgsSettings settings;
 
  112   splitter->restoreState( settings.value( QStringLiteral( 
"/Processing/dialogBaseSplitter" ), QByteArray() ).toByteArray() );
 
  113   mSplitterState = splitter->saveState();
 
  114   splitterChanged( 0, 0 );
 
  117   mButtonRun = mButtonBox->button( QDialogButtonBox::Ok );
 
  118   mButtonRun->setText( tr( 
"Run" ) );
 
  121   mButtonChangeParameters = mButtonBox->button( QDialogButtonBox::Yes );
 
  122   mButtonChangeParameters->setText( tr( 
"Change Parameters" ) );
 
  124   buttonCancel->setEnabled( 
false );
 
  125   mButtonClose = mButtonBox->button( QDialogButtonBox::Close );
 
  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 );
 
  134   connect( mButtonSaveLog, &QToolButton::clicked, 
this, &QgsProcessingAlgorithmDialogBase::saveLog );
 
  135   connect( mButtonCopyLog, &QToolButton::clicked, 
this, &QgsProcessingAlgorithmDialogBase::copyLogToClipboard );
 
  136   connect( mButtonClearLog, &QToolButton::clicked, 
this, &QgsProcessingAlgorithmDialogBase::clearLog );
 
  138   connect( mTabWidget, &QTabWidget::currentChanged, 
this, &QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged );
 
  141   mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
 
  142   verticalLayout->insertWidget( 0,  mMessageBar );
 
  147 QgsProcessingAlgorithmDialogBase::~QgsProcessingAlgorithmDialogBase() = 
default;
 
  159     title = mAlgorithm->displayName();
 
  161   setWindowTitle( title );
 
  163   QString algHelp = formatHelp( 
algorithm );
 
  164   if ( algHelp.isEmpty() )
 
  165     textShortHelp->hide();
 
  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 );
 
  180     mButtonBox->removeButton( mButtonBox->button( QDialogButtonBox::Help ) );
 
  184   if ( !warning.isEmpty() )
 
  186     mMessageBar->pushMessage( warning, Qgis::MessageLevel::Warning );
 
  192   return mAlgorithm.get();
 
  195 void QgsProcessingAlgorithmDialogBase::setMainWidget( 
QgsPanelWidget *widget )
 
  199     mMainWidget->deleteLater();
 
  202   mPanelStack->setMainPanel( widget );
 
  205   mMainWidget = widget;
 
  214 void QgsProcessingAlgorithmDialogBase::saveLogToFile( 
const QString &path, 
const LogFormat format )
 
  216   QFile logFile( path );
 
  217   if ( !logFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
 
  221   QTextStream fout( &logFile );
 
  225     case FormatPlainText:
 
  226       fout << txtLog->toPlainText();
 
  230       fout << txtLog->toHtml();
 
  237   auto feedback = std::make_unique< QgsProcessingAlgorithmDialogFeedback >();
 
  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 );
 
  247   return feedback.release();
 
  250 QDialogButtonBox *QgsProcessingAlgorithmDialogBase::buttonBox()
 
  255 QTabWidget *QgsProcessingAlgorithmDialogBase::tabWidget()
 
  260 void QgsProcessingAlgorithmDialogBase::showLog()
 
  262   mTabWidget->setCurrentIndex( 1 );
 
  265 void QgsProcessingAlgorithmDialogBase::showParameters()
 
  267   mTabWidget->setCurrentIndex( 0 );
 
  270 QPushButton *QgsProcessingAlgorithmDialogBase::runButton()
 
  275 QPushButton *QgsProcessingAlgorithmDialogBase::cancelButton()
 
  280 QPushButton *QgsProcessingAlgorithmDialogBase::changeParametersButton()
 
  282   return mButtonChangeParameters;
 
  285 void QgsProcessingAlgorithmDialogBase::clearProgress()
 
  287   progressBar->setMaximum( 0 );
 
  290 void QgsProcessingAlgorithmDialogBase::setExecuted( 
bool executed )
 
  292   mExecuted = executed;
 
  295 void QgsProcessingAlgorithmDialogBase::setExecutedAnyResult( 
bool executedAnyResult )
 
  297   mExecutedAnyResult = executedAnyResult;
 
  300 void QgsProcessingAlgorithmDialogBase::setResults( 
const QVariantMap &results )
 
  310 void QgsProcessingAlgorithmDialogBase::openHelp()
 
  312   QUrl algHelp = mAlgorithm->helpUrl();
 
  313   if ( algHelp.isEmpty() )
 
  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() ) ) );
 
  318   if ( !algHelp.isEmpty() )
 
  319     QDesktopServices::openUrl( algHelp );
 
  322 void QgsProcessingAlgorithmDialogBase::toggleCollapsed()
 
  324   if ( mHelpCollapsed )
 
  326     splitter->restoreState( mSplitterState );
 
  327     mButtonCollapse->setArrowType( Qt::RightArrow );
 
  331     mSplitterState = splitter->saveState();
 
  332     splitter->setSizes( QList<int>() << 1 << 0 );
 
  333     mButtonCollapse->setArrowType( Qt::LeftArrow );
 
  335   mHelpCollapsed = !mHelpCollapsed;
 
  338 void QgsProcessingAlgorithmDialogBase::splitterChanged( 
int, 
int )
 
  340   if ( splitter->sizes().at( 1 ) == 0 )
 
  342     mHelpCollapsed = 
true;
 
  343     mButtonCollapse->setArrowType( Qt::LeftArrow );
 
  347     mHelpCollapsed = 
false;
 
  348     mButtonCollapse->setArrowType( Qt::RightArrow );
 
  352 void QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged( 
int )
 
  354   updateRunButtonVisibility();
 
  357 void QgsProcessingAlgorithmDialogBase::linkClicked( 
const QUrl &url )
 
  359   QDesktopServices::openUrl( url.toString() );
 
  362 void QgsProcessingAlgorithmDialogBase::algExecuted( 
bool successful, 
const QVariantMap & )
 
  364   mAlgorithmTask = 
nullptr;
 
  371     setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
 
  385 void QgsProcessingAlgorithmDialogBase::taskTriggered( 
QgsTask *task )
 
  387   if ( task == mAlgorithmTask )
 
  391     setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
 
  397 void QgsProcessingAlgorithmDialogBase::closeClicked()
 
  413 void QgsProcessingAlgorithmDialogBase::reportError( 
const QString &error, 
bool fatalError )
 
  415   setInfo( error, 
true );
 
  422 void QgsProcessingAlgorithmDialogBase::pushWarning( 
const QString &warning )
 
  424   setInfo( warning, 
false, 
true, 
true );
 
  428 void QgsProcessingAlgorithmDialogBase::pushInfo( 
const QString &info )
 
  434 void QgsProcessingAlgorithmDialogBase::pushCommandInfo( 
const QString &command )
 
  436   txtLog->append( QStringLiteral( 
"<code>%1<code>" ).arg( formatStringForLog( command.toHtmlEscaped() ) ) );
 
  437   scrollToBottomOfLog();
 
  441 void QgsProcessingAlgorithmDialogBase::pushDebugInfo( 
const QString &message )
 
  443   txtLog->append( QStringLiteral( 
"<span style=\"color:#777\">%1</span>" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
 
  444   scrollToBottomOfLog();
 
  448 void QgsProcessingAlgorithmDialogBase::pushConsoleInfo( 
const QString &info )
 
  450   txtLog->append( QStringLiteral( 
"<code style=\"color:#777\">%1</code>" ).arg( formatStringForLog( info.toHtmlEscaped() ) ) );
 
  451   scrollToBottomOfLog();
 
  455 QDialog *QgsProcessingAlgorithmDialogBase::createProgressDialog()
 
  457   QgsProcessingAlgorithmProgressDialog *dialog = 
new QgsProcessingAlgorithmProgressDialog( 
this );
 
  458   dialog->setWindowModality( Qt::ApplicationModal );
 
  459   dialog->setWindowTitle( windowTitle() );
 
  460   dialog->setGeometry( geometry() ); 
 
  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]()
 
  466     dialog->logTextEdit()->setHtml( txtLog->toHtml() );
 
  467     QScrollBar *sb = dialog->logTextEdit()->verticalScrollBar();
 
  468     sb->setValue( sb->maximum() );
 
  473 void QgsProcessingAlgorithmDialogBase::clearLog()
 
  478 void QgsProcessingAlgorithmDialogBase::saveLog()
 
  480   QgsSettings settings;
 
  481   QString lastUsedDir = settings.value( QStringLiteral( 
"/Processing/lastUsedLogDirectory" ), QDir::homePath() ).toString();
 
  484   const QString txtExt = tr( 
"Text files" ) + QStringLiteral( 
" (*.txt *.TXT)" );
 
  485   const QString htmlExt = tr( 
"HTML files" ) + QStringLiteral( 
" (*.html *.HTML)" );
 
  487   QString path = QFileDialog::getSaveFileName( 
this, tr( 
"Save Log to File" ), lastUsedDir, txtExt + 
";;" + htmlExt, &filter );
 
  488   if ( path.isEmpty() )
 
  493   settings.setValue( QStringLiteral( 
"/Processing/lastUsedLogDirectory" ), QFileInfo( path ).path() );
 
  495   LogFormat format = FormatPlainText;
 
  496   if ( filter == htmlExt )
 
  500   saveLogToFile( path, format );
 
  503 void QgsProcessingAlgorithmDialogBase::copyLogToClipboard()
 
  505   QMimeData *m = 
new QMimeData();
 
  506   m->setText( txtLog->toPlainText() );
 
  507   m->setHtml( txtLog->toHtml() );
 
  508   QClipboard *cb = QApplication::clipboard();
 
  511   cb->setMimeData( m, QClipboard::Selection );
 
  513   cb->setMimeData( m, QClipboard::Clipboard );
 
  516 void QgsProcessingAlgorithmDialogBase::closeEvent( QCloseEvent *e )
 
  518   if ( !mHelpCollapsed )
 
  520     QgsSettings settings;
 
  521     settings.setValue( QStringLiteral( 
"/Processing/dialogBaseSplitter" ), splitter->saveState() );
 
  524   QDialog::closeEvent( e );
 
  526   if ( !mAlgorithmTask )
 
  535 void QgsProcessingAlgorithmDialogBase::runAlgorithm()
 
  540 void QgsProcessingAlgorithmDialogBase::setPercentage( 
double percent )
 
  543   if ( progressBar->maximum() == 0 )
 
  544     progressBar->setMaximum( 100 );
 
  545   progressBar->setValue( percent );
 
  549 void QgsProcessingAlgorithmDialogBase::setProgressText( 
const QString &text )
 
  551   lblProgress->setText( text );
 
  552   setInfo( text, 
false );
 
  553   scrollToBottomOfLog();
 
  560   if ( !text.isEmpty() )
 
  562     QStringList paragraphs = text.split( 
'\n' );
 
  564     for ( 
const QString ¶graph : paragraphs )
 
  566       help += QStringLiteral( 
"<p>%1</p>" ).arg( paragraph );
 
  578 void QgsProcessingAlgorithmDialogBase::processEvents()
 
  580   if ( mAlgorithmTask )
 
  596   while ( ++nIters < 100 )
 
  599     QCoreApplication::processEvents();
 
  603 void QgsProcessingAlgorithmDialogBase::scrollToBottomOfLog()
 
  605   QScrollBar *sb = txtLog->verticalScrollBar();
 
  606   sb->setValue( sb->maximum() );
 
  609 void QgsProcessingAlgorithmDialogBase::resetGui()
 
  611   lblProgress->clear();
 
  612   progressBar->setMaximum( 100 );
 
  613   progressBar->setValue( 0 );
 
  614   mButtonRun->setEnabled( 
true );
 
  615   mButtonChangeParameters->setEnabled( 
true );
 
  616   mButtonClose->setEnabled( 
true );
 
  619     mMainWidget->setEnabled( 
true );
 
  621   updateRunButtonVisibility();
 
  622   resetAdditionalGui();
 
  625 void QgsProcessingAlgorithmDialogBase::updateRunButtonVisibility()
 
  628   bool runButtonVisible = mTabWidget->currentIndex() == 0;
 
  629   mButtonRun->setVisible( runButtonVisible );
 
  630   mButtonChangeParameters->setVisible( !runButtonVisible && mExecutedAnyResult && mButtonChangeParameters->isEnabled() );
 
  633 void QgsProcessingAlgorithmDialogBase::resetAdditionalGui()
 
  638 void QgsProcessingAlgorithmDialogBase::blockControlsWhileRunning()
 
  640   mButtonRun->setEnabled( 
false );
 
  641   mButtonChangeParameters->setEnabled( 
false );
 
  644     mMainWidget->setEnabled( 
false );
 
  646   blockAdditionalControlsWhileRunning();
 
  649 void QgsProcessingAlgorithmDialogBase::blockAdditionalControlsWhileRunning()
 
  654 QgsMessageBar *QgsProcessingAlgorithmDialogBase::messageBar()
 
  659 void QgsProcessingAlgorithmDialogBase::hideShortHelp()
 
  661   textShortHelp->setVisible( 
false );
 
  666   mAlgorithmTask = task;
 
  671 QString QgsProcessingAlgorithmDialogBase::formatStringForLog( 
const QString &
string )
 
  674   s.replace( 
'\n', QLatin1String( 
"<br>" ) );
 
  678 void QgsProcessingAlgorithmDialogBase::setInfo( 
const QString &message, 
bool isError, 
bool escapeHtml, 
bool isWarning )
 
  682   if ( isError || isWarning )
 
  683     txtLog->append( QStringLiteral( 
"<span style=\"color:%1\">%2</span>" ).arg( isError ? QStringLiteral( 
"red" ) : QStringLiteral( 
"#b85a20" ), escapeHtml ? formatStringForLog( message.toHtmlEscaped() ) : formatStringForLog( message ) ) );
 
  684   else if ( escapeHtml )
 
  685     txtLog->append( QStringLiteral( 
"<span>%1</span" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
 
  687     txtLog->append( QStringLiteral( 
"<span>%1</span>" ).arg( formatStringForLog( message ) ) );
 
  688   scrollToBottomOfLog();
 
  692 void QgsProcessingAlgorithmDialogBase::reject()
 
  694   if ( !mAlgorithmTask )
 
  696     setAttribute( Qt::WA_DeleteOnClose );
 
  705 QgsProcessingAlgorithmProgressDialog::QgsProcessingAlgorithmProgressDialog( QWidget *parent )
 
  711 QProgressBar *QgsProcessingAlgorithmProgressDialog::progressBar()
 
  716 QPushButton *QgsProcessingAlgorithmProgressDialog::cancelButton()
 
  718   return mButtonBox->button( QDialogButtonBox::Cancel );
 
  721 QTextEdit *QgsProcessingAlgorithmProgressDialog::logTextEdit()
 
  726 void QgsProcessingAlgorithmProgressDialog::reject()
 
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 ...
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...
@ HigDialogTitleIsTitleCase
Dialog titles should be title case.
static QgsGui::HigFlags higFlags()
Returns the platform's HIG flags.
static QUrl helpUrl(const QString &key)
Returns URI of the help topic for the given key.
A bar for displaying non-blocking messages to the user.
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