Start moving processing algorithm dialog to c++

In an attempt to avoid Python global interpreter locks which
block the UI thread.
This commit is contained in:
Nyall Dawson 2017-11-29 14:38:08 +10:00
parent 3c238a2ba4
commit ce170918c9
11 changed files with 1011 additions and 68 deletions

View File

@ -148,6 +148,7 @@ IF (WITH_GUI)
${CMAKE_SOURCE_DIR}/src/gui/layertree
${CMAKE_SOURCE_DIR}/src/gui/layout
${CMAKE_SOURCE_DIR}/src/gui/locator
${CMAKE_SOURCE_DIR}/src/gui/processing
${CMAKE_BINARY_DIR}/src/gui
)

View File

@ -310,4 +310,5 @@
%Include layout/qgslayoutviewtooltemporarymousepan.sip
%Include layout/qgslayoutviewtoolzoom.sip
%Include locator/qgslocatorwidget.sip
%Include processing/qgsprocessingalgorithmdialogbase.sip
%Include qgsadvanceddigitizingcanvasitem.sip

View File

@ -0,0 +1,191 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/processing/qgsprocessingalgorithmdialogbase.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsProcessingAlgorithmDialogBase : QDialog
{
%Docstring
Base class for processing algorithm dialogs.
.. note::
This is not considered stable API and may change in future QGIS versions.
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgsprocessingalgorithmdialogbase.h"
%End
public:
QgsProcessingAlgorithmDialogBase( QWidget *parent = 0, Qt::WindowFlags flags = 0 );
%Docstring
Constructor for QgsProcessingAlgorithmDialogBase.
%End
void setAlgorithm( QgsProcessingAlgorithm *algorithm );
%Docstring
Sets the ``algorithm`` to run in the dialog.
.. seealso:: algorithm()
%End
QgsProcessingAlgorithm *algorithm();
%Docstring
Returns the algorithm running in the dialog.
.. seealso:: setAlgorithm()
:rtype: QgsProcessingAlgorithm
%End
void setMainWidget( QWidget *widget /Transfer/ );
%Docstring
Sets the main ``widget`` for the dialog, usually a panel for configuring algorithm parameters.
.. seealso:: mainWidget()
%End
QWidget *mainWidget();
%Docstring
Returns the main widget for the dialog, usually a panel for configuring algorithm parameters.
.. seealso:: setMainWidget()
:rtype: QWidget
%End
void showLog();
%Docstring
Switches the dialog to the log page.
%End
bool wasExecuted() const;
%Docstring
Returns true if an algorithm was executed in the dialog.
:rtype: bool
%End
QgsProcessingFeedback *createFeedback() /Factory/;
%Docstring
Creates a new processing feedback object, automatically connected to the appropriate
slots in this dialog.
:rtype: QgsProcessingFeedback
%End
virtual QVariantMap getParameterValues() const;
%Docstring
Returns the parameter values for the algorithm to run in the dialog.
:rtype: QVariantMap
%End
public slots:
virtual void accept();
virtual void reject();
void reportError( const QString &error );
%Docstring
Reports an ``error`` string to the dialog's log.
%End
void pushInfo( const QString &info );
%Docstring
Pushes an information string to the dialog's log.
%End
void pushDebugInfo( const QString &message );
%Docstring
Pushes a debug info string to the dialog's log.
%End
void pushCommandInfo( const QString &info );
%Docstring
Pushes command info to the dialog's log.
%End
void setPercentage( double percent );
%Docstring
Sets the percentage progress for the dialog, between 0 and 100.
%End
void setProgressText( const QString &text );
%Docstring
Sets a progress text message.
%End
void pushConsoleInfo( const QString &info );
%Docstring
Pushes a console info string to the dialog's log.
%End
protected:
virtual void closeEvent( QCloseEvent *e );
QPushButton *runButton();
%Docstring
Returns the dialog's run button.
:rtype: QPushButton
%End
QPushButton *cancelButton();
%Docstring
Returns the dialog's cancel button.
:rtype: QPushButton
%End
QDialogButtonBox *buttonBox();
%Docstring
Returns the dialog's button box.
:rtype: QDialogButtonBox
%End
void clearProgress();
%Docstring
Clears any current progress from the dialog.
%End
void setExecuted( bool executed );
%Docstring
Sets whether the algorithm was executed through the dialog.
%End
void setInfo( const QString &message, bool isError = false, bool escapeHtml = true );
%Docstring
Displays an info ``message`` in the dialog's log.
%End
void resetGui();
%Docstring
Resets the dialog's gui, ready for another algorithm execution.
%End
QgsMessageBar *messageBar();
%Docstring
Returns the dialog's message bar.
:rtype: QgsMessageBar
%End
protected slots:
virtual void finished( bool successful, const QVariantMap &result, QgsProcessingContext &context, QgsProcessingFeedback *feedback );
%Docstring
Called when the algorithm has finished executing.
%End
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/processing/qgsprocessingalgorithmdialogbase.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -43,7 +43,8 @@ from qgis.core import (QgsProject,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterRasterDestination,
QgsProcessingAlgorithm)
from qgis.gui import QgsMessageBar
from qgis.gui import (QgsMessageBar,
QgsProcessingAlgorithmDialogBase)
from qgis.utils import iface
from processing.core.ProcessingLog import ProcessingLog
@ -58,43 +59,38 @@ from processing.gui.Postprocessing import handleAlgorithmResults
from processing.tools import dataobjects
class AlgorithmDialog(AlgorithmDialogBase):
class AlgorithmDialog(QgsProcessingAlgorithmDialogBase):
def __init__(self, alg):
AlgorithmDialogBase.__init__(self, alg)
self.alg = alg
super().__init__()
self.setAlgorithm(alg)
self.setMainWidget(self.getParametersPanel(alg, self))
self.bar = QgsMessageBar()
self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
self.layout().insertWidget(0, self.bar)
self.runAsBatchButton = QPushButton(QCoreApplication.translate("AlgorithmDialog", "Run as Batch Process…"))
self.runAsBatchButton.clicked.connect(self.runAsBatch)
self.buttonBox.addButton(self.runAsBatchButton, QDialogButtonBox.ResetRole) # reset role to ensure left alignment
self.buttonBox().addButton(self.runAsBatchButton, QDialogButtonBox.ResetRole) # reset role to ensure left alignment
def getParametersPanel(self, alg, parent):
return ParametersPanel(parent, alg)
def runAsBatch(self):
self.close()
dlg = BatchAlgorithmDialog(self.alg)
dlg = BatchAlgorithmDialog(self.algorithm())
dlg.show()
dlg.exec_()
def getParamValues(self):
parameters = {}
if not hasattr(self, 'mainWidget') or self.mainWidget is None:
if self.mainWidget() is None:
return parameters
for param in self.alg.parameterDefinitions():
for param in self.algorithm().parameterDefinitions():
if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue
if not param.isDestination():
wrapper = self.mainWidget.wrappers[param.name()]
wrapper = self.mainWidget().wrappers[param.name()]
value = None
if wrapper.widget:
value = wrapper.value()
@ -106,10 +102,10 @@ class AlgorithmDialog(AlgorithmDialogBase):
dest_project = None
if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \
isinstance(param, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination)):
if self.mainWidget.checkBoxes[param.name()].isChecked():
if self.mainWidget().checkBoxes[param.name()].isChecked():
dest_project = QgsProject.instance()
value = self.mainWidget.outputWidgets[param.name()].getValue()
value = self.mainWidget().outputWidgets[param.name()].getValue()
if value and isinstance(value, QgsProcessingOutputLayerDefinition):
value.destinationProject = dest_project
if value:
@ -123,7 +119,7 @@ class AlgorithmDialog(AlgorithmDialogBase):
context = dataobjects.createContext()
projectCRS = iface.mapCanvas().mapSettings().destinationCrs()
layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance())
for param in self.alg.parameterDefinitions():
for param in self.algorithm().parameterDefinitions():
if isinstance(param, (ParameterRaster, ParameterVector, ParameterMultipleInput)):
if param.value:
if isinstance(param, ParameterMultipleInput):
@ -144,15 +140,13 @@ class AlgorithmDialog(AlgorithmDialogBase):
if param.skip_crs_check:
continue
value = self.mainWidget.wrappers[param.name()].widget.leText.text().strip()
value = self.mainWidget().wrappers[param.name()].widget.leText.text().strip()
if value:
hasExtent = True
return hasExtent and unmatchingCRS
def accept(self):
super(AlgorithmDialog, self)._saveGeometry()
feedback = self.createFeedback()
context = dataobjects.createContext(feedback)
@ -160,7 +154,7 @@ class AlgorithmDialog(AlgorithmDialogBase):
try:
parameters = self.getParamValues()
if checkCRS and not self.alg.validateInputCrs(parameters, context):
if checkCRS and not self.algorithm().validateInputCrs(parameters, context):
reply = QMessageBox.question(self, self.tr("Unmatching CRS's"),
self.tr('Layers do not all use the same CRS. This can '
'cause unexpected results.\nDo you want to '
@ -181,14 +175,14 @@ class AlgorithmDialog(AlgorithmDialogBase):
QMessageBox.No)
if reply == QMessageBox.No:
return
ok, msg = self.alg.checkParameterValues(parameters, context)
ok, msg = self.algorithm().checkParameterValues(parameters, context)
if msg:
QMessageBox.warning(
self, self.tr('Unable to execute algorithm'), msg)
return
self.btnRun.setEnabled(False)
self.btnClose.setEnabled(False)
buttons = self.mainWidget.iterateButtons
self.runButton().setEnabled(False)
self.cancelButton().setEnabled(False)
buttons = self.mainWidget().iterateButtons
self.iterateParam = None
for i in range(len(list(buttons.values()))):
@ -197,41 +191,41 @@ class AlgorithmDialog(AlgorithmDialogBase):
self.iterateParam = list(buttons.keys())[i]
break
self.progressBar.setMaximum(0)
self.lblProgress.setText(self.tr('Processing algorithm...'))
self.clearProgress()
self.setProgressText(self.tr('Processing algorithm...'))
# Make sure the Log tab is visible before executing the algorithm
try:
self.tabWidget.setCurrentIndex(1)
self.showLog()
self.repaint()
except:
pass
self.setInfo(
self.tr('<b>Algorithm \'{0}\' starting...</b>').format(self.alg.displayName()), escape_html=False)
self.tr('<b>Algorithm \'{0}\' starting...</b>').format(self.algorithm().displayName()), escapeHtml=False)
feedback.pushInfo(self.tr('Input parameters:'))
display_params = []
for k, v in parameters.items():
display_params.append("'" + k + "' : " + self.alg.parameterDefinition(k).valueAsPythonString(v, context))
display_params.append("'" + k + "' : " + self.algorithm().parameterDefinition(k).valueAsPythonString(v, context))
feedback.pushCommandInfo('{ ' + ', '.join(display_params) + ' }')
feedback.pushInfo('')
start_time = time.time()
if self.iterateParam:
self.buttonCancel.setEnabled(self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel)
if executeIterating(self.alg, parameters, self.iterateParam, context, feedback):
self.cancelButton().setEnabled(self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanCancel)
if executeIterating(self.algorithm(), parameters, self.iterateParam, context, feedback):
feedback.pushInfo(
self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - start_time)))
self.buttonCancel.setEnabled(False)
self.cancelButton().setEnabled(False)
self.finish(True, parameters, context, feedback)
else:
self.buttonCancel.setEnabled(False)
self.resetGUI()
self.cancelButton().setEnabled(False)
self.resetGui()
else:
command = self.alg.asPythonCommand(parameters, context)
command = self.algorithm().asPythonCommand(parameters, context)
if command:
ProcessingLog.addToLog(command)
self.buttonCancel.setEnabled(self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel)
self.cancelButton().setEnabled(self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanCancel)
def on_complete(ok, results):
if ok:
@ -243,25 +237,25 @@ class AlgorithmDialog(AlgorithmDialogBase):
self.tr('Execution failed after {0:0.2f} seconds'.format(time.time() - start_time)))
feedback.pushInfo('')
self.buttonCancel.setEnabled(False)
self.cancelButton().setEnabled(False)
self.finish(ok, results, context, feedback)
task = QgsProcessingAlgRunnerTask(self.alg, parameters, context, feedback)
task = QgsProcessingAlgRunnerTask(self.algorithm(), parameters, context, feedback)
task.executed.connect(on_complete)
QgsApplication.taskManager().addTask(task)
except AlgorithmDialogBase.InvalidParameterValue as e:
try:
self.buttonBox.accepted.connect(lambda e=e:
e.widget.setPalette(QPalette()))
self.buttonBox().accepted.connect(lambda e=e:
e.widget.setPalette(QPalette()))
palette = e.widget.palette()
palette.setColor(QPalette.Base, QColor(255, 255, 0))
e.widget.setPalette(palette)
except:
pass
self.bar.clearWidgets()
self.bar.pushMessage("", self.tr("Wrong or missing parameter value: {0}").format(e.parameter.description()),
level=QgsMessageBar.WARNING, duration=5)
self.messageBar().clearWidgets()
self.messageBar().pushMessage("", self.tr("Wrong or missing parameter value: {0}").format(e.parameter.description()),
level=QgsMessageBar.WARNING, duration=5)
def finish(self, successful, result, context, feedback):
keepOpen = not successful or ProcessingConfig.getSetting(ProcessingConfig.KEEP_DIALOG_OPEN)
@ -269,23 +263,23 @@ class AlgorithmDialog(AlgorithmDialogBase):
if self.iterateParam is None:
# add html results to results dock
for out in self.alg.outputDefinitions():
for out in self.algorithm().outputDefinitions():
if isinstance(out, QgsProcessingOutputHtml) and out.name() in result and result[out.name()]:
resultsList.addResult(icon=self.alg.icon(), name=out.description(),
resultsList.addResult(icon=self.algorithm().icon(), name=out.description(),
result=result[out.name()])
if not handleAlgorithmResults(self.alg, context, feedback, not keepOpen):
self.resetGUI()
if not handleAlgorithmResults(self.algorithm(), context, feedback, not keepOpen):
self.resetGui()
return
self.executed = True
self.setInfo(self.tr('Algorithm \'{0}\' finished').format(self.alg.displayName()), escape_html=False)
self.setExecuted(True)
self.setInfo(self.tr('Algorithm \'{0}\' finished').format(self.algorithm().displayName()), escapeHtml=False)
if not keepOpen:
self.close()
else:
self.resetGUI()
if self.alg.hasHtmlOutputs():
self.resetGui()
if self.algorithm().hasHtmlOutputs():
self.setInfo(
self.tr('HTML output has been generated by this algorithm.'
'\nOpen the results dialog to check it.'), escape_html=False)
'\nOpen the results dialog to check it.'), escapeHtml=False)

View File

@ -41,7 +41,8 @@ from qgis.core import (QgsProcessingParameterDefinition,
QgsProcessingOutputString,
QgsProject)
from qgis.gui import QgsMessageBar
from qgis.gui import (QgsMessageBar,
QgsProcessingAlgorithmDialogBase)
from qgis.utils import OverrideCursor
from processing.gui.BatchPanel import BatchPanel
@ -57,7 +58,7 @@ from processing.tools import dataobjects
import codecs
class BatchAlgorithmDialog(AlgorithmDialogBase):
class BatchAlgorithmDialog(QgsProcessingAlgorithmDialogBase):
def __init__(self, alg):
AlgorithmDialogBase.__init__(self, alg)
@ -65,15 +66,9 @@ class BatchAlgorithmDialog(AlgorithmDialogBase):
self.alg = alg
self.setWindowTitle(self.tr('Batch Processing - {0}').format(self.alg.displayName()))
self.setMainWidget(BatchPanel(self, self.alg))
self.textShortHelp.setVisible(False)
self.bar = QgsMessageBar()
self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
self.layout().insertWidget(0, self.bar)
def accept(self):
alg_parameters = []
load = []
@ -92,9 +87,9 @@ class BatchAlgorithmDialog(AlgorithmDialogBase):
wrapper = self.mainWidget.wrappers[row][col]
parameters[param.name()] = wrapper.value()
if not param.checkValueIsAcceptable(wrapper.value()):
self.bar.pushMessage("", self.tr('Wrong or missing parameter value: {0} (row {1})').format(
param.description(), row + 1),
level=QgsMessageBar.WARNING, duration=5)
self.messageBar().pushMessage("", self.tr('Wrong or missing parameter value: {0} (row {1})').format(
param.description(), row + 1),
level=QgsMessageBar.WARNING, duration=5)
return
col += 1
count_visible_outputs = 0
@ -114,9 +109,9 @@ class BatchAlgorithmDialog(AlgorithmDialogBase):
parameters[out.name()] = text
col += 1
else:
self.bar.pushMessage("", self.tr('Wrong or missing output value: {0} (row {1})').format(
out.description(), row + 1),
level=QgsMessageBar.WARNING, duration=5)
self.messageBar().pushMessage("", self.tr('Wrong or missing output value: {0} (row {1})').format(
out.description(), row + 1),
level=QgsMessageBar.WARNING, duration=5)
return
alg_parameters.append(parameters)

View File

@ -293,7 +293,7 @@ class ProcessingToolbox(BASE, WIDGET):
except:
pass
canvas.setMapTool(prevMapTool)
if dlg.executed:
if dlg.wasExecuted():
showRecent = ProcessingConfig.getSetting(
ProcessingConfig.SHOW_RECENT_ALGORITHMS)
if showRecent:

View File

@ -188,6 +188,8 @@ SET(QGIS_GUI_SRCS
ogr/qgsnewogrconnection.cpp
ogr/qgsvectorlayersaveasdialog.cpp
processing/qgsprocessingalgorithmdialogbase.cpp
qgisinterface.cpp
qgsactionmenu.cpp
qgsadvanceddigitizingcanvasitem.cpp
@ -697,6 +699,8 @@ SET(QGIS_GUI_MOC_HDRS
layout/qgslayoutviewtoolzoom.h
locator/qgslocatorwidget.h
processing/qgsprocessingalgorithmdialogbase.h
)
SET_PROPERTY(GLOBAL PROPERTY QGIS_GUI_MOC_HDRS ${QGIS_GUI_MOC_HDRS})

View File

@ -0,0 +1,356 @@
/***************************************************************************
qgsprocessingalgorithmdialogbase.cpp
------------------------------------
Date : November 2017
Copyright : (C) 2017 Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsprocessingalgorithmdialogbase.h"
#include "qgssettings.h"
#include "qgshelp.h"
#include "qgsmessagebar.h"
#include "processing/qgsprocessingalgorithm.h"
#include "processing/qgsprocessingprovider.h"
#include <QToolButton>
#include <QDesktopServices>
///@cond NOT_STABLE
QgsProcessingAlgorithmDialogFeedback::QgsProcessingAlgorithmDialogFeedback( QgsProcessingAlgorithmDialogBase *dialog )
: QgsProcessingFeedback()
, mDialog( dialog )
{
}
void QgsProcessingAlgorithmDialogFeedback::setProgressText( const QString &text )
{
mDialog->setProgressText( text );
}
void QgsProcessingAlgorithmDialogFeedback::reportError( const QString &error )
{
mDialog->reportError( error );
}
void QgsProcessingAlgorithmDialogFeedback::pushInfo( const QString &info )
{
mDialog->pushInfo( info );
}
void QgsProcessingAlgorithmDialogFeedback::pushCommandInfo( const QString &info )
{
mDialog->pushCommandInfo( info );
}
void QgsProcessingAlgorithmDialogFeedback::pushDebugInfo( const QString &info )
{
mDialog->pushDebugInfo( info );
}
void QgsProcessingAlgorithmDialogFeedback::pushConsoleInfo( const QString &info )
{
mDialog->pushConsoleInfo( info );
}
//
// QgsProcessingAlgorithmDialogBase
//
QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *parent, Qt::WindowFlags flags )
: QDialog( parent, flags )
{
setupUi( this );
//don't collapse parameters panel
splitter->setCollapsible( 0, false );
// add collapse button to splitter
QSplitterHandle *splitterHandle = splitter->handle( 1 );
QVBoxLayout *handleLayout = new QVBoxLayout();
handleLayout->setContentsMargins( 0, 0, 0, 0 );
mButtonCollapse = new QToolButton( splitterHandle );
mButtonCollapse->setAutoRaise( true );
mButtonCollapse->setFixedSize( 12, 12 );
mButtonCollapse->setCursor( Qt::ArrowCursor );
handleLayout->addWidget( mButtonCollapse );
handleLayout->addStretch();
splitterHandle->setLayout( handleLayout );
QgsSettings settings;
restoreGeometry( settings.value( QStringLiteral( "/Processing/dialogBase" ) ).toByteArray() );
splitter->restoreState( settings.value( QStringLiteral( "/Processing/dialogBaseSplitter" ), QByteArray() ).toByteArray() );
mSplitterState = splitter->saveState();
splitterChanged( 0, 0 );
connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsProcessingAlgorithmDialogBase::reject );
connect( mButtonBox, &QDialogButtonBox::accepted, this, &QgsProcessingAlgorithmDialogBase::accept );
// Rename OK button to Run
mButtonRun = mButtonBox->button( QDialogButtonBox::Ok );
mButtonRun->setText( tr( "Run" ) );
buttonCancel->setEnabled( false );
mButtonClose = mButtonBox->button( QDialogButtonBox::Close );
connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsProcessingAlgorithmDialogBase::openHelp );
connect( mButtonCollapse, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::toggleCollapsed );
connect( splitter, &QSplitter::splitterMoved, this, &QgsProcessingAlgorithmDialogBase::splitterChanged );
mMessageBar = new QgsMessageBar();
mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
verticalLayout->insertWidget( 0, mMessageBar );
}
void QgsProcessingAlgorithmDialogBase::setAlgorithm( QgsProcessingAlgorithm *algorithm )
{
mAlgorithm = algorithm;
setWindowTitle( mAlgorithm->displayName() );
QString algHelp = formatHelp( algorithm );
if ( algHelp.isEmpty() )
textShortHelp->hide();
else
{
textShortHelp->document()->setDefaultStyleSheet( QStringLiteral( ".summary { margin-left: 10px; margin-right: 10px; }\n"
"h2 { color: #555555; padding-bottom: 15px; }\n"
"a { text - decoration: none; color: #3498db; font-weight: bold; }\n"
"p { color: #666666; }\n"
"b { color: #333333; }\n"
"dl dd { margin - bottom: 5px; }" ) );
textShortHelp->setHtml( algHelp );
connect( textShortHelp, &QTextBrowser::anchorClicked, this, &QgsProcessingAlgorithmDialogBase::linkClicked );
}
}
QgsProcessingAlgorithm *QgsProcessingAlgorithmDialogBase::algorithm()
{
return mAlgorithm;
}
void QgsProcessingAlgorithmDialogBase::setMainWidget( QWidget *widget )
{
if ( mMainWidget )
{
//QgsProject.instance().layerWasAdded.disconnect( self.mainWidget.layerRegistryChanged )
//QgsProject.instance().layersWillBeRemoved.disconnect( self.mainWidget.layerRegistryChanged )
mMainWidget->deleteLater();
}
mMainWidget = widget;
tabWidget->widget( 0 )->layout()->addWidget( mMainWidget );
//QgsProject.instance().layerWasAdded.connect( self.mainWidget.layerRegistryChanged )
//QgsProject.instance().layersWillBeRemoved.connect( self.mainWidget.layerRegistryChanged )
}
QWidget *QgsProcessingAlgorithmDialogBase::mainWidget()
{
return mMainWidget;
}
QVariantMap QgsProcessingAlgorithmDialogBase::getParameterValues() const
{
return QVariantMap();
}
QgsProcessingFeedback *QgsProcessingAlgorithmDialogBase::createFeedback()
{
auto feedback = qgis::make_unique< QgsProcessingAlgorithmDialogFeedback >( this );
connect( feedback.get(), &QgsProcessingFeedback::progressChanged, this, &QgsProcessingAlgorithmDialogBase::setPercentage );
connect( buttonCancel, &QPushButton::clicked, feedback.get(), &QgsProcessingFeedback::cancel );
return feedback.release();
}
QDialogButtonBox *QgsProcessingAlgorithmDialogBase::buttonBox()
{
return mButtonBox;
}
void QgsProcessingAlgorithmDialogBase::showLog()
{
tabWidget->setCurrentIndex( 1 );
}
void QgsProcessingAlgorithmDialogBase::closeEvent( QCloseEvent *e )
{
saveWindowGeometry();
QDialog::closeEvent( e );
}
QPushButton *QgsProcessingAlgorithmDialogBase::runButton()
{
return mButtonRun;
}
QPushButton *QgsProcessingAlgorithmDialogBase::cancelButton()
{
return buttonCancel;
}
void QgsProcessingAlgorithmDialogBase::clearProgress()
{
progressBar->setMaximum( 0 );
}
void QgsProcessingAlgorithmDialogBase::setExecuted( bool executed )
{
mExecuted = executed;
}
void QgsProcessingAlgorithmDialogBase::finished( bool, const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
{
}
void QgsProcessingAlgorithmDialogBase::accept()
{
}
void QgsProcessingAlgorithmDialogBase::reject()
{
saveWindowGeometry();
QDialog::reject();
}
void QgsProcessingAlgorithmDialogBase::openHelp()
{
QUrl algHelp = mAlgorithm->helpUrl();
if ( algHelp.isEmpty() )
{
algHelp = QgsHelp::helpUrl( QStringLiteral( "processing_algs/%1/%2" ).arg( mAlgorithm->provider()->id(), mAlgorithm->id() ) );
}
if ( !algHelp.isEmpty() )
QDesktopServices::openUrl( algHelp );
}
void QgsProcessingAlgorithmDialogBase::toggleCollapsed()
{
if ( mHelpCollapsed )
{
splitter->restoreState( mSplitterState );
mButtonCollapse->setArrowType( Qt::RightArrow );
}
else
{
mSplitterState = splitter->saveState();
splitter->setSizes( QList<int>() << 1 << 0 );
mButtonCollapse->setArrowType( Qt::LeftArrow );
}
mHelpCollapsed = !mHelpCollapsed;
}
void QgsProcessingAlgorithmDialogBase::splitterChanged( int, int )
{
if ( splitter->sizes().at( 1 ) == 0 )
{
mHelpCollapsed = true;
mButtonCollapse->setArrowType( Qt::LeftArrow );
}
else
{
mHelpCollapsed = false;
mButtonCollapse->setArrowType( Qt::RightArrow );
}
}
void QgsProcessingAlgorithmDialogBase::linkClicked( const QUrl &url )
{
QDesktopServices::openUrl( url.toString() );
}
void QgsProcessingAlgorithmDialogBase::reportError( const QString &error )
{
setInfo( error, true );
resetGui();
tabWidget->setCurrentIndex( 1 );
}
void QgsProcessingAlgorithmDialogBase::pushInfo( const QString &info )
{
setInfo( info );
}
void QgsProcessingAlgorithmDialogBase::pushCommandInfo( const QString &command )
{
txtLog->append( QStringLiteral( "<code>%1<code>" ).arg( command.toHtmlEscaped() ) );
}
void QgsProcessingAlgorithmDialogBase::pushDebugInfo( const QString &message )
{
txtLog->append( QStringLiteral( "<span style=\"color:blue\">%1</span>" ).arg( message.toHtmlEscaped() ) );
}
void QgsProcessingAlgorithmDialogBase::pushConsoleInfo( const QString &info )
{
txtLog->append( QStringLiteral( "<code><span style=\"color:blue\">%1</darkgray></code>" ).arg( info.toHtmlEscaped() ) );
}
void QgsProcessingAlgorithmDialogBase::setPercentage( double percent )
{
// delay setting maximum progress value until we know algorithm reports progress
if ( progressBar->maximum() == 0 )
progressBar->setMaximum( 100 );
progressBar->setValue( percent );
}
void QgsProcessingAlgorithmDialogBase::setProgressText( const QString &text )
{
lblProgress->setText( text );
setInfo( text, false );
}
QString QgsProcessingAlgorithmDialogBase::formatHelp( QgsProcessingAlgorithm *algorithm )
{
QString text = algorithm->shortHelpString();
if ( !text.isEmpty() )
{
QStringList paragraphs = text.split( '\n' );
QString help;
for ( const QString &paragraph : paragraphs )
{
help += QStringLiteral( "<p>%1</p>" ).arg( paragraph );
}
return QStringLiteral( "<h2>%1</h2>%2" ).arg( algorithm->displayName(), help );
}
else
return QString();
}
void QgsProcessingAlgorithmDialogBase::saveWindowGeometry()
{
}
void QgsProcessingAlgorithmDialogBase::resetGui()
{
lblProgress->clear();
progressBar->setMaximum( 100 );
progressBar->setValue( 0 );
mButtonRun->setEnabled( true );
mButtonClose->setEnabled( true );
}
QgsMessageBar *QgsProcessingAlgorithmDialogBase::messageBar()
{
return mMessageBar;
}
void QgsProcessingAlgorithmDialogBase::setInfo( const QString &message, bool isError, bool escapeHtml )
{
if ( isError )
txtLog->append( QStringLiteral( "<span style=\"color:red\">%1</span><br />" ).arg( message ) );
else if ( escapeHtml )
txtLog->append( message.toHtmlEscaped() );
else
txtLog->append( message );
}
///@endcond

View File

@ -0,0 +1,248 @@
/***************************************************************************
qgsprocessingalgorithmdialogbase.h
----------------------------------
Date : November 2017
Copyright : (C) 2017 Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSPROCESSINGALGORITHMDIALOGBASE_H
#define QGSPROCESSINGALGORITHMDIALOGBASE_H
#include "qgis.h"
#include "qgis_gui.h"
#include "ui_qgsprocessingalgorithmdialogbase.h"
#include "processing/qgsprocessingcontext.h"
#include "processing/qgsprocessingfeedback.h"
///@cond NOT_STABLE
class QgsProcessingAlgorithm;
class QToolButton;
class QgsProcessingAlgorithmDialogBase;
class QgsMessageBar;
#ifndef SIP_RUN
/**
* \ingroup gui
* \brief QgsProcessingFeedback subclass linked to a QgsProcessingAlgorithmDialogBase
* \note Not stable API
* \since QGIS 3.0
*/
class QgsProcessingAlgorithmDialogFeedback : public QgsProcessingFeedback
{
Q_OBJECT
public:
/**
* Constructor for QgsProcessingAlgorithmDialogFeedback.
*/
QgsProcessingAlgorithmDialogFeedback( QgsProcessingAlgorithmDialogBase *dialog );
public slots:
void setProgressText( const QString &text ) override;
void reportError( const QString &error ) override;
void pushInfo( const QString &info ) override;
void pushCommandInfo( const QString &info ) override;
void pushDebugInfo( const QString &info ) override;
void pushConsoleInfo( const QString &info ) override;
private:
QgsProcessingAlgorithmDialogBase *mDialog = nullptr;
};
#endif
/**
* \ingroup gui
* \brief Base class for processing algorithm dialogs.
* \note This is not considered stable API and may change in future QGIS versions.
* \since QGIS 3.0
*/
class GUI_EXPORT QgsProcessingAlgorithmDialogBase : public QDialog, private Ui::QgsProcessingDialogBase
{
Q_OBJECT
public:
/**
* Constructor for QgsProcessingAlgorithmDialogBase.
*/
QgsProcessingAlgorithmDialogBase( QWidget *parent = nullptr, Qt::WindowFlags flags = 0 );
/**
* Sets the \a algorithm to run in the dialog.
* \see algorithm()
*/
void setAlgorithm( QgsProcessingAlgorithm *algorithm );
/**
* Returns the algorithm running in the dialog.
* \see setAlgorithm()
*/
QgsProcessingAlgorithm *algorithm();
/**
* Sets the main \a widget for the dialog, usually a panel for configuring algorithm parameters.
* \see mainWidget()
*/
void setMainWidget( QWidget *widget SIP_TRANSFER );
/**
* Returns the main widget for the dialog, usually a panel for configuring algorithm parameters.
* \see setMainWidget()
*/
QWidget *mainWidget();
/**
* Switches the dialog to the log page.
*/
void showLog();
/**
* Returns true if an algorithm was executed in the dialog.
*/
bool wasExecuted() const { return mExecuted; }
/**
* Creates a new processing feedback object, automatically connected to the appropriate
* slots in this dialog.
*/
QgsProcessingFeedback *createFeedback() SIP_FACTORY;
/**
* Returns the parameter values for the algorithm to run in the dialog.
*/
virtual QVariantMap getParameterValues() const;
public slots:
void accept() override;
void reject() override;
/**
* Reports an \a error string to the dialog's log.
*/
void reportError( const QString &error );
/**
* Pushes an information string to the dialog's log.
*/
void pushInfo( const QString &info );
/**
* Pushes a debug info string to the dialog's log.
*/
void pushDebugInfo( const QString &message );
/**
* Pushes command info to the dialog's log.
*/
void pushCommandInfo( const QString &info );
/**
* Sets the percentage progress for the dialog, between 0 and 100.
*/
void setPercentage( double percent );
/**
* Sets a progress text message.
*/
void setProgressText( const QString &text );
/**
* Pushes a console info string to the dialog's log.
*/
void pushConsoleInfo( const QString &info );
protected:
void closeEvent( QCloseEvent *e ) override;
/**
* Returns the dialog's run button.
*/
QPushButton *runButton();
/**
* Returns the dialog's cancel button.
*/
QPushButton *cancelButton();
/**
* Returns the dialog's button box.
*/
QDialogButtonBox *buttonBox();
/**
* Clears any current progress from the dialog.
*/
void clearProgress();
/**
* Sets whether the algorithm was executed through the dialog.
*/
void setExecuted( bool executed );
/**
* Displays an info \a message in the dialog's log.
*/
void setInfo( const QString &message, bool isError = false, bool escapeHtml = true );
/**
* Resets the dialog's gui, ready for another algorithm execution.
*/
void resetGui();
/**
* Returns the dialog's message bar.
*/
QgsMessageBar *messageBar();
protected slots:
/**
* Called when the algorithm has finished executing.
*/
virtual void finished( bool successful, const QVariantMap &result, QgsProcessingContext &context, QgsProcessingFeedback *feedback );
private slots:
void openHelp();
void toggleCollapsed();
void splitterChanged( int pos, int index );
void linkClicked( const QUrl &url );
private:
QPushButton *mButtonRun = nullptr;
QPushButton *mButtonClose = nullptr;
QByteArray mSplitterState;
QToolButton *mButtonCollapse = nullptr;
QgsMessageBar *mMessageBar = nullptr;
bool mExecuted = false;
QWidget *mMainWidget = nullptr;
QgsProcessingAlgorithm *mAlgorithm = nullptr;
bool mHelpCollapsed = false;
QString formatHelp( QgsProcessingAlgorithm *algorithm );
void saveWindowGeometry();
};
///@endcond
#endif // QGSPROCESSINGALGORITHMDIALOGBASE_H

View File

@ -5,6 +5,7 @@ FILE(GLOB EDITORWIDGET_UIS "${CMAKE_CURRENT_SOURCE_DIR}/editorwidgets/*.ui")
FILE(GLOB PAINTEFFECT_UIS "${CMAKE_CURRENT_SOURCE_DIR}/effects/*.ui")
FILE(GLOB COMPOSER_UIS "${CMAKE_CURRENT_SOURCE_DIR}/composer/*.ui")
FILE(GLOB LAYOUT_UIS "${CMAKE_CURRENT_SOURCE_DIR}/layout/*.ui")
FILE(GLOB PROCESSING_UIS "${CMAKE_CURRENT_SOURCE_DIR}/processing/*.ui")
FILE(GLOB AUTH_UIS "${CMAKE_CURRENT_SOURCE_DIR}/auth/*.ui")
FILE(GLOB RASTER_UIS "${CMAKE_CURRENT_SOURCE_DIR}/raster/*.ui")
FILE(GLOB STYLEDOCK_UIS "${CMAKE_CURRENT_SOURCE_DIR}/styledock/*.ui")
@ -16,6 +17,7 @@ QT5_WRAP_UI(QGIS_UIS_H
${EDITORWIDGET_UIS}
${PAINTEFFECT_UIS}
${COMPOSER_UIS}
${PROCESSING_UIS}
${AUTH_UIS}
${RASTER_UIS}
${STYLEDOCK_UIS}

View File

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsProcessingDialogBase</class>
<widget class="QDialog" name="QgsProcessingDialogBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>688</width>
<height>523</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="handleWidth">
<number>16</number>
</property>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Parameters</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Log</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTextEdit" name="txtLog">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QTextBrowser" name="textShortHelp">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="accessibleName">
<string/>
</property>
<property name="openLinks">
<bool>false</bool>
</property>
</widget>
</widget>
</item>
<item>
<widget class="QLabel" name="lblProgress">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonCancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="mButtonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>