Suite

Forcer l'outil Python Toolbox à rompre la boucle et à effectuer un nettoyage lorsque l'utilisateur clique sur Annuler ?

Forcer l'outil Python Toolbox à rompre la boucle et à effectuer un nettoyage lorsque l'utilisateur clique sur Annuler ?


J'écris un outil Python qui effectue des tâches lourdes en boucle. Lorsque l'utilisateur clique sur "Fermer", je souhaite rompre la boucle, effectuer un nettoyage, puis terminer.

Considérez cet exemple d'outil qui compte à rebours à partir de dix :

import arcpy import time class Tool(object): def __init__(self): self.label = "Cancel Test" self.description = "Test comment fonctionne la fonction d'annulation." self.canRunInBackground = False def execute(self, parameters, messages): for i in range(10, -1, -1): arcpy.AddMessage(i) time.sleep(1) arcpy.AddMessage("Nous avons lift- off !") retour

Lorsque je clique sur "Fermer" au début de l'exécution, cela ne interrompt pas la boucle et ne me donne pas la possibilité d'effectuer un nettoyage. Au lieu de cela, il boucle jusqu'à la fin et ajoute le message« Nous avons le décollage ! »avant qu'il n'affiche ceci :

Script terminé CancelTest… Fonction annulée (CancelTest) abandonnée par l'utilisateur. Échec à [TIME] (Temps écoulé : 10,01 secondes)

J'ai trouvé une documentation pour ArcGIS Pro (qui utilise Python 3.4) qui décrit comment vous pouvez le faire en utilisantest annulé:

import arcpy import time #Assurez-vous qu'il ne s'annule pas automatiquement. arcpy.env.autoCancelling = False class Tool(object): def __init__(self): self.label = "Cancel Test" self.description = "Test comment fonctionne la fonction d'annulation." self.canRunInBackground = False def execute(self, parameters, messages): for i in range(10, -1, -1): arcpy.AddMessage(i) time.sleep(1) #Vérifiez si l'utilisateur a cliqué sur "Fermer" if arcpy.env.isCancelled : arcpy.AddMessage("Lancement abandonné !") return arcpy.AddMessage("Nous avons un décollage !") return

Cependant, lorsque je l'exécute à partir d'ArcCatalog 10.3 (c'est-à-dire sans utiliser ArcGIS Pro), l'erreur suivante s'affiche :

Traceback (appel le plus récent en dernier) : fichier "H:Mina DokumentDGDPythonCancelTest.py", ligne 19, en cours d'exécution si arcpy.env.isCancelled : AttributeError : l'objet 'GPEnvironment' n'a pas d'attribut 'isCancelled'

Existe-t-il un moyen d'imiter le comportement disponible dans ArcGIS Pro dans une boîte à outils Python ordinaire à l'aide de Python 2.7 ?


La réponse de Farid Cher est partiellement correcte.

Vous pouvez utiliser le Progressor pour "attraper" un événement d'annulation à partir d'un script Python, et vous pouvez également effectuer le nettoyage nécessaire, si vous incluez l'appel dans unessayez… saufclause.

Par exemple, une boîte à outils python :

import arcpy import time class Toolbox(object): def __init__(self): self.label = "Toolbox" self.alias = "" self.tools = [Tool] class Tool(object): def __init__(self): self. label = "Tool" self.description = "" self.canRunInBackground = False def execute(self, parameters, messages): some_stuff = [] # une variable à nettoyer plus tard max = 1000 arcpy.SetProgressor('step','Testing ArcPy script cancel',0,max,1) # Vous n'avez pas besoin d'être dans une boucle for, mais c'est le # moyen le plus simple de montrer un exemple pour i in range(max) : essayez : # ne mettez que SetProgressorPosition dans ce clause # sinon vous pouvez détecter d'autres erreurs qui ne sont pas # l'utilisateur annulant arcpy.SetProgressorPosition() sauf : arcpy.AddWarning('User cancelled at i = {0}'.format(i)) del some_stuff # heure de retour du nettoyage. sleep(0.1) some_stuff.append(i) arcpy.AddMessage('L'utilisateur n'a pas annulé') del some_stuff

Cela permet à l'utilisateur d'exécuter l'outil :

Et puis annuler, ce qui est géré avec élégance (et toujours considéré comme un échec) :

Éditer: Cela ne semble pas fonctionner avec 10.3 ou ArcGIS Pro.

Pour rattraper les annulations (testées dans ArcGIS Pro), nous ramenons notre bon amiarcgisscripting. Ceci est similaire à la capture de l'annulation du progresseur, mais l'intégralité de la fonction d'exécution, ou au moins la majorité, doit être à l'intérieur duessayer:

import arcpy import arcgisscripting import time class Toolbox(object): def __init__(self): self.label = "Toolbox" self.alias = "" self.tools = [Tool] class Tool(object): def __init__(self): self.label = "Tool" self.description = "" self.canRunInBackground = False def execute(self, parameters, messages): try: some_stuff = [] max = 1000 arcpy.SetProgressor('step','Testing ArcPy script cancel ',0,max,1) pour i dans la plage (max): arcpy.SetProgressorPosition() time.sleep(0.1) some_stuff.append(i) sauf arcgisscripting.ExecuteAbort: arcpy.AddWarning('User cancelled at i={0 }'.format(i)) del some_stuff return arcpy.AddMessage('L'utilisateur n'a pas annulé') del some_stuff


Votre message est clair :

AttributeError : l'objet 'GPEnvironment' n'a pas d'attribut 'isCancelled'

En réalitéarcpy.env.autoCancellingetarcpy.env.isCancelledsont ajoutés dans Arcgis Pro 1.1 (prévu pour ArcGIS 10.4)

De plus, si vous utilisez Arcgis 10.3, ces fonctionnalités ne sont pas disponibles.


Le moyen le plus proche auquel je puisse penser est l'utilisation de Progressor dans Arcpy. En utilisant progressor, une fois que l'utilisateur appuie sur le bouton d'annulation, la boucle se rompt et l'exécution se termine. Cependant, vous ne pouvez pas faire de nettoyage ! Voici un exemple de code :

def execute(self, parameters, messages): import time n=10 p=1 arcpy.SetProgressor("step", "Step progressor: Counting from 0 to {0}".format(n), 0, n, p) loopTime=.3 for i in range(n): if (i % p) == 0: ## mettez votre code personnalisé ici ## cela interrompra la boucle si l'utilisateur annule. et l'exécution de l'outil se termine arcpy.SetProgressorLabel("Iteration : {0}".format(i)) arcpy.SetProgressorPosition(i) time.sleep(loopTime) arcpy.AddMessage("Vous n'aurez pas ici pour faire le nettoyage si la boucle se termine prématurément") arcpy.SetProgressorLabel("Itération terminée : {0}".format(i + 1)) arcpy.SetProgressorPosition(i + 1) return

Mise à jour (Pour répondre aux questions en commentaire) :

pourquoi l'utilisation d'un progresseur modifie le comportement du programme, à part l'ajout d'une belle barre de progression ?

Le progresseur est une sorte de traitement multi-thread. chaque fois que le progresseur avance, l'interface graphique (un fil distinct) doit être mise à jour. Lorsque vous utilisez un progresseur, vous exécutez votre code dans un autre thread en ligne. Cependant, ce n'est certainement pas comme le géotraitement en arrière-plan dans lequel vous pouvez exécuter plusieurs threads simultanément.

L'utilisation d'un progresseur oblige-t-elle ArcGIS à freiner le code lorsque l'utilisateur appuie sur « Annuler » ?

Oui, notez que la rupture se produit dans la boucle, où vous mettez à jour votre barre de progression (par exemple comme dans l'exemple ci-dessus)

où ça casse ? Est-ce directement après la ligne à laquelle il se trouve lorsque le bouton est enfoncé ?

La rupture se produit entre deux étapes de progression que l'utilisateur clique sur le bouton annuler. Dans l'exemple de code ci-dessus, c'est :

if (i % p) == 0: ## mettez votre code personnalisé ici ## cela interrompra la boucle si l'utilisateur annule. et l'exécution de l'outil se termine arcpy.SetProgressorLabel("Iteration : {0}".format(i))

Si l'utilisateur cliqueAnnuler, votre code personnalisé sera exécuté jusqu'à ce qu'il atteignearcpy.SetProgressorLabel("Itération : {0}".format(i)). C'est là que l'outil gp sera informé de l'événement d'annulation (par exemple, tuez le thread en ligne séparé)


Voir la vidéo: cours python boucle for.. in range.. programmation tutoriel lycée