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 );
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() )
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 = qgis::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()
403 void QgsProcessingAlgorithmDialogBase::reportError(
const QString &error,
bool fatalError )
405 setInfo( error,
true );
412 void QgsProcessingAlgorithmDialogBase::pushWarning(
const QString &warning )
414 setInfo( warning,
false,
true,
true );
418 void QgsProcessingAlgorithmDialogBase::pushInfo(
const QString &info )
424 void QgsProcessingAlgorithmDialogBase::pushCommandInfo(
const QString &command )
426 txtLog->append( QStringLiteral(
"<code>%1<code>" ).arg( formatStringForLog( command.toHtmlEscaped() ) ) );
427 scrollToBottomOfLog();
431 void QgsProcessingAlgorithmDialogBase::pushDebugInfo(
const QString &message )
433 txtLog->append( QStringLiteral(
"<span style=\"color:#777\">%1</span>" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
434 scrollToBottomOfLog();
438 void QgsProcessingAlgorithmDialogBase::pushConsoleInfo(
const QString &info )
440 txtLog->append( QStringLiteral(
"<code style=\"color:#777\">%1</code>" ).arg( formatStringForLog( info.toHtmlEscaped() ) ) );
441 scrollToBottomOfLog();
445 QDialog *QgsProcessingAlgorithmDialogBase::createProgressDialog()
447 QgsProcessingAlgorithmProgressDialog *dialog =
new QgsProcessingAlgorithmProgressDialog(
this );
448 dialog->setWindowModality( Qt::ApplicationModal );
449 dialog->setWindowTitle( windowTitle() );
450 dialog->setGeometry( geometry() );
451 connect( progressBar, &QProgressBar::valueChanged, dialog->progressBar(), &QProgressBar::setValue );
452 connect( dialog->cancelButton(), &QPushButton::clicked, buttonCancel, &QPushButton::click );
453 dialog->logTextEdit()->setHtml( txtLog->toHtml() );
454 connect( txtLog, &QTextEdit::textChanged, dialog, [
this, dialog]()
456 dialog->logTextEdit()->setHtml( txtLog->toHtml() );
457 QScrollBar *sb = dialog->logTextEdit()->verticalScrollBar();
458 sb->setValue( sb->maximum() );
463 void QgsProcessingAlgorithmDialogBase::clearLog()
468 void QgsProcessingAlgorithmDialogBase::saveLog()
471 QString lastUsedDir = settings.
value( QStringLiteral(
"/Processing/lastUsedLogDirectory" ), QDir::homePath() ).toString();
474 const QString txtExt = tr(
"Text files" ) + QStringLiteral(
" (*.txt *.TXT)" );
475 const QString htmlExt = tr(
"HTML files" ) + QStringLiteral(
" (*.html *.HTML)" );
477 QString path = QFileDialog::getSaveFileName(
this, tr(
"Save Log to File" ), lastUsedDir, txtExt +
";;" + htmlExt, &filter );
478 if ( path.isEmpty() )
483 settings.
setValue( QStringLiteral(
"/Processing/lastUsedLogDirectory" ), QFileInfo( path ).path() );
485 LogFormat format = FormatPlainText;
486 if ( filter == htmlExt )
490 saveLogToFile( path, format );
493 void QgsProcessingAlgorithmDialogBase::copyLogToClipboard()
495 QMimeData *m =
new QMimeData();
496 m->setText( txtLog->toPlainText() );
497 m->setHtml( txtLog->toHtml() );
498 QClipboard *cb = QApplication::clipboard();
501 cb->setMimeData( m, QClipboard::Selection );
503 cb->setMimeData( m, QClipboard::Clipboard );
506 void QgsProcessingAlgorithmDialogBase::closeEvent( QCloseEvent *e )
508 if ( !mHelpCollapsed )
511 settings.
setValue( QStringLiteral(
"/Processing/dialogBaseSplitter" ), splitter->saveState() );
514 QDialog::closeEvent( e );
516 if ( !mAlgorithmTask )
525 void QgsProcessingAlgorithmDialogBase::runAlgorithm()
530 void QgsProcessingAlgorithmDialogBase::setPercentage(
double percent )
533 if ( progressBar->maximum() == 0 )
534 progressBar->setMaximum( 100 );
535 progressBar->setValue( percent );
539 void QgsProcessingAlgorithmDialogBase::setProgressText(
const QString &text )
541 lblProgress->setText( text );
542 setInfo( text,
false );
543 scrollToBottomOfLog();
550 if ( !text.isEmpty() )
552 QStringList paragraphs = text.split(
'\n' );
554 for (
const QString ¶graph : paragraphs )
556 help += QStringLiteral(
"<p>%1</p>" ).arg( paragraph );
568 void QgsProcessingAlgorithmDialogBase::processEvents()
570 if ( mAlgorithmTask )
586 while ( ++nIters < 100 )
589 QCoreApplication::processEvents();
593 void QgsProcessingAlgorithmDialogBase::scrollToBottomOfLog()
595 QScrollBar *sb = txtLog->verticalScrollBar();
596 sb->setValue( sb->maximum() );
599 void QgsProcessingAlgorithmDialogBase::resetGui()
601 lblProgress->clear();
602 progressBar->setMaximum( 100 );
603 progressBar->setValue( 0 );
604 mButtonRun->setEnabled(
true );
605 mButtonChangeParameters->setEnabled(
true );
606 mButtonClose->setEnabled(
true );
609 mMainWidget->setEnabled(
true );
611 updateRunButtonVisibility();
612 resetAdditionalGui();
615 void QgsProcessingAlgorithmDialogBase::updateRunButtonVisibility()
618 bool runButtonVisible = mTabWidget->currentIndex() == 0;
619 mButtonRun->setVisible( runButtonVisible );
620 mButtonChangeParameters->setVisible( !runButtonVisible && mExecutedAnyResult && mButtonChangeParameters->isEnabled() );
623 void QgsProcessingAlgorithmDialogBase::resetAdditionalGui()
628 void QgsProcessingAlgorithmDialogBase::blockControlsWhileRunning()
630 mButtonRun->setEnabled(
false );
631 mButtonChangeParameters->setEnabled(
false );
634 mMainWidget->setEnabled(
false );
636 blockAdditionalControlsWhileRunning();
639 void QgsProcessingAlgorithmDialogBase::blockAdditionalControlsWhileRunning()
644 QgsMessageBar *QgsProcessingAlgorithmDialogBase::messageBar()
649 void QgsProcessingAlgorithmDialogBase::hideShortHelp()
651 textShortHelp->setVisible(
false );
656 mAlgorithmTask = task;
661 QString QgsProcessingAlgorithmDialogBase::formatStringForLog(
const QString &
string )
664 s.replace(
'\n', QLatin1String(
"<br>" ) );
668 void QgsProcessingAlgorithmDialogBase::setInfo(
const QString &message,
bool isError,
bool escapeHtml,
bool isWarning )
672 if ( isError || isWarning )
673 txtLog->append( QStringLiteral(
"<span style=\"color:%1\">%2</span>" ).arg( isError ? QStringLiteral(
"red" ) : QStringLiteral(
"#b85a20" ), escapeHtml ? formatStringForLog( message.toHtmlEscaped() ) : formatStringForLog( message ) ) );
674 else if ( escapeHtml )
675 txtLog->append( QStringLiteral(
"<span>%1</span" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
677 txtLog->append( QStringLiteral(
"<span>%1</span>" ).arg( formatStringForLog( message ) ) );
678 scrollToBottomOfLog();
682 void QgsProcessingAlgorithmDialogBase::reject()
684 if ( !mAlgorithmTask )
686 setAttribute( Qt::WA_DeleteOnClose );
695 QgsProcessingAlgorithmProgressDialog::QgsProcessingAlgorithmProgressDialog( QWidget *parent )
701 QProgressBar *QgsProcessingAlgorithmProgressDialog::progressBar()
706 QPushButton *QgsProcessingAlgorithmProgressDialog::cancelButton()
708 return mButtonBox->button( QDialogButtonBox::Cancel );
711 QTextEdit *QgsProcessingAlgorithmProgressDialog::logTextEdit()
716 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.
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.
This class is a composition of two QSettings instances:
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.
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