Більше

Змушуючи інструмент Python Toolbox розривати цикл і виконувати очищення, коли користувач натискає Скасувати?


Я пишу інструмент Python, який виконує важкі завдання в циклі. Коли користувач натискає "Закрити", я хочу розірвати цикл, виконати деяке очищення, а потім завершити.

Розглянемо цей приклад інструменту, який відраховує від десяти:

import arcpy клас часу імпорту Інструмент (об'єкт): def __init __ (self): self.label = "Скасувати тест" self.description = "Перевіряє, як працює функція скасування." self.canRunInBackground = False def execute (self, параметри, повідомлення): для i в діапазоні (10, -1, -1): arcpy.AddMessage (i) time.sleep (1) arcpy.AddMessage ("У нас є lift- вимкнено! ") повернення

Коли я натискаю "Закрити" на початку виконання, це не гальмує цикл і не дає мені можливості виконати будь -яку очистку. Натомість він зациклюється до самого кінця і додає повідомлення"У нас виліт!"перш ніж він виведе це:

Завершено сценарій CancelTest… Користувач скасував функцію (CancelTest). Помилка в [TIME] (час, що минув: 10,01 секунди)

Я знайшов документацію для ArcGIS Pro (яка використовує Python 3.4), яка описує, як ви можете це зробити за допомогоювідмінено:

import arcpy import time #Переконайтеся, що він не скасовується автоматично. arcpy.env.autoCancelling = False Клас Інструмент (об'єкт): def __init __ (self): self.label = "Скасувати тест" self.description = "Перевіряє, як працює функція скасування." self.canRunInBackground = False def execute (self, параметри, повідомлення): для i в діапазоні (10, -1, -1): arcpy.AddMessage (i) time.sleep (1) #Перевірте, чи користувач натиснув "Закрити" якщо arcpy.env.isCanceled: arcpy.AddMessage ("Запуск припинено!") повертаємо arcpy.AddMessage ("У нас є підйом!") повернення

Однак, коли я запускаю це з ArcCatalog 10.3 (тобто не використовую ArcGIS Pro), це видає мені таку помилку:

Відстеження (останній виклик останній): Файл "H:  Mina Dokument  DGD  Python  CancelTest.py", рядок 19, виконується, якщо arcpy.env.is Скасовано: AttributeError: Об'єкт "GPEnvironment" не має атрибута "isCanceled"

Чи все -таки можна імітувати поведінку, доступну в ArcGIS Pro у звичайному наборі інструментів Python за допомогою Python 2.7?


Відповідь Фаріда Шер частково правильна.

Ви можете використовувати прогресор, щоб "зловити" подію скасування з сценарію Python, а також можете виконати будь -яку необхідну очистку, якщо ви вкладете виклик успробуйте ... крімпункт.

Наприклад, набір інструментів python:

import arcpy клас часу імпорту Toolbox (об'єкт): def __init __ (self): self.label = "Панель інструментів" self.alias = "" self.tools = [Tool] клас Інструмент (об'єкт): def __init __ (self): self. label = "Інструмент" self.description = "" self.canRunInBackground = Помилкове визначення def (self, параметри, повідомлення): some_stuff = [] # змінна для подальшого очищення max = 1000 arcpy.SetProgressor ('step', 'Testing ArcPy script cancel ', 0, max, 1) # Вам не потрібно бути у циклі for, але це # найпростіший спосіб показати приклад для i в діапазоні (макс.): try: # тільки вставте SetProgressorPosition у це пункт # інакше ви можете впіймати інші помилки, які не # користувач скасовує arcpy.SetProgressorPosition (), крім: arcpy.AddWarning ('Користувач скасовано за i = {0}'. format (i)) del some_stuff # очищення час повернення. sleep (0.1) some_stuff.append (i) arcpy.AddMessage ('Користувач не скасував') del some_stuff

Це дозволяє користувачеві запускати інструмент:

А потім скасуйте, що обробляється витончено (і все ще зараховується як невдалий запуск):

редагувати: Здається, це не працює з 10.3 або ArcGIS Pro.

Щоб виявити скасування (перевірено в ArcGIS Pro), ми повертаємо нашого хорошого другаarcgisscripting. Це подібно до того, як відмінити скасування від прогресора, але вся функція виконувати, або принаймні більшість її, повинна бути всерединіспробуйте:

import arcpy import arcgisscripting клас часу імпорту Панель інструментів (об'єкт): def __init __ (себе): self.label = "Панель інструментів" self.alias = "" self.tools = [Інструмент] клас Інструмент (об'єкт): def __init __ (само): self.label = "Інструмент" self.description = "" self.canRunInBackground = Помилкове виконання def (self, параметри, повідомлення): try: some_stuff = [] max = 1000 arcpy.SetProgressor ('step', 'Тестування скасування сценарію ArcPy ', 0, max, 1) для i в діапазоні (max): arcpy.SetProgressorPosition () time.sleep (0.1) some_stuff.append (i), крім arcgisscripting.ExecuteAbort: arcpy.AddWarning (' Користувач скасовано за i = {0 } '. format (i)) del some_stuff return arcpy.AddMessage (' Користувач не скасував ') del some_stuff


Ваше повідомлення чітке:

AttributeError: Об'єкт "GPEnvironment" не має атрибута "isCanceled"

Фактичноarcpy.env.autoCancellingтаarcpy.env.isСкасованододано в Arcgis Pro 1.1 (планується для ArcGIS 10.4)

Крім того, якщо ви використовуєте Arcgis 10.3, ці функції недоступні.


Найближчий спосіб, який я можу придумати, - це використання Progressor в Arcpy. За допомогою прогресора, як тільки користувач натисне кнопку скасування, цикл розривається, і виконання припиняється. Однак ніякої чистки не можна робити! Ось зразок коду:

def execute (self, параметри, повідомлення): час імпорту n = 10 p = 1 arcpy.SetProgressor ("крок", "Прогресор кроків: підрахунок від 0 до {0}". формат (n), 0, n, p) loopTime = .3 для i в діапазоні (n): якщо (i % p) == 0: ## розмістіть тут власний код ##, це розірве цикл, якщо користувач скасує. і виконання інструменту завершує arcpy.SetProgressorLabel ("Ітерація: {0}". format (i)) arcpy.SetProgressorPosition (i) time.sleep (loopTime) arcpy.AddMessage ("Ви не потрапите сюди, щоб очистити, якщо цикл закінчується передчасно ") arcpy.SetProgressorLabel (" Ітерація завершена: {0} ". формат (i + 1)) arcpy.SetProgressorPosition (i + 1) return

Оновлення (Щоб відповісти на запитання в коментарі):

чому використання прогресора змінює поведінку програми, окрім додавання красиво виглядає індикатора прогресу?

Прогресор-це різновид багатопоточної обробки. щоразу, коли прогресор крокує вперед, графічний інтерфейс (окремий потік) повинен оновлюватися. Під час використання прогресора ви запускаєте свій код в іншому вбудованому потоці. Однак це точно не схоже на фонову геообробку, в якій можна запускати кілька потоків одночасно.

Чи робить використання прогресора ArcGIS гальмуванням коду, коли користувач натискає "Скасувати"?

Так, зверніть увагу, що розрив відбувається в циклі, де ви оновлюєте індикатор прогресу (наприклад, як у прикладі вище)

де він ламається? Це безпосередньо після будь -якого рядка, коли він натискається на кнопку?

Розрив відбувається між будь -якими двома кроками, коли користувач натискає кнопку скасування. У наведеному вище прикладі коду це:

якщо (i % p) == 0: ## розмістіть тут власний код ##, це розірве цикл, якщо користувач скасує. а виконання інструменту припиняє arcpy.SetProgressorLabel ("Ітерація: {0}". формат (i))

Якщо користувач натискаєскасувати, ваш користувацький код буде виконуватися, доки він не досягнеarcpy.SetProgressorLabel ("Ітерація: {0}". формат (i)). Тут інструмент gp отримуватиме сповіщення про подію скасування (наприклад, вбити окремий вбудований потік)