1# -*- coding: utf-8 -*- 2 3""" 4*************************************************************************** 5 ProcessingToolbox.py 6 --------------------- 7 Date : August 2012 8 Copyright : (C) 2012 by Victor Olaya 9 Email : volayaf at gmail dot com 10*************************************************************************** 11* * 12* This program is free software; you can redistribute it and/or modify * 13* it under the terms of the GNU General Public License as published by * 14* the Free Software Foundation; either version 2 of the License, or * 15* (at your option) any later version. * 16* * 17*************************************************************************** 18""" 19 20__author__ = 'Victor Olaya' 21__date__ = 'August 2012' 22__copyright__ = '(C) 2012, Victor Olaya' 23 24import operator 25import os 26import warnings 27 28from qgis.PyQt import uic 29from qgis.PyQt.QtCore import Qt, QCoreApplication 30from qgis.PyQt.QtWidgets import QToolButton, QMenu, QAction 31from qgis.utils import iface 32from qgis.core import (QgsWkbTypes, 33 QgsMapLayerType, 34 QgsApplication, 35 QgsProcessingAlgorithm) 36from qgis.gui import (QgsGui, 37 QgsDockWidget, 38 QgsProcessingToolboxProxyModel) 39 40from processing.gui.Postprocessing import handleAlgorithmResults 41from processing.core.ProcessingConfig import ProcessingConfig 42from processing.gui.MessageDialog import MessageDialog 43from processing.gui.AlgorithmDialog import AlgorithmDialog 44from processing.gui.BatchAlgorithmDialog import BatchAlgorithmDialog 45from processing.gui.EditRenderingStylesDialog import EditRenderingStylesDialog 46from processing.gui.MessageBarProgress import MessageBarProgress 47from processing.gui.AlgorithmExecutor import execute 48from processing.gui.ProviderActions import (ProviderActions, 49 ProviderContextMenuActions) 50from processing.tools import dataobjects 51from processing.gui.AlgorithmExecutor import execute_in_place 52 53pluginPath = os.path.split(os.path.dirname(__file__))[0] 54 55with warnings.catch_warnings(): 56 warnings.filterwarnings("ignore", category=DeprecationWarning) 57 WIDGET, BASE = uic.loadUiType( 58 os.path.join(pluginPath, 'ui', 'ProcessingToolbox.ui')) 59 60 61class ProcessingToolbox(QgsDockWidget, WIDGET): 62 ALG_ITEM = 'ALG_ITEM' 63 PROVIDER_ITEM = 'PROVIDER_ITEM' 64 GROUP_ITEM = 'GROUP_ITEM' 65 66 NAME_ROLE = Qt.UserRole 67 TAG_ROLE = Qt.UserRole + 1 68 TYPE_ROLE = Qt.UserRole + 2 69 70 def __init__(self): 71 super(ProcessingToolbox, self).__init__(None) 72 self.tipWasClosed = False 73 self.in_place_mode = False 74 self.setupUi(self) 75 self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) 76 self.processingToolbar.setIconSize(iface.iconSize(True)) 77 78 self.algorithmTree.setRegistry(QgsApplication.processingRegistry(), 79 QgsGui.instance().processingRecentAlgorithmLog()) 80 filters = QgsProcessingToolboxProxyModel.Filters(QgsProcessingToolboxProxyModel.FilterToolbox) 81 if ProcessingConfig.getSetting(ProcessingConfig.SHOW_ALGORITHMS_KNOWN_ISSUES): 82 filters |= QgsProcessingToolboxProxyModel.FilterShowKnownIssues 83 self.algorithmTree.setFilters(filters) 84 85 self.searchBox.setShowSearchIcon(True) 86 87 self.searchBox.textChanged.connect(self.set_filter_string) 88 self.searchBox.returnPressed.connect(self.activateCurrent) 89 self.algorithmTree.customContextMenuRequested.connect( 90 self.showPopupMenu) 91 self.algorithmTree.doubleClicked.connect(self.executeAlgorithm) 92 self.txtTip.setVisible(self.disabledProviders()) 93 94 def openSettings(url): 95 if url == "close": 96 self.txtTip.setVisible(False) 97 self.tipWasClosed = True 98 else: 99 iface.showOptionsDialog(iface.mainWindow(), 'processingOptions') 100 self.txtTip.setVisible(self.disabledProviders()) 101 102 self.txtTip.linkActivated.connect(openSettings) 103 if hasattr(self.searchBox, 'setPlaceholderText'): 104 self.searchBox.setPlaceholderText(QCoreApplication.translate('ProcessingToolbox', 'Search…')) 105 106 # connect to existing providers 107 for p in QgsApplication.processingRegistry().providers(): 108 if p.isActive(): 109 self.addProviderActions(p) 110 111 QgsApplication.processingRegistry().providerRemoved.connect(self.addProvider) 112 QgsApplication.processingRegistry().providerRemoved.connect(self.removeProvider) 113 114 iface.currentLayerChanged.connect(self.layer_changed) 115 116 def set_filter_string(self, string): 117 filters = self.algorithmTree.filters() 118 if ProcessingConfig.getSetting(ProcessingConfig.SHOW_ALGORITHMS_KNOWN_ISSUES): 119 filters |= QgsProcessingToolboxProxyModel.FilterShowKnownIssues 120 else: 121 filters &= ~QgsProcessingToolboxProxyModel.FilterShowKnownIssues 122 self.algorithmTree.setFilters(filters) 123 self.algorithmTree.setFilterString(string) 124 125 def set_in_place_edit_mode(self, enabled): 126 filters = QgsProcessingToolboxProxyModel.Filters(QgsProcessingToolboxProxyModel.FilterToolbox) 127 if ProcessingConfig.getSetting(ProcessingConfig.SHOW_ALGORITHMS_KNOWN_ISSUES): 128 filters |= QgsProcessingToolboxProxyModel.FilterShowKnownIssues 129 130 if enabled: 131 self.algorithmTree.setFilters(filters | QgsProcessingToolboxProxyModel.FilterInPlace) 132 else: 133 self.algorithmTree.setFilters(filters) 134 self.in_place_mode = enabled 135 136 def layer_changed(self, layer): 137 if layer is None or layer.type() != QgsMapLayerType.VectorLayer: 138 return 139 self.algorithmTree.setInPlaceLayer(layer) 140 141 def disabledProviders(self): 142 showTip = ProcessingConfig.getSetting(ProcessingConfig.SHOW_PROVIDERS_TOOLTIP) 143 if not showTip or self.tipWasClosed: 144 return False 145 146 for provider in QgsApplication.processingRegistry().providers(): 147 if not provider.isActive() and provider.canBeActivated(): 148 return True 149 150 return False 151 152 def addProviderActions(self, provider): 153 if provider.id() in ProviderActions.actions: 154 toolbarButton = QToolButton() 155 toolbarButton.setObjectName('provideraction_' + provider.id()) 156 toolbarButton.setIcon(provider.icon()) 157 toolbarButton.setToolTip(provider.name()) 158 toolbarButton.setPopupMode(QToolButton.InstantPopup) 159 160 actions = ProviderActions.actions[provider.id()] 161 menu = QMenu(provider.name(), self) 162 for action in actions: 163 action.setData(self) 164 act = QAction(action.name, menu) 165 act.triggered.connect(action.execute) 166 menu.addAction(act) 167 toolbarButton.setMenu(menu) 168 self.processingToolbar.addWidget(toolbarButton) 169 170 def addProvider(self, provider_id): 171 provider = QgsApplication.processingRegistry().providerById(provider_id) 172 if provider is not None: 173 self.addProviderActions(provider) 174 175 def removeProvider(self, provider_id): 176 button = self.findChild(QToolButton, 'provideraction-' + provider_id) 177 if button: 178 self.processingToolbar.removeChild(button) 179 180 def showPopupMenu(self, point): 181 index = self.algorithmTree.indexAt(point) 182 popupmenu = QMenu() 183 alg = self.algorithmTree.algorithmForIndex(index) 184 if alg is not None: 185 executeAction = QAction(QCoreApplication.translate('ProcessingToolbox', 'Execute…'), popupmenu) 186 executeAction.triggered.connect(self.executeAlgorithm) 187 popupmenu.addAction(executeAction) 188 if alg.flags() & QgsProcessingAlgorithm.FlagSupportsBatch: 189 executeBatchAction = QAction( 190 QCoreApplication.translate('ProcessingToolbox', 'Execute as Batch Process…'), 191 popupmenu) 192 executeBatchAction.triggered.connect( 193 self.executeAlgorithmAsBatchProcess) 194 popupmenu.addAction(executeBatchAction) 195 popupmenu.addSeparator() 196 editRenderingStylesAction = QAction( 197 QCoreApplication.translate('ProcessingToolbox', 'Edit Rendering Styles for Outputs…'), 198 popupmenu) 199 editRenderingStylesAction.triggered.connect( 200 self.editRenderingStyles) 201 popupmenu.addAction(editRenderingStylesAction) 202 actions = ProviderContextMenuActions.actions 203 if len(actions) > 0: 204 popupmenu.addSeparator() 205 for action in actions: 206 action.setData(alg, self) 207 if action.is_separator: 208 popupmenu.addSeparator() 209 elif action.isEnabled(): 210 contextMenuAction = QAction(action.name, 211 popupmenu) 212 contextMenuAction.setIcon(action.icon()) 213 contextMenuAction.triggered.connect(action.execute) 214 popupmenu.addAction(contextMenuAction) 215 216 popupmenu.exec_(self.algorithmTree.mapToGlobal(point)) 217 218 def editRenderingStyles(self): 219 alg = self.algorithmTree.selectedAlgorithm().create() if self.algorithmTree.selectedAlgorithm() is not None else None 220 if alg is not None: 221 dlg = EditRenderingStylesDialog(alg) 222 dlg.exec_() 223 224 def activateCurrent(self): 225 self.executeAlgorithm() 226 227 def executeAlgorithmAsBatchProcess(self): 228 alg = self.algorithmTree.selectedAlgorithm().create() if self.algorithmTree.selectedAlgorithm() is not None else None 229 if alg is not None: 230 dlg = BatchAlgorithmDialog(alg, iface.mainWindow()) 231 dlg.setAttribute(Qt.WA_DeleteOnClose) 232 dlg.show() 233 dlg.exec_() 234 235 def executeAlgorithm(self): 236 config = {} 237 if self.in_place_mode: 238 config['IN_PLACE'] = True 239 alg = self.algorithmTree.selectedAlgorithm().create(config) if self.algorithmTree.selectedAlgorithm() is not None else None 240 if alg is not None: 241 ok, message = alg.canExecute() 242 if not ok: 243 dlg = MessageDialog() 244 dlg.setTitle(self.tr('Error executing algorithm')) 245 dlg.setMessage( 246 self.tr('<h3>This algorithm cannot ' 247 'be run :-( </h3>\n{0}').format(message)) 248 dlg.exec_() 249 return 250 251 in_place_input_parameter_name = 'INPUT' 252 if hasattr(alg, 'inputParameterName'): 253 in_place_input_parameter_name = alg.inputParameterName() 254 255 if self.in_place_mode and not [d for d in alg.parameterDefinitions() if d.name() not in (in_place_input_parameter_name, 'OUTPUT')]: 256 parameters = {} 257 feedback = MessageBarProgress(algname=alg.displayName()) 258 ok, results = execute_in_place(alg, parameters, feedback=feedback) 259 if ok: 260 iface.messageBar().pushSuccess('', self.tr('{algname} completed. %n feature(s) processed.', n=results['__count']).format(algname=alg.displayName())) 261 feedback.close() 262 # MessageBarProgress handles errors 263 return 264 265 if alg.countVisibleParameters() > 0: 266 dlg = alg.createCustomParametersWidget(self) 267 268 if not dlg: 269 dlg = AlgorithmDialog(alg, self.in_place_mode, iface.mainWindow()) 270 canvas = iface.mapCanvas() 271 prevMapTool = canvas.mapTool() 272 dlg.show() 273 dlg.exec_() 274 if canvas.mapTool() != prevMapTool: 275 try: 276 canvas.mapTool().reset() 277 except: 278 pass 279 canvas.setMapTool(prevMapTool) 280 else: 281 feedback = MessageBarProgress(algname=alg.displayName()) 282 context = dataobjects.createContext(feedback) 283 parameters = {} 284 ret, results = execute(alg, parameters, context, feedback) 285 handleAlgorithmResults(alg, context, feedback) 286 feedback.close() 287