ArcGIS Python Tool - Importazione di script personalizzati nella classe ToolValidator


9

La settimana scorsa avevo pubblicato una domanda sulla personalizzazione di una classe ToolValidator e ho ottenuto delle ottime risposte. Nel lavorare con le soluzioni proposte, ho creato un modulo personalizzato che esegue query su un db e verrà chiamato sia dalla classe ToolValidator (per fornire i valori per gli elenchi a discesa) sia successivamente nello script di geoprocessing (per ottenere altri parametri basati sugli elementi selezionati negli elenchi a discesa). Tuttavia, non riesco a chiamare il modulo personalizzato nella classe ToolValidator. Ho provato ad aggiungere al percorso senza fortuna. Quando provo ad applicare queste modifiche allo script, ottengo un errore di runtime: [Errno 9] Descrittore di file errato. Se commento la riga di importazione, nessun errore.

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

Molti di voi potrebbero chiedersi perché non devo implementare uno strumento personalizzato con ArcObjects. Il motivo è che i miei utenti finali non hanno i privilegi necessari per registrare QUALSIASI DLL sul proprio computer.

AGGIORNAMENTO: Mi stava succedendo in ArcGIS 10. È interessante notare che inizialmente stavo aggiungendo il percorso all'interno della funzione initialiazeParameters della classe ToolValidator. Se faccio l'appendice all'esterno (cioè sopra) della classe ToolValidator, tutto funziona come previsto.

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()

AGGIORNAMENTO 2: Penso di aver trovato la vera causa del mio problema. Negli snippet di codice in questo post, ho aggiunto quelli che sembrano essere percorsi reali (ex C: / Working / SomeFolder) al sys.path. Nella mia attuale classe ToolValidator, stavo costruendo un percorso relativo usando os.path.dirname(__file__)+ "\ my_special_folder ...". Stavo anticipando che os.path.dirname(__file__)avrebbe restituito il percorso della casella degli strumenti, poiché contiene la classe ToolValidator. Sono venuto a scoprire che non è così. Per quanto ne so, la classe ToolValidator non viene mai effettivamente scritta in un file .py e suppongo che il codice this sia passato all'interprete python in memoria, quindi __file__è inutile, oppure alcuni script temporanei sono persistenti e quindi eseguono ( path_to_script) viene chiamato, di nuovo rendering__file__inutili. Sono sicuro che ci sono anche altri motivi per cui mi manca.

Per farla breve, se uso un percorso hardcoded, sys.append funziona ovunque, i percorsi relativi non funzionano così bene nella classe ToolValidator.


È in 9.3 o 10?
Jason Scheirer,

Abbiamo problemi a riprodurlo qui su Esri, se isoliamo la causa possiamo eseguire il backport di una correzione per 10.0 SP3. Nel frattempo, suppongo che tu sia bloccato con il primo e non con il secondo modello di utilizzo.
Jason Scheirer,

Risposte:


3

Il modo in cui lo faccio è, dopo aver avviato ArcGIS o ArcCatalog, eseguire prima uno strumento fittizio ("Esegui una volta") chiamando uno script dummy.py. Dopo averlo fatto, puoi importare script Python nel validatore usando sys.argv [0]. Questo indicherà la cartella in cui si trovava il primo script. Successivamente è possibile importare lo script necessario in de Validator Class.

Lo script dummy.py chiamato dallo strumento "Esegui questa volta":

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')

Spiacenti, impossibile ottenere la formattazione corretta Saluti, Maarten Tromp


3

Finalmente risolto questo orribile bug! Ad esempio, quando si tenta di applicare le modifiche per importare un modulo o un pacchetto relativo, è possibile che venga visualizzato il seguente errore:

inserisci qui la descrizione dell'immagine

Opzione 1: solo
per lo sviluppatore, aggiungere il percorso completo al modulo in PYTHONPATH . È necessario riavviare ArcMap / ArcCatalog prima che abbia effetto. Utilizzare il codice seguente per importare il modulo da un percorso relativo (per la distribuzione). Non preoccuparti, l'utente finale non ha bisogno di aggiunte alla sua variabile PYTHONPATH, funzionerà!

Opzione 2:
aggiungi una riga aggiuntiva al codice seguente per aggiungere il percorso hardcoded, ad esempio: sys.path.append (r "c: \ temp \ test \ scripts")
Quando sei pronto per la distribuzione, hai un directory estranea, ma non importa, tutto dovrebbe funzionare sul computer dell'utilizzatore perché il primo percorso che hai aggiunto era la relativa directory (il nostro obiettivo era quello di superare la finestra di dialogo dell'errore).

Codice comune ad entrambe le opzioni:

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

Aggiornare

Addio male che taglia e incolla! Ho aggiornato l'esempio di codice in modo che la classe ToolValidator sia importata dalla libreria. Ho tagliato e incollato una sola volta quando i parametri dell'utensile sono stati impostati per la prima volta. Conservo questo frammento di codice nel docstring di ToolValidator che viene importato.

In questo esempio, il nome della directory di origine si basa sul nome tbx. Questo approccio evita le collisioni se si hanno due toolbox con directory di origine diverse. Lo standard che ho usato per la denominazione della cartella di origine è il seguente:
TOOLBOXNAME__anything.tbx -> TOOLBOXNAME.src

Perché il "__anything"? Poiché i file binari non possono essere uniti nel nostro DVCS, possiamo assegnare strumenti agli individui e non preoccuparci di perdere le modifiche. Quando lo strumento viene finalizzato, viene tagliato e incollato nel master.

Avevo anche bisogno di accedere ai file nella cartella di origine per popolare un menu a discesa, utilizzare questo metodo per ottenere il percorso della casella degli strumenti all'interno del modulo importato:

import __main__
tbxPath = __main__.__file__.split("#")[0]

Potrebbe essere che il codice ToolValidator stia impostando il valore predefinito del parametro? Controllare l'impostazione "Valore predefinito" del parametro nelle proprietà dello strumento di script.
blah238,

Grazie per il suggerimento Ho controllato e il valore predefinito non è impostato nella casella degli strumenti ... ma ho copiato la casella degli strumenti e rinominato tutto, e il valore persisteva ancora in entrambe le copie. Pertanto abbandonerò la mia idea di cache e suggerirò che potrebbe effettivamente essere memorizzata nel file .tbx, che è ancora un comportamento strano.
MJ,

2

Mettere le importazioni in cima al modulo di validazione, al di fuori della ToolValidatorclasse sembra funzionare bene per me - Sono su 10.0 SP2. Tuttavia non sto facendo nulla con il modulo importato da nessuna parte ma dentro 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:
    ...

Ho provato a importare al di fuori della classe ToolValidator ma non è riuscito sull'istruzione import. Stavi usando un ArcCatalog appena aperto, prima che fossero eseguiti gli script? Immagino che questo sia il motivo per cui ESRI ha difficoltà a riprodurre l'errore ... si verifica solo in un'applicazione appena aperta prima che vengano eseguiti gli script.
MJ,

Funziona per me con un ArcCatalog appena aperto. Mi chiedo se sta importando una classe vs una funzione che è il problema?
blah238,

Grazie, potresti essere a qualcosa ... Ricordo vagamente un caso in cui ha funzionato quando ho importato direttamente una funzione, farò qualche altro test.
MJ,

Comportamento molto strano ... avrebbe funzionato fino a quando non sarei riuscito a romperlo. Dopo averlo rotto, genererebbe costantemente un errore. L'uso di PYTHONPATH sulla macchina dello sviluppatore o l'aggiunta di un secondo percorso hard coded, come indicato sopra, ha funzionato.
MJ,

0

Sono stato in grado di spostare la mia convalida in un file PY importandolo e chiamandolo dall'interno della convalida dello strumento TBX esistente. La chiave stava chiamando l'importazione all'interno del costruttore. Se l'ho chiamato dall'esterno della classe ToolValidator l'importazione non è riuscita. Ecco cosa ho avuto nella scheda di convalida del 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

La mia logica di convalida ha quindi vissuto in ExportParcelIntersected.ToolValidator (). Dove potrebbe essere mantenuto più facilmente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.