Инструмент ArcGIS Python - импорт пользовательского скрипта в класс ToolValidator

9

На прошлой неделе я опубликовал вопрос о настройке класса ToolValidator и получил несколько очень хороших ответов. Работая с предлагаемыми решениями, я создал пользовательский модуль, который выполняет запросы к базе данных, и будет вызываться как классом ToolValidator (для предоставления значений для раскрывающихся списков), так и позже в сценарии геообработки (чтобы получить другие параметры на основе элементов, выбранных в раскрывающихся списках). Однако я не могу вызвать пользовательский модуль в классе ToolValidator. Я пытался присоединиться к пути без удачи. Когда я пытаюсь применить эти изменения к сценарию, я получаю сообщение об ошибке во время выполнения: [Errno 9] Неверный дескриптор файла. Если я закомментирую строку импорта, ошибок нет.

sys.path.append('my_custom_module_directory')
import my_custom_module

Многие из вас могут спросить, почему бы мне не реализовать собственный инструмент с ArcObjects. Причина в том, что мои конечные пользователи не имеют прав, необходимых для регистрации ЛЮБЫХ библиотек на своем компьютере.

ОБНОВЛЕНИЕ: Это происходило со мной в ArcGIS 10. Интересно, что я изначально добавлял путь в функцию initialiazeParameters класса ToolValidator. Если я добавлю за пределы (т.е. поверх) класса ToolValidator, все будет работать так, как ожидается.

sys.path.append('C:/Working/SomeFolder')
import somescript -------->THIS WORKS

class ToolValidator:
  """Class for validating a tool's parameter values and controlling
  the behavior of the tool's dialog."""

  def __init__(self):
    """Setup arcpy and the list of tool parameters."""
    import arcpy
    sys.path.append('C:/Working/SomeFolder')
    import somescript -------> THIS DOESNT WORK
    self.params = arcpy.GetParameterInfo()

ОБНОВЛЕНИЕ 2: я думаю, что нашел истинную причину своей проблемы. В фрагментах кода в этом посте я добавляю реальные пути (например, C: / Working / SomeFolder) к sys.path. В моем собственном классе ToolValidator я строил относительный путь, используя os.path.dirname(__file__)+ "\ my_special_folder ...". Я ожидал, что os.path.dirname(__file__)это вернет путь к набору инструментов, так как он содержит класс ToolValidator. Я пришел к выводу, что это не тот случай. Насколько я могу судить, класс ToolValidator фактически никогда не записывается в файл .py, и я предполагаю, что этот код передается интерпретатору python в памяти, поэтому __file__он бесполезен, или какой-то временный скрипт сохраняется и затем выполняется execfile ( path_to_script) вызывается, снова рендеринг__file__бесполезный. Я уверен, что есть и другие причины, по которым я скучаю.

Короче говоря, если я использую жестко закодированный путь, sys.append работает где угодно, относительные пути не так хорошо работают в классе ToolValidator.

user890
источник
Это в 9,3 или 10?
Джейсон Шайрер,
У нас возникают проблемы с воспроизведением этого здесь, в Esri, если мы изолируем причину, мы можем сделать бэкпорт для исправления для 10.0 SP3. В то же время, я полагаю, вы застряли на первом, а не на последнем шаблоне использования.
Джейсон Шайрер,

Ответы:

3

Для этого я после запуска ArcGIS или ArcCatalog сначала запускаю фиктивный инструмент («Выполнить это один раз»), вызывающий скрипт dummy.py. После этого вы можете импортировать скрипты Python в валидатор, используя sys.argv [0]. Это будет указывать на папку, где был расположен первый скрипт. После этого вы можете импортировать необходимый скрипт в класс de Validator.

Сценарий dummy.py, вызываемый инструментом «Запустить один раз»:

import arcgisscripting, sys, os
gp = arcgisscripting.create(9.3)

# set up paths to Toolshare, scripts en Tooldata
scriptPath = sys.path[0]  
toolSharePath = os.path.dirname(scriptPath)
toolDataPath = toolSharePath + os.sep + "ToolData"
gp.addmessage("scriptPath: " + scriptPath)
gp.addmessage("toolSharePath: " + toolSharePath)
gp.addmessage("toolDataPath: " + toolDataPath)

# Use this to read properties, VERY handy!!
import ConfigParser
config = ConfigParser.SafeConfigParser()
config.readfp(open(scriptPath + os.sep + 'properties.ini'))
activeOTAP = config.get('DEFAULT', 'activeOTAP')
activeprojectspace = config.get('DEFAULT', 'activeprojectspace')
activeproject = config.get('DEFAULT', 'activeproject')
activesdeconnection = config.get('DEFAULT', 'activesdeconnection')

Извините, не могу получить правильное форматирование С уважением, Мартен Тромп

Маартен Тромп
источник
3

Наконец-то взломали эту ужасную ошибку! Например, когда вы пытаетесь применить изменения для импорта относительного модуля или пакета, вы можете увидеть следующую ошибку:

введите описание изображения здесь

Вариант 1.
Только для разработчика, добавьте полный путь к модулю в PYTHONPATH . Вам нужно будет перезапустить ArcMap / ArcCatalog, прежде чем он вступит в силу. Используйте код ниже, чтобы импортировать модуль из относительного пути (для развертывания). Не волнуйтесь, конечный пользователь не нуждается в каких-либо дополнениях к своей переменной PYTHONPATH, это будет работать!

Вариант 2.
Добавьте дополнительную строку в код ниже, чтобы добавить жестко запрограммированный путь, например: sys.path.append (r "c: \ temp \ test \ scripts").
Когда вы будете готовы к развертыванию, у вас есть посторонний каталог, но это не имеет значения, все должно работать на компьютере конечного пользователя, потому что первый путь, который вы добавили, был относительный каталог (наша цель состояла в том, чтобы просто пройти диалог сбоя).

Код, общий для обоих вариантов:

import os
import sys

tbxPath = __file__.split("#")[0]
srcDirName = os.path.basename(tbxPath).rstrip(".tbx").split("__")[0] + ".src" 
tbxParentDirPath =  os.path.dirname(tbxPath)
srcDirPath = os.path.join(tbxParentDirPath, srcDirName)

sys.path.append(srcDirPath)
# sys.path.append(r"c:\temp\test\scripts")  #option2

from esdlepy.metrics.validation.LandCoverProportions import ToolValidator

Обновить

Прощай, злой обрез и оклейка Я обновил пример кода, чтобы класс ToolValidator был импортирован из библиотеки. Я вырезал и вставил только один раз, когда параметры инструмента установлены впервые. Я храню этот фрагмент кода в строке документации импортируемого ToolValidator.

В этом примере имя исходного каталога основано на имени tbx. Этот подход позволяет избежать коллизий, если у вас есть две панели инструментов с разными исходными каталогами. Стандарт, используемый для именования исходных папок, следующий:
TOOLBOXNAME__anything.tbx -> TOOLBOXNAME.src

Почему «все»? Поскольку двоичные файлы не могут быть объединены в нашей DVCS, мы можем назначать инструменты отдельным лицам и не беспокоиться о потере изменений. Когда инструмент завершен, он обрезается и вставляется в мастер.

Мне также нужно было получить доступ к файлам в исходной папке, чтобы заполнить раскрывающийся список, используйте этот метод, чтобы получить путь к панели инструментов из импортированного модуля:

import __main__
tbxPath = __main__.__file__.split("#")[0]
Майкл Джексон
источник
Может ли быть так, что код ToolValidator устанавливает значение параметра по умолчанию? Проверьте значение параметра «Значение по умолчанию» в свойствах инструмента-скрипта.
blah238
Спасибо за предложение. Я проверил, и значение по умолчанию не установлено в наборе инструментов ... но я скопировал набор инструментов и переименовал все, и значение все еще сохранялось в обеих копиях. Поэтому я откажусь от своей идеи кеша и предположу, что она действительно может храниться в файле .tbx, что по-прежнему странное поведение.
MJ
2

Размещение импорта в верхней части модуля проверки, вне ToolValidatorкласса, кажется, работает нормально для меня - я на 10.0 SP2. Однако я ничего не делаю с импортированным модулем нигде, кроме как в updateParameters.

import os
import sys
scriptDir = os.path.join(os.path.dirname(__file__.split("#")[0]), "Scripts") 
sys.path.append(scriptDir)
from someModuleInScriptDir import someFunction

class ToolValidator:
    ...
blah238
источник
Я попытался импортировать за пределы класса ToolValidator, но в операторе импорта произойдет сбой. Использовали ли вы недавно открытый ArcCatalog до запуска каких-либо скриптов? Я полагаю, именно поэтому ESRI с трудом воспроизводит ошибку ... это происходит только во вновь открытом приложении до запуска любых сценариев.
MJ
Это работает для меня с недавно открытым ArcCatalog. Интересно, если это импорт класса против функции, которая является проблемой?
blah238
Спасибо, возможно, вы к чему-то относитесь ... Я смутно помню случай, когда это работало, когда я напрямую импортировал функцию, я проведу еще какое-то тестирование.
MJ
Очень странное поведение ... это будет работать, пока мне не удастся сломать его. Если его сломать, он будет постоянно выдавать ошибку. Использование PYTHONPATH на компьютере разработчика или добавление второго жестко закодированного пути, как описано выше, сделали свое дело.
MJ
0

Я смог переместить свою проверку в py-файл, импортировав его и вызвав его из существующей проверки инструмента TBX. Ключ вызывал импорт внутри конструктора. Если я вызвал его извне класса ToolValidator, импорт не удался. Вот что у меня было на вкладке проверки TBX.

import arcpy
import os
import sys

class ToolValidator(object):
   """Class for validating a tool's parameter values and controlling
   the behavior of the tool's dialog."""

def __init__(self):
   """Setup arcpy and the list of tool parameters."""
   self.scriptDir = os.path.dirname(__file__.split("#")[0])
   sys.path.append(self.scriptDir)
   import ExportParcelIntersected
   self.validator = ExportParcelIntersected.ToolValidator()
   self.params = self.validator.params

 def initializeParameters(self):
   """Refine the properties of a tool's parameters.  This method is
   called when the tool is opened."""
   self.validator.initializeParameters()
   return

 def updateParameters(self):
   """Modify the values and properties of parameters before internal
   validation is performed.  This method is called whenever a parameter
   has been changed."""
   self.validator.updateParameters()
   return

 def updateMessages(self):
   """Modify the messages created by internal validation for each tool
   parameter.  This method is called after internal validation."""
   self.validator.updateMessages()
   return

Моя логика проверки тогда жила в ExportParcelIntersected.ToolValidator (). Где это можно было бы поддерживать проще.

TurboGus
источник