mirror of
https://github.com/qgis/QGIS.git
synced 2025-06-06 00:04:01 -04:00
Messy mockup of feature
This commit is contained in:
parent
34e29b545b
commit
681d44f11f
@ -44,6 +44,7 @@ Abstract base class for processing algorithms.
|
||||
FlagRequiresMatchingCrs,
|
||||
FlagNoThreading,
|
||||
FlagDisplayNameIsLiteral,
|
||||
FlagSupportsInPlaceEdits,
|
||||
FlagDeprecated,
|
||||
};
|
||||
typedef QFlags<QgsProcessingAlgorithm::Flag> Flags;
|
||||
@ -853,6 +854,9 @@ algorithm in "chains", avoiding the need for temporary outputs in multi-step mod
|
||||
Constructor for QgsProcessingFeatureBasedAlgorithm.
|
||||
%End
|
||||
|
||||
virtual QgsProcessingAlgorithm::Flags flags() const;
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
virtual void initAlgorithm( const QVariantMap &configuration = QVariantMap() );
|
||||
|
@ -379,6 +379,7 @@ the results.
|
||||
{
|
||||
FilterToolbox,
|
||||
FilterModeler,
|
||||
FilterInPlace,
|
||||
};
|
||||
typedef QFlags<QgsProcessingToolboxProxyModel::Filter> Filters;
|
||||
|
||||
@ -417,6 +418,8 @@ Returns any filters that affect how toolbox content is filtered.
|
||||
.. seealso:: :py:func:`setFilters`
|
||||
%End
|
||||
|
||||
void setInPlaceLayerType( QgsWkbTypes::GeometryType type );
|
||||
|
||||
void setFilterString( const QString &filter );
|
||||
%Docstring
|
||||
Sets a ``filter`` string, such that only algorithms matching the
|
||||
|
@ -73,6 +73,8 @@ if no algorithm is currently selected.
|
||||
Sets ``filters`` controlling the view's contents.
|
||||
%End
|
||||
|
||||
void setInPlaceLayerType( QgsWkbTypes::GeometryType type );
|
||||
|
||||
public slots:
|
||||
|
||||
void setFilterString( const QString &filter );
|
||||
|
@ -35,6 +35,7 @@ from qgis.core import (QgsApplication,
|
||||
QgsDataItemProvider,
|
||||
QgsDataProvider,
|
||||
QgsDataItem,
|
||||
QgsMapLayer,
|
||||
QgsMimeDataUtils)
|
||||
from qgis.gui import (QgsOptionsWidgetFactory,
|
||||
QgsCustomDropHandler)
|
||||
@ -48,7 +49,8 @@ from processing.gui.ProcessingToolbox import ProcessingToolbox
|
||||
from processing.gui.HistoryDialog import HistoryDialog
|
||||
from processing.gui.ConfigDialog import ConfigOptionsPage
|
||||
from processing.gui.ResultsDock import ResultsDock
|
||||
from processing.gui.AlgorithmLocatorFilter import AlgorithmLocatorFilter
|
||||
from processing.gui.AlgorithmLocatorFilter import (AlgorithmLocatorFilter,
|
||||
InPlaceAlgorithmLocatorFilter)
|
||||
from processing.modeler.ModelerDialog import ModelerDialog
|
||||
from processing.tools.system import tempHelpFolder
|
||||
from processing.gui.menus import removeMenus, initializeMenus, createMenus
|
||||
@ -168,6 +170,8 @@ class ProcessingPlugin:
|
||||
QgsApplication.dataItemProviderRegistry().addProvider(self.item_provider)
|
||||
self.locator_filter = AlgorithmLocatorFilter()
|
||||
iface.registerLocatorFilter(self.locator_filter)
|
||||
self.edit_features_locator_filter = InPlaceAlgorithmLocatorFilter()
|
||||
iface.registerLocatorFilter(self.edit_features_locator_filter)
|
||||
Processing.initialize()
|
||||
|
||||
def initGui(self):
|
||||
@ -233,6 +237,14 @@ class ProcessingPlugin:
|
||||
self.optionsAction.triggered.connect(self.openProcessingOptions)
|
||||
self.toolbox.processingToolbar.addAction(self.optionsAction)
|
||||
|
||||
self.editSelectedAction = QAction(
|
||||
QgsApplication.getThemeIcon("/mActionToggleEditing.svg"),
|
||||
self.tr('Edit Selected Features'), self.iface.mainWindow())
|
||||
self.editSelectedAction.setObjectName('editSelectedFeatures')
|
||||
self.editSelectedAction.setCheckable(True)
|
||||
self.editSelectedAction.toggled.connect(self.editSelected)
|
||||
self.toolbox.processingToolbar.addAction(self.editSelectedAction)
|
||||
|
||||
menuBar = self.iface.mainWindow().menuBar()
|
||||
menuBar.insertMenu(
|
||||
self.iface.firstRightStandardMenu().menuAction(), self.menu)
|
||||
@ -242,6 +254,15 @@ class ProcessingPlugin:
|
||||
initializeMenus()
|
||||
createMenus()
|
||||
|
||||
self.iface.currentLayerChanged.connect(self.layer_changed)
|
||||
|
||||
def layer_changed(self, layer):
|
||||
if layer is None or layer.type() != QgsMapLayer.VectorLayer or not layer.isEditable() or not layer.selectedFeatureCount():
|
||||
self.editSelectedAction.setChecked(False)
|
||||
self.editSelectedAction.setEnabled(False)
|
||||
else:
|
||||
self.editSelectedAction.setEnabled(True)
|
||||
|
||||
def openProcessingOptions(self):
|
||||
self.iface.showOptionsDialog(self.iface.mainWindow(), currentPage='processingOptions')
|
||||
|
||||
@ -273,6 +294,7 @@ class ProcessingPlugin:
|
||||
|
||||
self.iface.unregisterOptionsWidgetFactory(self.options_factory)
|
||||
self.iface.deregisterLocatorFilter(self.locator_filter)
|
||||
self.iface.deregisterLocatorFilter(self.edit_features_locator_filter)
|
||||
self.iface.unregisterCustomDropHandler(self.drop_handler)
|
||||
QgsApplication.dataItemProviderRegistry().removeProvider(self.item_provider)
|
||||
|
||||
@ -306,3 +328,6 @@ class ProcessingPlugin:
|
||||
|
||||
def tr(self, message):
|
||||
return QCoreApplication.translate('ProcessingPlugin', message)
|
||||
|
||||
def editSelected(self, enabled):
|
||||
self.toolbox.set_in_place_edit_mode(enabled)
|
||||
|
@ -28,6 +28,7 @@ __revision__ = '$Format:%H$'
|
||||
from qgis.PyQt.QtCore import QVariant
|
||||
from qgis.core import (QgsField,
|
||||
QgsProcessing,
|
||||
QgsProcessingAlgorithm,
|
||||
QgsProcessingParameterString,
|
||||
QgsProcessingParameterNumber,
|
||||
QgsProcessingParameterEnum,
|
||||
@ -57,6 +58,9 @@ class AddTableField(QgisFeatureBasedAlgorithm):
|
||||
self.tr('String')]
|
||||
self.field = None
|
||||
|
||||
def flags(self):
|
||||
return super().flags() & ~QgsProcessingAlgorithm.FlagSupportsInPlaceEdits
|
||||
|
||||
def initParameters(self, config=None):
|
||||
self.addParameter(QgsProcessingParameterString(self.FIELD_NAME,
|
||||
self.tr('Field name')))
|
||||
|
@ -29,6 +29,7 @@ from qgis.PyQt.QtCore import QCoreApplication
|
||||
|
||||
from qgis.core import (QgsProcessingParameterField,
|
||||
QgsProcessing,
|
||||
QgsProcessingAlgorithm,
|
||||
QgsProcessingFeatureSource)
|
||||
from processing.algs.qgis.QgisAlgorithm import QgisFeatureBasedAlgorithm
|
||||
|
||||
@ -37,6 +38,9 @@ class DeleteColumn(QgisFeatureBasedAlgorithm):
|
||||
|
||||
COLUMNS = 'COLUMN'
|
||||
|
||||
def flags(self):
|
||||
return super().flags() & ~QgsProcessingAlgorithm.FlagSupportsInPlaceEdits
|
||||
|
||||
def tags(self):
|
||||
return self.tr('drop,delete,remove,fields,columns,attributes').split(',')
|
||||
|
||||
|
@ -29,6 +29,7 @@ from qgis.core import (QgsWkbTypes,
|
||||
QgsExpression,
|
||||
QgsGeometry,
|
||||
QgsProcessing,
|
||||
QgsProcessingAlgorithm,
|
||||
QgsProcessingException,
|
||||
QgsProcessingParameterBoolean,
|
||||
QgsProcessingParameterEnum,
|
||||
@ -51,6 +52,9 @@ class GeometryByExpression(QgisFeatureBasedAlgorithm):
|
||||
def groupId(self):
|
||||
return 'vectorgeometry'
|
||||
|
||||
def flags(self):
|
||||
return super().flags() & ~QgsProcessingAlgorithm.FlagSupportsInPlaceEdits
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.geometry_types = [self.tr('Polygon'),
|
||||
|
@ -57,7 +57,7 @@ from processing.core.ProcessingResults import resultsList
|
||||
from processing.gui.ParametersPanel import ParametersPanel
|
||||
from processing.gui.BatchAlgorithmDialog import BatchAlgorithmDialog
|
||||
from processing.gui.AlgorithmDialogBase import AlgorithmDialogBase
|
||||
from processing.gui.AlgorithmExecutor import executeIterating, execute
|
||||
from processing.gui.AlgorithmExecutor import executeIterating, execute, execute_in_place
|
||||
from processing.gui.Postprocessing import handleAlgorithmResults
|
||||
from processing.gui.wrappers import WidgetWrapper
|
||||
|
||||
@ -66,19 +66,25 @@ from processing.tools import dataobjects
|
||||
|
||||
class AlgorithmDialog(QgsProcessingAlgorithmDialogBase):
|
||||
|
||||
def __init__(self, alg):
|
||||
def __init__(self, alg, in_place=False):
|
||||
super().__init__()
|
||||
self.feedback_dialog = None
|
||||
self.in_place = in_place
|
||||
|
||||
self.setAlgorithm(alg)
|
||||
self.setMainWidget(self.getParametersPanel(alg, self))
|
||||
|
||||
if not self.in_place:
|
||||
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
|
||||
else:
|
||||
self.runAsBatchButton = None
|
||||
self.buttonBox().button(QDialogButtonBox.Ok).setText('Modify Selected Features')
|
||||
self.buttonBox().button(QDialogButtonBox.Close).setText('Cancel')
|
||||
|
||||
def getParametersPanel(self, alg, parent):
|
||||
return ParametersPanel(parent, alg)
|
||||
return ParametersPanel(parent, alg, self.in_place)
|
||||
|
||||
def runAsBatch(self):
|
||||
self.close()
|
||||
@ -118,10 +124,23 @@ class AlgorithmDialog(QgsProcessingAlgorithmDialogBase):
|
||||
|
||||
value = wrapper.parameterValue()
|
||||
parameters[param.name()] = value
|
||||
if self.in_place and param.name() == 'INPUT':
|
||||
parameters[param.name()] = iface.activeLayer()
|
||||
continue
|
||||
|
||||
wrapper = self.mainWidget().wrappers[param.name()]
|
||||
value = None
|
||||
if wrapper.widget is not None:
|
||||
value = wrapper.value()
|
||||
parameters[param.name()] = value
|
||||
|
||||
if not param.checkValueIsAcceptable(value):
|
||||
raise AlgorithmDialogBase.InvalidParameterValue(param, widget)
|
||||
else:
|
||||
if self.in_place and param.name() == 'OUTPUT':
|
||||
parameters[param.name()] = 'memory:'
|
||||
continue
|
||||
|
||||
dest_project = None
|
||||
if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \
|
||||
isinstance(param, (QgsProcessingParameterRasterDestination,
|
||||
@ -226,9 +245,12 @@ class AlgorithmDialog(QgsProcessingAlgorithmDialogBase):
|
||||
|
||||
self.cancelButton().setEnabled(False)
|
||||
|
||||
if not self.in_place:
|
||||
self.finish(ok, results, context, feedback)
|
||||
elif ok:
|
||||
self.close()
|
||||
|
||||
if not (self.algorithm().flags() & QgsProcessingAlgorithm.FlagNoThreading):
|
||||
if not self.in_place and not (self.algorithm().flags() & QgsProcessingAlgorithm.FlagNoThreading):
|
||||
# Make sure the Log tab is visible before executing the algorithm
|
||||
self.showLog()
|
||||
|
||||
@ -241,6 +263,9 @@ class AlgorithmDialog(QgsProcessingAlgorithmDialogBase):
|
||||
feedback.progressChanged.connect(self.proxy_progress.setProxyProgress)
|
||||
self.feedback_dialog = self.createProgressDialog()
|
||||
self.feedback_dialog.show()
|
||||
if self.in_place:
|
||||
ok, results = execute_in_place(self.algorithm(), parameters, context, feedback)
|
||||
else:
|
||||
ok, results = execute(self.algorithm(), parameters, context, feedback)
|
||||
feedback.progressChanged.disconnect()
|
||||
self.proxy_progress.finalize(ok)
|
||||
|
@ -33,9 +33,11 @@ from qgis.core import (Qgis,
|
||||
QgsProcessingUtils,
|
||||
QgsMessageLog,
|
||||
QgsProcessingException,
|
||||
QgsProcessingFeatureSourceDefinition,
|
||||
QgsProcessingParameters)
|
||||
from processing.gui.Postprocessing import handleAlgorithmResults
|
||||
from processing.tools import dataobjects
|
||||
from qgis.utils import iface
|
||||
|
||||
|
||||
def execute(alg, parameters, context=None, feedback=None):
|
||||
@ -61,6 +63,40 @@ def execute(alg, parameters, context=None, feedback=None):
|
||||
return False, {}
|
||||
|
||||
|
||||
def execute_in_place(alg, parameters, context=None, feedback=None):
|
||||
|
||||
if feedback is None:
|
||||
feedback = QgsProcessingFeedback()
|
||||
if context is None:
|
||||
context = dataobjects.createContext(feedback)
|
||||
|
||||
parameters['INPUT'] = QgsProcessingFeatureSourceDefinition(iface.activeLayer().id(), True)
|
||||
|
||||
parameters['OUTPUT'] = 'memory:'
|
||||
|
||||
try:
|
||||
results, ok = alg.run(parameters, context, feedback)
|
||||
|
||||
layer = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context)
|
||||
iface.activeLayer().beginEditCommand('Edit features')
|
||||
iface.activeLayer().deleteFeatures(iface.activeLayer().selectedFeatureIds())
|
||||
features = []
|
||||
for f in layer.getFeatures():
|
||||
features.append(f)
|
||||
iface.activeLayer().addFeatures(features)
|
||||
new_selection = [f.id() for f in features]
|
||||
iface.activeLayer().endEditCommand()
|
||||
#iface.activeLayer().selectByIds(new_selection)
|
||||
iface.activeLayer().triggerRepaint()
|
||||
|
||||
return ok, results
|
||||
except QgsProcessingException as e:
|
||||
QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical)
|
||||
if feedback is not None:
|
||||
feedback.reportError(e.msg)
|
||||
return False, {}
|
||||
|
||||
|
||||
def executeIterating(alg, parameters, paramToIter, context, feedback):
|
||||
# Generate all single-feature layers
|
||||
parameter_definition = alg.parameterDefinition(paramToIter)
|
||||
|
@ -28,10 +28,16 @@ __revision__ = '$Format:%H$'
|
||||
|
||||
from qgis.core import (QgsApplication,
|
||||
QgsProcessingAlgorithm,
|
||||
QgsProcessingFeatureBasedAlgorithm,
|
||||
QgsLocatorFilter,
|
||||
QgsLocatorResult)
|
||||
QgsLocatorResult,
|
||||
QgsProcessing,
|
||||
QgsWkbTypes,
|
||||
QgsMapLayer,
|
||||
QgsFields)
|
||||
from processing.gui.MessageDialog import MessageDialog
|
||||
from processing.gui.AlgorithmDialog import AlgorithmDialog
|
||||
from processing.gui.AlgorithmExecutor import execute_in_place
|
||||
from qgis.utils import iface
|
||||
|
||||
|
||||
@ -101,3 +107,91 @@ class AlgorithmLocatorFilter(QgsLocatorFilter):
|
||||
except:
|
||||
pass
|
||||
canvas.setMapTool(prevMapTool)
|
||||
|
||||
|
||||
class InPlaceAlgorithmLocatorFilter(QgsLocatorFilter):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
def clone(self):
|
||||
return InPlaceAlgorithmLocatorFilter()
|
||||
|
||||
def name(self):
|
||||
return 'edit_features'
|
||||
|
||||
def displayName(self):
|
||||
return self.tr('Edit Selected Features')
|
||||
|
||||
def priority(self):
|
||||
return QgsLocatorFilter.Low
|
||||
|
||||
def prefix(self):
|
||||
return 'ef'
|
||||
|
||||
def flags(self):
|
||||
return QgsLocatorFilter.FlagFast
|
||||
|
||||
def fetchResults(self, string, context, feedback):
|
||||
# collect results in main thread, since this method is inexpensive and
|
||||
# accessing the processing registry/current layer is not thread safe
|
||||
|
||||
if iface.activeLayer() is None or iface.activeLayer().type() != QgsMapLayer.VectorLayer or not iface.activeLayer().selectedFeatureCount():
|
||||
return
|
||||
|
||||
for a in QgsApplication.processingRegistry().algorithms():
|
||||
if not a.flags() & QgsProcessingAlgorithm.FlagSupportsInPlaceEdits:
|
||||
continue
|
||||
|
||||
if a.inputLayerTypes() and \
|
||||
QgsProcessing.TypeVector not in a.inputLayerTypes() \
|
||||
and QgsProcessing.TypeVectorAnyGeometry not in a.inputLayerTypes() \
|
||||
and (QgsWkbTypes.geometryType(iface.activeLayer().wkbType()) == QgsWkbTypes.PolygonGeometry and QgsProcessing.TypeVectorPolygon not in a.inputLayerTypes() or
|
||||
QgsWkbTypes.geometryType(
|
||||
iface.activeLayer().wkbType()) == QgsWkbTypes.LineGeometry and QgsProcessing.TypeVectorLine not in a.inputLayerTypes() or
|
||||
QgsWkbTypes.geometryType(
|
||||
iface.activeLayer().wkbType()) == QgsWkbTypes.PointGeometry and QgsProcessing.TypeVectorPoint not in a.inputLayerTypes()):
|
||||
continue
|
||||
|
||||
if QgsLocatorFilter.stringMatches(a.displayName(), string) or [t for t in a.tags() if QgsLocatorFilter.stringMatches(t, string)] or \
|
||||
(context.usingPrefix and not string):
|
||||
result = QgsLocatorResult()
|
||||
result.filter = self
|
||||
result.displayString = a.displayName()
|
||||
result.icon = a.icon()
|
||||
result.userData = a.id()
|
||||
if string and QgsLocatorFilter.stringMatches(a.displayName(), string):
|
||||
result.score = float(len(string)) / len(a.displayName())
|
||||
else:
|
||||
result.score = 0
|
||||
self.resultFetched.emit(result)
|
||||
|
||||
def triggerResult(self, result):
|
||||
alg = QgsApplication.processingRegistry().createAlgorithmById(result.userData)
|
||||
if alg:
|
||||
ok, message = alg.canExecute()
|
||||
if not ok:
|
||||
dlg = MessageDialog()
|
||||
dlg.setTitle(self.tr('Missing dependency'))
|
||||
dlg.setMessage(message)
|
||||
dlg.exec_()
|
||||
return
|
||||
|
||||
if len(alg.parameterDefinitions()) > 2:
|
||||
# hack!!
|
||||
dlg = alg.createCustomParametersWidget(None)
|
||||
if not dlg:
|
||||
dlg = AlgorithmDialog(alg, True)
|
||||
canvas = iface.mapCanvas()
|
||||
prevMapTool = canvas.mapTool()
|
||||
dlg.show()
|
||||
dlg.exec_()
|
||||
if canvas.mapTool() != prevMapTool:
|
||||
try:
|
||||
canvas.mapTool().reset()
|
||||
except:
|
||||
pass
|
||||
canvas.setMapTool(prevMapTool)
|
||||
else:
|
||||
parameters = {}
|
||||
execute_in_place(alg, parameters)
|
||||
|
@ -66,9 +66,10 @@ class ParametersPanel(BASE, WIDGET):
|
||||
|
||||
NOT_SELECTED = QCoreApplication.translate('ParametersPanel', '[Not selected]')
|
||||
|
||||
def __init__(self, parent, alg):
|
||||
def __init__(self, parent, alg, in_place=False):
|
||||
super(ParametersPanel, self).__init__(None)
|
||||
self.setupUi(self)
|
||||
self.in_place = in_place
|
||||
|
||||
self.grpAdvanced.hide()
|
||||
|
||||
@ -126,6 +127,9 @@ class ParametersPanel(BASE, WIDGET):
|
||||
if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
|
||||
continue
|
||||
|
||||
if self.in_place and param.name() in ('INPUT', 'OUTPUT'):
|
||||
continue
|
||||
|
||||
if param.isDestination():
|
||||
continue
|
||||
else:
|
||||
@ -196,6 +200,9 @@ class ParametersPanel(BASE, WIDGET):
|
||||
if output.flags() & QgsProcessingParameterDefinition.FlagHidden:
|
||||
continue
|
||||
|
||||
if self.in_place and param.name() in ('INPUT', 'OUTPUT'):
|
||||
continue
|
||||
|
||||
label = QLabel(output.description())
|
||||
widget = DestinationSelectionPanel(output, self.alg)
|
||||
self.layoutMain.insertWidget(self.layoutMain.count() - 1, label)
|
||||
|
@ -33,7 +33,9 @@ from qgis.PyQt import uic
|
||||
from qgis.PyQt.QtCore import Qt, QCoreApplication
|
||||
from qgis.PyQt.QtWidgets import QToolButton, QMenu, QAction
|
||||
from qgis.utils import iface
|
||||
from qgis.core import (QgsApplication,
|
||||
from qgis.core import (QgsWkbTypes,
|
||||
QgsMapLayer,
|
||||
QgsApplication,
|
||||
QgsProcessingAlgorithm)
|
||||
from qgis.gui import (QgsGui,
|
||||
QgsDockWidget,
|
||||
@ -50,6 +52,7 @@ from processing.gui.AlgorithmExecutor import execute
|
||||
from processing.gui.ProviderActions import (ProviderActions,
|
||||
ProviderContextMenuActions)
|
||||
from processing.tools import dataobjects
|
||||
from processing.gui.AlgorithmExecutor import execute_in_place
|
||||
|
||||
pluginPath = os.path.split(os.path.dirname(__file__))[0]
|
||||
|
||||
@ -71,6 +74,7 @@ class ProcessingToolbox(QgsDockWidget, WIDGET):
|
||||
def __init__(self):
|
||||
super(ProcessingToolbox, self).__init__(None)
|
||||
self.tipWasClosed = False
|
||||
self.in_place_mode = False
|
||||
self.setupUi(self)
|
||||
self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
|
||||
self.processingToolbar.setIconSize(iface.iconSize(True))
|
||||
@ -108,6 +112,20 @@ class ProcessingToolbox(QgsDockWidget, WIDGET):
|
||||
QgsApplication.processingRegistry().providerRemoved.connect(self.addProvider)
|
||||
QgsApplication.processingRegistry().providerRemoved.connect(self.removeProvider)
|
||||
|
||||
iface.currentLayerChanged.connect(self.layer_changed)
|
||||
|
||||
def set_in_place_edit_mode(self, enabled):
|
||||
if enabled:
|
||||
self.algorithmTree.setFilters(QgsProcessingToolboxProxyModel.Filters(QgsProcessingToolboxProxyModel.FilterToolbox | QgsProcessingToolboxProxyModel.FilterInPlace))
|
||||
else:
|
||||
self.algorithmTree.setFilters(QgsProcessingToolboxProxyModel.FilterToolbox)
|
||||
self.in_place_mode = enabled
|
||||
|
||||
def layer_changed(self, layer):
|
||||
if layer is None or layer.type() != QgsMapLayer.VectorLayer:
|
||||
return
|
||||
self.algorithmTree.setInPlaceLayerType(QgsWkbTypes.geometryType(layer.wkbType()))
|
||||
|
||||
def disabledProviders(self):
|
||||
showTip = ProcessingConfig.getSetting(ProcessingConfig.SHOW_PROVIDERS_TOOLTIP)
|
||||
if not showTip or self.tipWasClosed:
|
||||
@ -211,11 +229,17 @@ class ProcessingToolbox(QgsDockWidget, WIDGET):
|
||||
dlg.exec_()
|
||||
return
|
||||
|
||||
if self.in_place_mode and len(alg.parameterDefinitions()) <= 2:
|
||||
# hack
|
||||
parameters = {}
|
||||
execute_in_place(alg, parameters)
|
||||
return
|
||||
|
||||
if alg.countVisibleParameters() > 0:
|
||||
dlg = alg.createCustomParametersWidget(self)
|
||||
|
||||
if not dlg:
|
||||
dlg = AlgorithmDialog(alg)
|
||||
dlg = AlgorithmDialog(alg, self.in_place_mode)
|
||||
canvas = iface.mapCanvas()
|
||||
prevMapTool = canvas.mapTool()
|
||||
dlg.show()
|
||||
|
@ -29,6 +29,7 @@ const QList<QString> QgsLocator::CORE_FILTERS = QList<QString>() << QStringLiter
|
||||
<< QStringLiteral( "calculator" )
|
||||
<< QStringLiteral( "bookmarks" )
|
||||
<< QStringLiteral( "optionpages" );
|
||||
<< QStringLiteral( "edit_features" );
|
||||
|
||||
QgsLocator::QgsLocator( QObject *parent )
|
||||
: QObject( parent )
|
||||
|
@ -787,6 +787,13 @@ bool QgsProcessingAlgorithm::createAutoOutputForParameter( QgsProcessingParamete
|
||||
// QgsProcessingFeatureBasedAlgorithm
|
||||
//
|
||||
|
||||
QgsProcessingAlgorithm::Flags QgsProcessingFeatureBasedAlgorithm::flags() const
|
||||
{
|
||||
Flags f = QgsProcessingAlgorithm::flags();
|
||||
f |= QgsProcessingAlgorithm::FlagSupportsInPlaceEdits;
|
||||
return f;
|
||||
}
|
||||
|
||||
void QgsProcessingFeatureBasedAlgorithm::initAlgorithm( const QVariantMap &config )
|
||||
{
|
||||
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), inputLayerTypes() ) );
|
||||
|
@ -74,6 +74,7 @@ class CORE_EXPORT QgsProcessingAlgorithm
|
||||
FlagRequiresMatchingCrs = 1 << 5, //!< Algorithm requires that all input layers have matching coordinate reference systems
|
||||
FlagNoThreading = 1 << 6, //!< Algorithm is not thread safe and cannot be run in a background thread, e.g. for algorithms which manipulate the current project, layer selections, or with external dependencies which are not thread-safe.
|
||||
FlagDisplayNameIsLiteral = 1 << 7, //!< Algorithm's display name is a static literal string, and should not be translated or automatically formatted. For use with algorithms named after commands, e.g. GRASS 'v.in.ogr'.
|
||||
FlagSupportsInPlaceEdits = 1 << 8, //!< Algorithm supports in-place editing
|
||||
FlagDeprecated = FlagHideFromToolbox | FlagHideFromModeler, //!< Algorithm is deprecated
|
||||
};
|
||||
Q_DECLARE_FLAGS( Flags, Flag )
|
||||
@ -861,6 +862,8 @@ class CORE_EXPORT QgsProcessingFeatureBasedAlgorithm : public QgsProcessingAlgor
|
||||
*/
|
||||
QgsProcessingFeatureBasedAlgorithm() = default;
|
||||
|
||||
QgsProcessingAlgorithm::Flags flags() const override;
|
||||
|
||||
protected:
|
||||
|
||||
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
|
||||
@ -970,6 +973,7 @@ class CORE_EXPORT QgsProcessingFeatureBasedAlgorithm : public QgsProcessingAlgor
|
||||
private:
|
||||
|
||||
std::unique_ptr< QgsProcessingFeatureSource > mSource;
|
||||
friend class QgsProcessingToolboxProxyModel;
|
||||
|
||||
};
|
||||
|
||||
|
@ -655,6 +655,12 @@ void QgsProcessingToolboxProxyModel::setFilters( QgsProcessingToolboxProxyModel:
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void QgsProcessingToolboxProxyModel::setInPlaceLayerType( QgsWkbTypes::GeometryType type )
|
||||
{
|
||||
mInPlaceGeometryType = type;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void QgsProcessingToolboxProxyModel::setFilterString( const QString &filter )
|
||||
{
|
||||
mFilterString = filter;
|
||||
@ -708,6 +714,32 @@ bool QgsProcessingToolboxProxyModel::filterAcceptsRow( int sourceRow, const QMod
|
||||
}
|
||||
}
|
||||
|
||||
if ( mFilters & FilterInPlace )
|
||||
{
|
||||
const bool supportsInPlace = sourceModel()->data( sourceIndex, QgsProcessingToolboxModel::RoleAlgorithmFlags ).toInt() & QgsProcessingAlgorithm::FlagSupportsInPlaceEdits;
|
||||
if ( !supportsInPlace )
|
||||
return false;
|
||||
|
||||
const QgsProcessingFeatureBasedAlgorithm *alg = dynamic_cast< const QgsProcessingFeatureBasedAlgorithm * >( mModel->algorithmForIndex( sourceIndex ) );
|
||||
|
||||
if ( !alg->inputLayerTypes().empty() &&
|
||||
!alg->inputLayerTypes().contains( QgsProcessing::TypeVector ) &&
|
||||
!alg->inputLayerTypes().contains( QgsProcessing::TypeVectorAnyGeometry ) &&
|
||||
( ( mInPlaceGeometryType == QgsWkbTypes::PolygonGeometry && !alg->inputLayerTypes().contains( QgsProcessing::TypeVectorPolygon ) ) ||
|
||||
( mInPlaceGeometryType == QgsWkbTypes::LineGeometry && !alg->inputLayerTypes().contains( QgsProcessing::TypeVectorLine ) ) ||
|
||||
( mInPlaceGeometryType == QgsWkbTypes::PointGeometry && !alg->inputLayerTypes().contains( QgsProcessing::TypeVectorPoint ) ) ) )
|
||||
return false;
|
||||
|
||||
QgsWkbTypes::Type type = QgsWkbTypes::Unknown;
|
||||
if ( mInPlaceGeometryType == QgsWkbTypes::PointGeometry )
|
||||
type = QgsWkbTypes::Point;
|
||||
else if ( mInPlaceGeometryType == QgsWkbTypes::LineGeometry )
|
||||
type = QgsWkbTypes::LineString;
|
||||
else if ( mInPlaceGeometryType == QgsWkbTypes::PolygonGeometry )
|
||||
type = QgsWkbTypes::Polygon;
|
||||
if ( QgsWkbTypes::geometryType( alg->outputWkbType( type ) ) != mInPlaceGeometryType )
|
||||
return false;
|
||||
}
|
||||
if ( mFilters & FilterModeler )
|
||||
{
|
||||
bool isHiddenFromModeler = sourceModel()->data( sourceIndex, QgsProcessingToolboxModel::RoleAlgorithmFlags ).toInt() & QgsProcessingAlgorithm::FlagHideFromModeler;
|
||||
|
@ -423,6 +423,7 @@ class GUI_EXPORT QgsProcessingToolboxProxyModel: public QSortFilterProxyModel
|
||||
{
|
||||
FilterToolbox = 1 << 1, //!< Filters out any algorithms and content which should not be shown in the toolbox
|
||||
FilterModeler = 1 << 2, //!< Filters out any algorithms and content which should not be shown in the modeler
|
||||
FilterInPlace = 1 << 3, //!< Only show algorithms which support in-place edits
|
||||
};
|
||||
Q_DECLARE_FLAGS( Filters, Filter )
|
||||
Q_FLAG( Filters )
|
||||
@ -459,6 +460,8 @@ class GUI_EXPORT QgsProcessingToolboxProxyModel: public QSortFilterProxyModel
|
||||
*/
|
||||
Filters filters() const { return mFilters; }
|
||||
|
||||
void setInPlaceLayerType( QgsWkbTypes::GeometryType type );
|
||||
|
||||
/**
|
||||
* Sets a \a filter string, such that only algorithms matching the
|
||||
* specified string will be shown.
|
||||
@ -485,6 +488,7 @@ class GUI_EXPORT QgsProcessingToolboxProxyModel: public QSortFilterProxyModel
|
||||
QgsProcessingToolboxModel *mModel = nullptr;
|
||||
Filters mFilters = nullptr;
|
||||
QString mFilterString;
|
||||
QgsWkbTypes::GeometryType mInPlaceGeometryType = QgsWkbTypes::UnknownGeometry;
|
||||
};
|
||||
|
||||
|
||||
|
@ -93,6 +93,11 @@ void QgsProcessingToolboxTreeView::setFilters( QgsProcessingToolboxProxyModel::F
|
||||
mModel->setFilters( filters );
|
||||
}
|
||||
|
||||
void QgsProcessingToolboxTreeView::setInPlaceLayerType( QgsWkbTypes::GeometryType type )
|
||||
{
|
||||
mModel->setInPlaceLayerType( type );
|
||||
}
|
||||
|
||||
QModelIndex QgsProcessingToolboxTreeView::findFirstVisibleAlgorithm( const QModelIndex &parent )
|
||||
{
|
||||
for ( int r = 0; r < mModel->rowCount( parent ); ++r )
|
||||
|
@ -85,6 +85,8 @@ class GUI_EXPORT QgsProcessingToolboxTreeView : public QTreeView
|
||||
*/
|
||||
void setFilters( QgsProcessingToolboxProxyModel::Filters filters );
|
||||
|
||||
void setInPlaceLayerType( QgsWkbTypes::GeometryType type );
|
||||
|
||||
public slots:
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user