def create_styles_to_attributes_tasks( task_wrappers: List[TaskWrapper], completed: Callable ) -> None: tasks = [] if len(task_wrappers) == 0: # TODO: custom execption raise ValueError() for task_wrapper in task_wrappers: alg = QgsApplication.processingRegistry().algorithmById( f"{SpatialDataPackageProcessingProvider.ID}:{StyleToAttributesAlg.ID}" ) task = QgsProcessingAlgRunnerTask( alg, task_wrapper.params, task_wrapper.context, task_wrapper.feedback ) task.setDependentLayers([task_wrapper.layer]) # noinspection PyUnresolvedReferences task.executed.connect( partial( task_wrapper.executed, task_wrapper.layer, task_wrapper.context, task_wrapper.id, ) ) # noinspection PyUnresolvedReferences task.taskCompleted.connect(completed) tasks.append(task) main_task = tasks[0] for i in range(1, len(tasks)): main_task.addSubTask(tasks[i]) QgsApplication.taskManager().addTask(main_task)
def test_bad_script_dont_crash(self): # spellok """Test regression #21270 (segfault)""" context = QgsProcessingContext() context.setProject(QgsProject.instance()) feedback = ConsoleFeedBack() task = QgsProcessingAlgRunnerTask(CrashingProcessingAlgorithm(), {}, context=context, feedback=feedback) self.assertTrue(task.isCanceled()) self.assertIn('name \'ExampleProcessingAlgorithm\' is not defined', feedback._error)
def test_bad_script_dont_crash(self): # spellok """Test regression #21270 (segfault)""" context = QgsProcessingContext() context.setProject(QgsProject.instance()) feedback = ConsoleFeedBack() task = QgsProcessingAlgRunnerTask(CrashingProcessingAlgorithm(), {}, context=context, feedback=feedback) self.assertTrue(task.isCanceled()) self.assertIn('name \'ExampleProcessingAlgorithm\' is not defined', feedback._error)
def handle_report(self): task = QgsProcessingAlgRunnerTask( self.algorithm, self.params, self.context, self.feedback ) task.executed.connect(self.task_finished) task_manager = QgsApplication.taskManager() task_manager.addTask(task)
def perform_automation(self): task = QgsProcessingAlgRunnerTask(self.algorithm, self.params, self.context, self.feedback) utils.log_message(f"self.algorithm: {self.algorithm}") utils.log_message(f"self.params: {self.params}") utils.log_message( f"self.params types: {[(k, type(v)) for k,v in self.params.items()]}" ) utils.log_message( f"self.params values: {[(k, v) for k,v in self.params.items()]}") task.executed.connect(self.task_finished) task_manager = QgsApplication.taskManager() task_manager.addTask(task)
def run_import_command(self): """Adds the polygonize script to a QgsTask""" self.s_tbl = "{schema}.{file_name}".format(file_name=self.file_name, schema=self.schema) input_ = self.file_name_with_path params = { 'INPUT': input_, 'BAND': 1, 'OUTPUT': self.plugin_dir + '/temp.shp', 'FIELD': 'raster_value' } alg = QgsApplication.processingRegistry().algorithmById( u'gdal:polygonize') context = QgsProcessingContext() task = QgsProcessingAlgRunnerTask(alg, params, context) task.executed.connect(partial(self.task_finished, context)) self.tsk_mngr.addTask(task)
def start_task(self, input_layer, field, max_iterations, max_average_error): self.context = QgsProcessingContext() self.feedback = QgsProcessingFeedback() self.task = QgsProcessingAlgRunnerTask( QgsApplication.processingRegistry().algorithmById("cartogram3:compute_cartogram"), { "INPUT": input_layer, "FIELD": field, "MAX_ITERATIONS": max_iterations, "MAX_AVERAGE_ERROR": max_average_error, "OUTPUT": "memory:" }, self.context, self.feedback ) self.task.executed.connect(self.task_finished) self.feedback.progressChanged.connect(self.update_progress) QgsApplication.taskManager().addTask(self.task)
def uploadSingleLayer(self): '''Uploads a single input layer to database''' alg = QgsApplication.processingRegistry().algorithmById( 'gdal:importvectorintopostgisdatabasenewconnection') params = { 'A_SRS': QgsCoordinateReferenceSystem('EPSG:3067'), 'T_SRS': None, 'S_SRS': None, 'HOST': self.connParams['host'], 'PORT': self.connParams['port'], 'USER': self.connParams['user'], 'DBNAME': self.connParams['database'], 'PASSWORD': self.connParams['password'], 'SCHEMA': 'user_input', 'PK': 'fid', 'PRIMARY_KEY': None, 'PROMOTETOMULTI': False } context = QgsProcessingContext() feedback = QgsProcessingFeedback() layer = self.inputLayers[self.layerUploadIndex] if not layer: self.tableNames[layer] = False self.uploadNextLayer() params['INPUT'] = layer tableName = self.sessionParams['uuid'] + '_' + layer.name() tableName = tableName.replace('-', '_') params['TABLE'] = tableName[:49] # truncate tablename to under 63c self.tableNames[layer] = params['TABLE'] if layer.geometryType() == 0: # point params['GTYPE'] = 3 elif layer.geometryType() == 2: # polygon params['GTYPE'] = 8 task = QgsProcessingAlgRunnerTask(alg, params, context, feedback) task.executed.connect(partial(self.uploadFinished, context)) QgsApplication.taskManager().addTask(task) self.iface.messageBar().pushMessage('Ladataan tasoa tietokantaan', layer.name(), Qgis.Info, duration=3)
def runAlgorithm(self): self.feedback = self.createFeedback() self.context = dataobjects.createContext(self.feedback) checkCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_CRS) try: parameters = self.getParameterValues() if checkCRS and not self.algorithm().validateInputCrs(parameters, self.context): reply = QMessageBox.question(self, self.tr("Unmatching CRS's"), self.tr('Parameters do not all use the same CRS. This can ' 'cause unexpected results.\nDo you want to ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return ok, msg = self.algorithm().checkParameterValues(parameters, self.context) if not ok: QMessageBox.warning( self, self.tr('Unable to execute algorithm'), msg) return self.runButton().setEnabled(False) self.cancelButton().setEnabled(False) buttons = self.mainWidget().iterateButtons self.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.clearProgress() self.setProgressText(QCoreApplication.translate('AlgorithmDialog', 'Processing algorithm…')) self.setInfo( QCoreApplication.translate('AlgorithmDialog', '<b>Algorithm \'{0}\' starting…</b>').format(self.algorithm().displayName()), escapeHtml=False) self.feedback.pushInfo(self.tr('Input parameters:')) display_params = [] for k, v in parameters.items(): display_params.append("'" + k + "' : " + self.algorithm().parameterDefinition(k).valueAsPythonString(v, self.context)) self.feedback.pushCommandInfo('{ ' + ', '.join(display_params) + ' }') self.feedback.pushInfo('') start_time = time.time() if self.iterateParam: # Make sure the Log tab is visible before executing the algorithm try: self.showLog() self.repaint() except: pass self.cancelButton().setEnabled(self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanCancel) if executeIterating(self.algorithm(), parameters, self.iterateParam, self.context, self.feedback): self.feedback.pushInfo( self.tr('Execution completed in {0:0.2f} seconds').format(time.time() - start_time)) self.cancelButton().setEnabled(False) self.finish(True, parameters, self.context, self.feedback) else: self.cancelButton().setEnabled(False) self.resetGui() else: command = self.algorithm().asPythonCommand(parameters, self.context) if command: ProcessingLog.addToLog(command) QgsGui.instance().processingRecentAlgorithmLog().push(self.algorithm().id()) self.cancelButton().setEnabled(self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanCancel) def on_complete(ok, results): if ok: self.feedback.pushInfo(self.tr('Execution completed in {0:0.2f} seconds').format(time.time() - start_time)) self.feedback.pushInfo(self.tr('Results:')) self.feedback.pushCommandInfo(pformat(results)) else: self.feedback.reportError( self.tr('Execution failed after {0:0.2f} seconds').format(time.time() - start_time)) self.feedback.pushInfo('') if self.feedback_dialog is not None: self.feedback_dialog.close() self.feedback_dialog.deleteLater() self.feedback_dialog = None self.cancelButton().setEnabled(False) if not self.in_place: self.finish(ok, results, self.context, self.feedback) elif ok: self.close() self.feedback = None self.context = None 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() task = QgsProcessingAlgRunnerTask(self.algorithm(), parameters, self.context, self.feedback) if task.isCanceled(): on_complete(False, {}) else: task.executed.connect(on_complete) self.setCurrentTask(task) else: self.proxy_progress = QgsProxyProgressTask(QCoreApplication.translate("AlgorithmDialog", "Executing “{}”").format(self.algorithm().displayName())) QgsApplication.taskManager().addTask(self.proxy_progress) self.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, self.context, self.feedback) else: ok, results = execute(self.algorithm(), parameters, self.context, self.feedback) self.feedback.progressChanged.disconnect() self.proxy_progress.finalize(ok) on_complete(ok, results) except AlgorithmDialogBase.InvalidParameterValue as e: try: 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.messageBar().clearWidgets() self.messageBar().pushMessage("", self.tr("Wrong or missing parameter value: {0}").format(e.parameter.description()), level=Qgis.Warning, duration=5) except AlgorithmDialogBase.InvalidOutputExtension as e: try: 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.messageBar().clearWidgets() self.messageBar().pushMessage("", e.message, level=Qgis.Warning, duration=5)
def accept(self): feedback = self.createFeedback() context = dataobjects.createContext(feedback) checkCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_CRS) try: parameters = self.getParameterValues() 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 ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return checkExtentCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS) # TODO if False and checkExtentCRS and self.checkExtentCRS(): reply = QMessageBox.question(self, self.tr("Extent CRS"), self.tr('Extent parameters must use the same CRS as the input layers.\n' 'Your input layers do not have the same extent as the project, ' 'so the extent might be in a wrong CRS if you have selected it from the canvas.\n' 'Do you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return ok, msg = self.algorithm().checkParameterValues(parameters, context) if msg: QMessageBox.warning( self, self.tr('Unable to execute algorithm'), msg) return self.runButton().setEnabled(False) self.cancelButton().setEnabled(False) buttons = self.mainWidget().iterateButtons self.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.clearProgress() self.setProgressText(self.tr('Processing algorithm...')) self.setInfo( 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.algorithm().parameterDefinition(k).valueAsPythonString(v, context)) feedback.pushCommandInfo('{ ' + ', '.join(display_params) + ' }') feedback.pushInfo('') start_time = time.time() if self.iterateParam: # Make sure the Log tab is visible before executing the algorithm try: self.showLog() self.repaint() except: pass 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.cancelButton().setEnabled(False) self.finish(True, parameters, context, feedback) else: self.cancelButton().setEnabled(False) self.resetGui() else: command = self.algorithm().asPythonCommand(parameters, context) if command: ProcessingLog.addToLog(command) self.cancelButton().setEnabled(self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanCancel) def on_complete(ok, results): if ok: feedback.pushInfo(self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - start_time))) feedback.pushInfo(self.tr('Results:')) feedback.pushCommandInfo(pformat(results)) else: feedback.reportError( self.tr('Execution failed after {0:0.2f} seconds'.format(time.time() - start_time))) feedback.pushInfo('') if self.feedback_dialog is not None: self.feedback_dialog.close() self.feedback_dialog.deleteLater() self.feedback_dialog = None self.cancelButton().setEnabled(False) self.finish(ok, results, context, feedback) if self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanRunInBackground: # Make sure the Log tab is visible before executing the algorithm self.showLog() task = QgsProcessingAlgRunnerTask(self.algorithm(), parameters, context, feedback) task.executed.connect(on_complete) self.setCurrentTask(task) else: self.feedback_dialog = self.createProgressDialog() self.feedback_dialog.show() ok, results = execute(self.algorithm(), parameters, context, feedback) on_complete(ok, results) except AlgorithmDialogBase.InvalidParameterValue as e: try: 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.messageBar().clearWidgets() self.messageBar().pushMessage("", self.tr("Wrong or missing parameter value: {0}").format(e.parameter.description()), level=QgsMessageBar.WARNING, duration=5)
def accept(self): super(AlgorithmDialog, self)._saveGeometry() feedback = self.createFeedback() context = dataobjects.createContext(feedback) checkCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_CRS) try: parameters = self.getParamValues() if checkCRS and not self.alg.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 ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return checkExtentCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS) # TODO if False and checkExtentCRS and self.checkExtentCRS(): reply = QMessageBox.question( self, self.tr("Extent CRS"), self. tr('Extent parameters must use the same CRS as the input layers.\n' 'Your input layers do not have the same extent as the project, ' 'so the extent might be in a wrong CRS if you have selected it from the canvas.\n' 'Do you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return ok, msg = self.alg.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.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.progressBar.setMaximum(0) self.lblProgress.setText(self.tr('Processing algorithm...')) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass self.setInfo( self.tr('<b>Algorithm \'{0}\' starting...</b>').format( self.alg.displayName()), escape_html=False) feedback.pushInfo(self.tr('Input parameters:')) feedback.pushCommandInfo(pformat(parameters)) 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): feedback.pushInfo( self.tr( 'Execution completed in {0:0.2f} seconds'.format( time.time() - start_time))) self.buttonCancel.setEnabled(False) self.finish(True, parameters, context, feedback) else: self.buttonCancel.setEnabled(False) self.resetGUI() else: command = self.alg.asPythonCommand(parameters, context) if command: ProcessingLog.addToLog(command) self.buttonCancel.setEnabled( self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel) def on_complete(ok, results): if ok: feedback.pushInfo( self.tr('Execution completed in {0:0.2f} seconds'. format(time.time() - start_time))) feedback.pushInfo(self.tr('Results:')) feedback.pushCommandInfo(pformat(results)) else: feedback.reportError( self.tr('Execution failed after {0:0.2f} seconds'. format(time.time() - start_time))) feedback.pushInfo('') self.buttonCancel.setEnabled(False) self.finish(ok, results, context, feedback) task = QgsProcessingAlgRunnerTask(self.alg, 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())) 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)
def union_executed(self, context, iteration, ok, result): #self.showInfo("Union executed: " + str(iteration) + ', OK: ' + str(ok) + ', Res: ' + str(result)) if not ok: self.showInfo("Union failed - " + str(iteration)) return # Get the result (polygon) dataset from the union algorithm unionlayer = QgsProcessingUtils.mapLayerFromString( result['OUTPUT'], context) # Add attributes provider = unionlayer.dataProvider() provider.addAttributes([QgsField('Area', QVariant.Double)]) provider.addAttributes([QgsField('Combined', QVariant.String, len=40)]) unionlayer.updateFields() unionlayer.startEditing() area_field_index = unionlayer.fields().lookupField('Area') # OK #self.showInfo('union, area field index: ' + str(area_field_index)) combined_field_index = unionlayer.fields().lookupField( 'Combined') # OK #self.showInfo('union, combined field index: ' + str(combined_field_index)) # Update the attributes for f in provider.getFeatures(): #self.showInfo('Feature: ' + str(f)) area = f.geometry().area() unionlayer.changeAttributeValue(f.id(), area_field_index, area) iidx = unionlayer.fields().lookupField('InputB') ridx = unionlayer.fields().lookupField('RefB') i = f.attributes()[iidx] r = f.attributes()[ridx] # Set the 'Combined' attribute value to show the combination comb = '' if i is not None: if r is not None: comb = str(i) + str(r) else: comb = str(i) else: if r is not None: comb = str(r) else: comb = None #self.showInfo('Combination: ' + str(comb)) unionlayer.changeAttributeValue(f.id(), combined_field_index, comb) unionlayer.commitChanges() # Do multipart to singlepart: # OK params = { #'INPUT': QgsProcessingUtils.mapLayerFromString(result['OUTPUT'], context), 'INPUT': unionlayer, #'INPUT': result['OUTPUT'], 'OUTPUT': 'memory:Singlepart' } task = QgsProcessingAlgRunnerTask(self.multitosinglealg, params, context) # Add extra parameters (context, iteration) using "partial" task.executed.connect( partial(self.tosingle_executed, context, iteration)) QgsApplication.taskManager().addTask(task) #self.showInfo('Start MultipartToSinglepart: ' + str(iteration)) # Do the statistics params = { 'INPUT': unionlayer, 'VALUES_FIELD_NAME': 'Area', 'CATEGORIES_FIELD_NAME': 'Combined', #'OUTPUT':'/home/havatv/stats.csv' 'OUTPUT': 'memory:Statistics' } task = QgsProcessingAlgRunnerTask(self.statalg, params, context) # Add extra parameters (context, iteration) using "partial" task.executed.connect(partial(self.stats_executed, context, iteration)) QgsApplication.taskManager().addTask(task)
def buffer_executed(self, context, iteration, kind, ok, result): #self.showInfo("Buffer executed (" + str(kind) + '): ' + # str(iteration) + ', OK: ' + str(ok) + # ', Res: ' + str(result)) self.tcmutex.acquire() try: self.testcounter = self.testcounter + 1 self.showInfo("Buffer finished: " + str(iteration) + ' - ' + str(kind) + ' ' + str(self.testcounter) + ' ' + str(QgsApplication.taskManager().count())) finally: self.tcmutex.release() #return if not ok: self.showInfo("Buffer failed - " + str(iteration) + ' ' + str(kind)) return #blayer = result['OUTPUT'] ## blayer blir string! # Get the result (line) dataset from the buffer algorithm blayer = QgsProcessingUtils.mapLayerFromString(result['OUTPUT'], context) # Add attribute provider = blayer.dataProvider() newfield = QgsField('InputB', QVariant.String, len=5) if kind == self.REF: newfield = QgsField('RefB', QVariant.String, len=5) provider.addAttributes([newfield]) blayer.updateFields() # Update the attribute blayer.startEditing() field_index = blayer.fields().lookupField('InputB') if kind == self.REF: field_index = blayer.fields().lookupField('RefB') #self.showInfo('refb, field index: ' + str(field_index)) for f in provider.getFeatures(): #self.showInfo('Feature (refb): ' + str(f)) if kind == self.REF: # Set the attribute value to 'R' blayer.changeAttributeValue(f.id(), field_index, 'R') else: # Set the attribute value to 'I' blayer.changeAttributeValue(f.id(), field_index, 'I') blayer.commitChanges() if kind == self.INPUT: self.ibmutex.acquire( ) # Important to acquire ibmutex first (deadlock) try: self.inputbuffers[iteration] = result['OUTPUT'] finally: self.ibmutex.release() elif kind == self.REF: self.rbmutex.acquire() try: self.referencebuffers[iteration] = result['OUTPUT'] finally: self.rbmutex.release() else: self.showInfo("Strange kind of buffer: " + str(kind)) # Do line overlay: # Works! if kind == self.INPUT: reflayercopy = QgsVectorLayer(self.reflayer.source(), 'refint' + str(iteration), self.reflayer.providerType()) #reflayercopy = self.copylayer(self.reflayer, 'refint' + str(iteration)) params = { 'INPUT': reflayercopy, #'INPUT': self.reflayer, #'OVERLAY': result['OUTPUT'], 'OVERLAY': blayer, #'OVERLAY': QgsProcessingUtils.mapLayerFromString(result['OUTPUT'], context), 'OUTPUT': 'memory:Intersection' } task = QgsProcessingAlgRunnerTask(self.intersectionalg, params, context) # Add a few extra parameters (context, radius) using "partial" task.executed.connect( partial(self.intersection_executed, context, iteration)) QgsApplication.taskManager().addTask(task) #self.showInfo('Start Intersection: ' + str(iteration)) elif kind == self.REF: # The reference buffer is used to remove parts of the input layer inlayercopy = QgsVectorLayer(self.inputlayer.source(), 'indiff' + str(iteration), self.inputlayer.providerType()) #inlayercopy = self.copylayer(self.inputlayer, 'indiff' + str(iteration)) params = { 'INPUT': inlayercopy, #'INPUT': self.inputlayer, 'OVERLAY': blayer, 'OUTPUT': 'memory:Difference' } task = QgsProcessingAlgRunnerTask(self.differencealg, params, context) # Add a few extra parameters (context, radius) using "partial" task.executed.connect( partial(self.difference_executed, context, iteration)) QgsApplication.taskManager().addTask(task) #self.showInfo('Start Difference: ' + str(iteration)) todelete = [] # buffer sizes to remove (after handling) # Do union, if possible # Check if both buffers are available: self.ibmutex.acquire() # Important to acquire ibmutex first (deadlock) try: self.rbmutex.acquire() try: for key in self.inputbuffers: if key in self.referencebuffers: # Union input # Does not work! params = { #'INPUT': self.inputbuffers[key], 'INPUT': QgsProcessingUtils.mapLayerFromString( self.inputbuffers[key], context), #'OVERLAY': self.referencebuffers[key], 'OVERLAY': QgsProcessingUtils.mapLayerFromString( self.referencebuffers[key], context), 'OUTPUT': 'memory:Union' } task = QgsProcessingAlgRunnerTask( self.unionalg, params, context) # Add a few extra parameters (context, radius) using "partial" task.executed.connect( partial(self.union_executed, context, iteration)) QgsApplication.taskManager().addTask(task) #self.showInfo('Start Union: ' + str(iteration)) todelete.append(key) #del self.inputbuffers[key] #del self.referencebuffers[key] for key in todelete: del self.inputbuffers[key] del self.referencebuffers[key] #self.showInfo('Removed key: ' + str(key)) finally: self.rbmutex.release() finally: self.ibmutex.release()
def runAlgorithm(self): self.feedback = self.createFeedback() self.context = dataobjects.createContext(self.feedback) self.context.setLogLevel(self.logLevel()) checkCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_CRS) try: # messy as all heck, but we don't want to call the dialog's implementation of # createProcessingParameters as we want to catch the exceptions raised by the # parameter panel instead... parameters = {} if self.mainWidget() is None else self.mainWidget( ).createProcessingParameters() if checkCRS and not self.algorithm().validateInputCrs( parameters, self.context): reply = QMessageBox.question( self, self.tr("Unmatching CRS's"), self.tr('Parameters do not all use the same CRS. This can ' 'cause unexpected results.\nDo you want to ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return ok, msg = self.algorithm().checkParameterValues( parameters, self.context) if not ok: QMessageBox.warning(self, self.tr('Unable to execute algorithm'), msg) return self.blockControlsWhileRunning() self.setExecutedAnyResult(True) self.cancelButton().setEnabled(False) self.iterateParam = None for param in self.algorithm().parameterDefinitions(): if isinstance( parameters.get(param.name(), None), QgsProcessingFeatureSourceDefinition ) and parameters[param.name( )].flags & QgsProcessingFeatureSourceDefinition.FlagCreateIndividualOutputPerInputFeature: self.iterateParam = param.name() break self.clearProgress() self.feedback.pushVersionInfo(self.algorithm().provider()) if self.algorithm().provider().warningMessage(): self.feedback.reportError( self.algorithm().provider().warningMessage()) self.feedback.pushInfo( QCoreApplication.translate('AlgorithmDialog', 'Algorithm started at: {}').format( datetime.datetime.now().replace( microsecond=0).isoformat())) self.setInfo(QCoreApplication.translate( 'AlgorithmDialog', '<b>Algorithm \'{0}\' starting…</b>').format( self.algorithm().displayName()), escapeHtml=False) self.feedback.pushInfo(self.tr('Input parameters:')) display_params = [] for k, v in parameters.items(): display_params.append("'" + k + "' : " + self.algorithm( ).parameterDefinition(k).valueAsPythonString(v, self.context)) self.feedback.pushCommandInfo('{ ' + ', '.join(display_params) + ' }') self.feedback.pushInfo('') start_time = time.time() def elapsed_time(start_time, result): delta_t = time.time() - start_time hours = int(delta_t / 3600) minutes = int((delta_t % 3600) / 60) seconds = delta_t - hours * 3600 - minutes * 60 str_hours = [self.tr("hour"), self.tr("hours")][hours > 1] str_minutes = [self.tr("minute"), self.tr("minutes")][minutes > 1] str_seconds = [self.tr("second"), self.tr("seconds")][seconds != 1] if hours > 0: elapsed = '{0} {1:0.2f} {2} ({3} {4} {5} {6} {7:0.0f} {2})'.format( result, delta_t, str_seconds, hours, str_hours, minutes, str_minutes, seconds) elif minutes > 0: elapsed = '{0} {1:0.2f} {2} ({3} {4} {5:0.0f} {2})'.format( result, delta_t, str_seconds, minutes, str_minutes, seconds) else: elapsed = '{0} {1:0.2f} {2}'.format( result, delta_t, str_seconds) return (elapsed) if self.iterateParam: # Make sure the Log tab is visible before executing the algorithm try: self.showLog() self.repaint() except: pass self.cancelButton().setEnabled( self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanCancel) if executeIterating(self.algorithm(), parameters, self.iterateParam, self.context, self.feedback): self.feedback.pushInfo( self.tr( elapsed_time(start_time, 'Execution completed in'))) self.cancelButton().setEnabled(False) self.finish(True, parameters, self.context, self.feedback) else: self.cancelButton().setEnabled(False) self.resetGui() else: self.history_details = { 'python_command': self.algorithm().asPythonCommand(parameters, self.context), 'algorithm_id': self.algorithm().id(), 'parameters': self.algorithm().asMap(parameters, self.context) } process_command, command_ok = self.algorithm( ).asQgisProcessCommand(parameters, self.context) if command_ok: self.history_details['process_command'] = process_command self.history_log_id, _ = QgsGui.historyProviderRegistry( ).addEntry('processing', self.history_details) QgsGui.instance().processingRecentAlgorithmLog().push( self.algorithm().id()) self.cancelButton().setEnabled( self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanCancel) def on_complete(ok, results): if ok: self.feedback.pushInfo( self.tr( elapsed_time(start_time, 'Execution completed in'))) self.feedback.pushInfo(self.tr('Results:')) r = { k: v for k, v in results.items() if k not in ('CHILD_RESULTS', 'CHILD_INPUTS') } self.feedback.pushCommandInfo(pformat(r)) else: self.feedback.reportError( self.tr( elapsed_time(start_time, 'Execution failed after'))) self.feedback.pushInfo('') if self.history_log_id is not None: self.history_details['results'] = results QgsGui.historyProviderRegistry().updateEntry( self.history_log_id, self.history_details) if self.feedback_dialog is not None: self.feedback_dialog.close() self.feedback_dialog.deleteLater() self.feedback_dialog = None self.cancelButton().setEnabled(False) self.finish(ok, results, self.context, self.feedback, in_place=self.in_place) self.feedback = None self.context = None 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() task = QgsProcessingAlgRunnerTask(self.algorithm(), parameters, self.context, self.feedback) if task.isCanceled(): on_complete(False, {}) else: task.executed.connect(on_complete) self.setCurrentTask(task) else: self.proxy_progress = QgsProxyProgressTask( QCoreApplication.translate( "AlgorithmDialog", "Executing “{}”").format( self.algorithm().displayName())) QgsApplication.taskManager().addTask(self.proxy_progress) self.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, self.context, self.feedback) else: ok, results = execute(self.algorithm(), parameters, self.context, self.feedback) self.feedback.progressChanged.disconnect() self.proxy_progress.finalize(ok) on_complete(ok, results) except AlgorithmDialogBase.InvalidParameterValue as e: self.flag_invalid_parameter_value(e.parameter.description(), e.widget) except AlgorithmDialogBase.InvalidOutputExtension as e: self.flag_invalid_output_extension(e.message, e.widget)
def runAlgorithm(self): self.feedback = self.createFeedback() self.context = dataobjects.createContext(self.feedback) checkCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_CRS) try: parameters = self.getParameterValues() if checkCRS and not self.algorithm().validateInputCrs( parameters, self.context): reply = QMessageBox.question( self, self.tr("Unmatching CRS's"), self.tr('Parameters do not all use the same CRS. This can ' 'cause unexpected results.\nDo you want to ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return ok, msg = self.algorithm().checkParameterValues( parameters, self.context) if not ok: QMessageBox.warning(self, self.tr('Unable to execute algorithm'), msg) return self.runButton().setEnabled(False) self.cancelButton().setEnabled(False) buttons = self.mainWidget().iterateButtons self.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.clearProgress() self.setProgressText( QCoreApplication.translate('AlgorithmDialog', 'Processing algorithm…')) self.setInfo(QCoreApplication.translate( 'AlgorithmDialog', '<b>Algorithm \'{0}\' starting…</b>').format( self.algorithm().displayName()), escapeHtml=False) self.feedback.pushInfo(self.tr('Input parameters:')) display_params = [] for k, v in parameters.items(): display_params.append("'" + k + "' : " + self.algorithm( ).parameterDefinition(k).valueAsPythonString(v, self.context)) self.feedback.pushCommandInfo('{ ' + ', '.join(display_params) + ' }') self.feedback.pushInfo('') start_time = time.time() if self.iterateParam: # Make sure the Log tab is visible before executing the algorithm try: self.showLog() self.repaint() except: pass self.cancelButton().setEnabled( self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanCancel) if executeIterating(self.algorithm(), parameters, self.iterateParam, self.context, self.feedback): self.feedback.pushInfo( self.tr('Execution completed in {0:0.2f} seconds'). format(time.time() - start_time)) self.cancelButton().setEnabled(False) self.finish(True, parameters, self.context, self.feedback) else: self.cancelButton().setEnabled(False) self.resetGui() else: command = self.algorithm().asPythonCommand( parameters, self.context) if command: ProcessingLog.addToLog(command) QgsGui.instance().processingRecentAlgorithmLog().push( self.algorithm().id()) self.cancelButton().setEnabled( self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanCancel) def on_complete(ok, results): if ok: self.feedback.pushInfo( self.tr('Execution completed in {0:0.2f} seconds'). format(time.time() - start_time)) self.feedback.pushInfo(self.tr('Results:')) self.feedback.pushCommandInfo(pformat(results)) else: self.feedback.reportError( self.tr('Execution failed after {0:0.2f} seconds'). format(time.time() - start_time)) self.feedback.pushInfo('') if self.feedback_dialog is not None: self.feedback_dialog.close() self.feedback_dialog.deleteLater() self.feedback_dialog = None self.cancelButton().setEnabled(False) if not self.in_place: self.finish(ok, results, self.context, self.feedback) elif ok: self.close() self.feedback = None self.context = None 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() task = QgsProcessingAlgRunnerTask(self.algorithm(), parameters, self.context, self.feedback) if task.isCanceled(): on_complete(False, {}) else: task.executed.connect(on_complete) self.setCurrentTask(task) else: self.proxy_progress = QgsProxyProgressTask( QCoreApplication.translate( "AlgorithmDialog", "Executing “{}”").format( self.algorithm().displayName())) QgsApplication.taskManager().addTask(self.proxy_progress) self.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, self.context, self.feedback) else: ok, results = execute(self.algorithm(), parameters, self.context, self.feedback) self.feedback.progressChanged.disconnect() self.proxy_progress.finalize(ok) on_complete(ok, results) except AlgorithmDialogBase.InvalidParameterValue as e: try: 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.messageBar().clearWidgets() self.messageBar().pushMessage( "", self.tr("Wrong or missing parameter value: {0}").format( e.parameter.description()), level=Qgis.Warning, duration=5) except AlgorithmDialogBase.InvalidOutputExtension as e: try: 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.messageBar().clearWidgets() self.messageBar().pushMessage("", e.message, level=Qgis.Warning, duration=5)
def startWorker(self): """Initialises and starts.""" self.inputbuffers = {} self.referencebuffers = {} self.polycount = {} self.completeness = {} self.miscodings = {} self.statistics = {} self.testcounter = 0 try: layerindex = self.inputLayer.currentIndex() layerId = self.inputLayer.itemData(layerindex) self.inputlayer = QgsProject.instance().mapLayer(layerId) if self.inputlayer is None: self.showError(self.tr('No input layer defined')) return refindex = self.referenceLayer.currentIndex() reflayerId = self.referenceLayer.itemData(refindex) self.reflayer = QgsProject.instance().mapLayer(reflayerId) if layerId == reflayerId: self.showInfo('The reference layer must be different' ' from the input layer!') return if self.reflayer is None: self.showError(self.tr('No reference layer defined')) return if self.inputlayer.sourceCrs().authid() != self.reflayer.sourceCrs( ).authid(): self.showWarning('Layers must have the same CRS - Input: ' + str(self.inputlayer.sourceCrs().authid()) + ' Reference: ' + str(self.reflayer.sourceCrs().authid())) return if self.reflayer.sourceCrs().isGeographic(): self.showWarning('Geographic CRS used -' + ' computations will be in decimal degrees!') # Algorithms self.bufferalg = QgsApplication.processingRegistry().algorithmById( 'native:buffer') #self.bufferalg=QgsApplication.processingRegistry().algorithmById('qgis:buffer') self.unionalg = QgsApplication.processingRegistry().algorithmById( 'qgis:union') self.intersectionalg = QgsApplication.processingRegistry( ).algorithmById('qgis:intersection') self.differencealg = QgsApplication.processingRegistry( ).algorithmById('qgis:difference') self.multitosinglealg = QgsApplication.processingRegistry( ).algorithmById('qgis:multiparttosingleparts') self.statalg = QgsApplication.processingRegistry().algorithmById( 'qgis:statisticsbycategories') # Calculate the total length of lines in the layers self.inpgeomlength = 0 for f in self.inputlayer.getFeatures(): self.inpgeomlength = self.inpgeomlength + f.geometry().length() self.refgeomlength = 0 for f in self.reflayer.getFeatures(): self.refgeomlength = self.refgeomlength + f.geometry().length() # Number of steps and radii steps = self.stepsSB.value() startradius = self.startRadiusSB.value() endradius = self.endRadiusSB.value() delta = (endradius - startradius) / (steps - 1) self.radiuses = [] for step in range(steps): self.radiuses.append(startradius + step * delta) #self.radiuses = [10,20,50] #self.showInfo(str(self.radiuses)) feedback = QgsProcessingFeedback() selectedinputonly = self.selectedFeaturesCheckBox.isChecked() selectedrefonly = self.selectedRefFeaturesCheckBox.isChecked() #plugincontext = dataobjects.createContext(feedback) #self.showInfo('Plugin context: ' + str(plugincontext)) #self.showInfo('GUI thread: ' + str(QThread.currentThread()) + ' ID: ' + str(QThread.currentThreadId())) ###### Testing QgsTask!!! # I følge oppskrifta på opengis.ch context = QgsProcessingContext() #context = plugincontext #self.showInfo('Normal context: ' + str(context)) #context.setProject(QgsProject.instance()) for radius in self.radiuses: # Buffer input # Works! inlayercopy = QgsVectorLayer(self.inputlayer.source(), 'in' + str(radius), self.inputlayer.providerType()) ##inlayercopy = self.copylayer(self.inputlayer, 'in' + str(radius)) params = { 'INPUT': inlayercopy, #'INPUT': self.inputlayer, 'DISTANCE': radius, #'OUTPUT':'/home/havatv/test.shp' 'OUTPUT': 'memory:InputBuffer' } task = QgsProcessingAlgRunnerTask(self.bufferalg, params, context) ##task = QgsProcessingAlgRunnerTask(self.bufferalg,params,context,feedback) # Add a few extra parameters (context, radius and "input") using "partial" task.executed.connect( partial(self.buffer_executed, context, radius, self.INPUT)) QgsApplication.taskManager().addTask(task) self.showInfo('Start Input buffer: ' + str(radius)) # Buffer reference # Works! reflayercopy = QgsVectorLayer(self.reflayer.source(), 'ref' + str(radius), self.reflayer.providerType()) #reflayercopy = self.copylayer(self.reflayer, 'ref' + str(radius)) params = { 'INPUT': reflayercopy, #'INPUT': self.reflayer, 'DISTANCE': radius, #'OUTPUT':'/home/havatv/test.shp' 'OUTPUT': 'memory:ReferenceBuffer' } task = QgsProcessingAlgRunnerTask(self.bufferalg, params, context) #task = QgsProcessingAlgRunnerTask(self.bufferalg,params,context,feedback) # Add a few extra parameters (context, radius and "reference") using "partial" task.executed.connect( partial(self.buffer_executed, context, radius, self.REF)) QgsApplication.taskManager().addTask(task) self.showInfo('Start Ref buffer: ' + str(radius)) ##task.begun.connect(self.task_begun) ##task.taskCompleted.connect(self.task_completed) ##task.progressChanged.connect(self.task_progress) ##task.taskTerminated.connect(self.task_stopped) #iteration = 5 # Identifiserer hvilken iterasjon det er snakk om ## I følge oppskrifta på opengis.ch (partial legger inn context som første parameter): ## context ser ut til å være helt nødvendig! #task.executed.connect(partial(self.task_executed, context, iteration)) #self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) #self.button_box.button(QDialogButtonBox.Close).setEnabled(False) #self.button_box.button(QDialogButtonBox.Cancel).setEnabled(True) except: import traceback self.showError(traceback.format_exc()) else: pass
def test_flags(self): """ Test task flags """ thread_safe_alg = QgsApplication.processingRegistry().algorithmById( 'native:buffer') nonthread_safe_alg = QgsApplication.processingRegistry().algorithmById( 'native:setprojectvariable') context = QgsProcessingContext() context.setProject(QgsProject.instance()) feedback = ConsoleFeedBack() task = QgsProcessingAlgRunnerTask(thread_safe_alg, {}, context=context, feedback=feedback) self.assertEqual(task.flags(), QgsTask.CanCancel) task = QgsProcessingAlgRunnerTask(thread_safe_alg, {}, context=context, feedback=feedback, flags=QgsTask.Flags()) self.assertEqual(task.flags(), QgsTask.Flags()) task = QgsProcessingAlgRunnerTask(thread_safe_alg, {}, context=context, feedback=feedback, flags=QgsTask.CanCancel) self.assertEqual(task.flags(), QgsTask.CanCancel) task = QgsProcessingAlgRunnerTask(thread_safe_alg, {}, context=context, feedback=feedback, flags=QgsTask.CancelWithoutPrompt) self.assertEqual(task.flags(), QgsTask.CancelWithoutPrompt) task = QgsProcessingAlgRunnerTask(thread_safe_alg, {}, context=context, feedback=feedback, flags=QgsTask.CancelWithoutPrompt | QgsTask.CanCancel) self.assertEqual(task.flags(), QgsTask.CancelWithoutPrompt | QgsTask.CanCancel) # alg which can't be canceled task = QgsProcessingAlgRunnerTask(nonthread_safe_alg, {}, context=context, feedback=feedback) self.assertEqual(task.flags(), QgsTask.Flags()) # we clear the CanCancel flag automatically, since the algorithm itself cannot be canceled task = QgsProcessingAlgRunnerTask(nonthread_safe_alg, {}, context=context, feedback=feedback, flags=QgsTask.CanCancel) self.assertEqual(task.flags(), QgsTask.Flags()) # hidden task task = QgsProcessingAlgRunnerTask(thread_safe_alg, {}, context=context, feedback=feedback, flags=QgsTask.Hidden) self.assertEqual(task.flags(), QgsTask.Hidden) task = QgsProcessingAlgRunnerTask(thread_safe_alg, {}, context=context, feedback=feedback, flags=QgsTask.Hidden | QgsTask.CanCancel) self.assertEqual(task.flags(), QgsTask.Hidden | QgsTask.CanCancel) task = QgsProcessingAlgRunnerTask(thread_safe_alg, {}, context=context, feedback=feedback, flags=QgsTask.Hidden | QgsTask.CanCancel | QgsTask.CancelWithoutPrompt) self.assertEqual( task.flags(), QgsTask.Hidden | QgsTask.CanCancel | QgsTask.CancelWithoutPrompt) task = QgsProcessingAlgRunnerTask(nonthread_safe_alg, {}, context=context, feedback=feedback, flags=QgsTask.Hidden) self.assertEqual(task.flags(), QgsTask.Hidden) task = QgsProcessingAlgRunnerTask(nonthread_safe_alg, {}, context=context, feedback=feedback, flags=QgsTask.Hidden | QgsTask.CanCancel) self.assertEqual(task.flags(), QgsTask.Hidden)
def calculate_zonal_stats(callback, zonal_layer, points_layer, join_fields, output_layer_name, discard_nonmatching=False, predicates=('intersects', ), summaries=('sum', )): """ Leveraging the QGIS processing algorithm 'Join attributes by location (summary)', that is described in QGIS as follows: This algorithm takes an input vector layer and creates a new vector layer that is an extended version of the input one, with additional attributes in its attribute table. The additional attributes and their values are taken from a second vector layer. A spatial criteria is applied to select the values from the second layer that are added to each feature from the first layer in the resulting one. The algorithm calculates a statistical summary for the values from matching features in the second layer (e.g. maximum value, mean value, etc). The full description of the algorithm can be obtained as follows: processing.algorithmHelp('qgis:joinbylocationsummary') and it includes the lists of predicates and summaries. The code of the algorithm is here: https://github.com/qgis/QGIS/blob /483b4ff977e3d36b166fac792254c31e89e3aeae/python/plugins/processing/algs /qgis/SpatialJoinSummary.py # NOQA :param callback: function to be called once the aggregation is complete, passing the output zonal layer as a parameter :param zonal_layer: vector layer containing polygons (or its path) :param points_layer: vector layer containing points (or its path) :param join_fields: fields for which we want to calculate statistics (e.g. structural) :param output_layer_name: a memory layer will be produced, named 'memory:output_layer_name' :param discard_nonmatching: discard records which could not be joined (in our case, purge zones that contain no loss/damage points) :param predicates: geometric predicates (default: 'intersects') :param summaries: statistics to be calculated for each join field (default: 'sum') :returns: it waits until the task is complete or terminated, then it calls the callback function, passing the output QgsVectorLayer as parameter, or None in case of failure """ processing.Processing.initialize() alg = QgsApplication.processingRegistry().algorithmById( 'qgis:joinbylocationsummary') # make sure to use the actual lists of predicates and summaries as defined # in the algorithm when it is instantiated predicate_keys = [predicate[0] for predicate in alg.predicates] PREDICATES = dict(zip(predicate_keys, range(len(predicate_keys)))) summary_keys = [statistic[0] for statistic in alg.statistics] SUMMARIES = dict(zip(summary_keys, range(len(summary_keys)))) context = QgsProcessingContext() feedback = QgsProcessingFeedback() params = { 'DISCARD_NONMATCHING': discard_nonmatching, 'INPUT': zonal_layer, 'JOIN': points_layer, 'PREDICATE': [PREDICATES[predicate] for predicate in predicates], 'JOIN_FIELDS': join_fields, 'SUMMARIES': [SUMMARIES[summary] for summary in summaries], 'OUTPUT': 'memory:%s' % output_layer_name } task = QgsProcessingAlgRunnerTask(alg, params, context, feedback) task.executed.connect(partial(task_finished, context, callback)) QgsApplication.taskManager().addTask(task) while True: # the user can "cancel" the task, interrupting this loop QgsApplication.processEvents() # status can be queued, onhold, running, complete, terminated if task.status() > 2: # Complete or terminated return time.sleep(0.1)