ArcGIS Python Tool - Importieren eines benutzerdefinierten Skripts in die ToolValidator-Klasse

9

Ich hatte letzte Woche eine Frage zum Anpassen einer ToolValidator-Klasse gestellt und einige sehr gute Antworten erhalten. Bei der Arbeit mit den vorgeschlagenen Lösungen habe ich ein benutzerdefiniertes Modul erstellt, das Abfragen für eine Datenbank ausführt und sowohl von der ToolValidator-Klasse (um Werte für die Dropdown-Listen bereitzustellen) als auch später im Geoverarbeitungsskript (um andere zu erhalten) aufgerufen wird Parameter basierend auf Elementen, die in Dropdown-Listen ausgewählt wurden). Ich kann das benutzerdefinierte Modul in der ToolValidator-Klasse jedoch nicht aufrufen. Ich habe versucht, ohne Glück an den Pfad anzuhängen. Wenn ich versuche, diese Änderungen auf das Skript anzuwenden, wird ein Laufzeitfehler angezeigt: [Errno 9] Ungültiger Dateideskriptor. Wenn ich die Importzeile auskommentiere, keine Fehler.

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

Viele von Ihnen fragen sich vielleicht, warum ich nicht einfach ein benutzerdefiniertes Tool mit ArcObjects implementiere. Der Grund dafür ist, dass meine Endbenutzer nicht über die erforderlichen Berechtigungen verfügen, um DLLs auf ihrem Computer zu registrieren.

UPDATE: Dies ist mir in ArcGIS 10 passiert. Interessanterweise habe ich zunächst den Pfad innerhalb der Funktion initialiazeParameters der ToolValidator-Klasse angehängt. Wenn ich das Anhängen außerhalb (dh über) der ToolValidator-Klasse durchführe, funktioniert alles wie erwartet.

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

UPDATE 2: Ich glaube, ich habe die wahre Ursache meines Problems gefunden. In den Codefragmenten in diesem Beitrag habe ich scheinbar echte Pfade (z. B. C: / Working / SomeFolder) an den sys.path angehängt. In meiner eigentlichen ToolValidator-Klasse habe ich einen relativen Pfad mit os.path.dirname(__file__)+ "\ my_special_folder ..." erstellt. Ich hatte erwartet, dass os.path.dirname(__file__)dies den Pfad der Toolbox zurückgeben würde, da dieser die ToolValidator-Klasse enthält. Ich habe festgestellt, dass dies nicht der Fall ist. Soweit ich das beurteilen kann, wird die ToolValidator-Klasse niemals in eine .py-Datei geschrieben, und ich spekuliere, dass dieser Code an den Python-Interpreter im Speicher übergeben wird, also __file__nutzlos ist oder ein temporäres Skript beibehalten und dann ausgeführt wird ( path_to_script) wird aufgerufen und erneut gerendert__file__nutzlos. Ich bin sicher, es gibt noch andere Gründe, die mir fehlen.

Kurz gesagt, wenn ich einen fest codierten Pfad verwende, funktioniert sys.append überall, relative Pfade funktionieren in der ToolValidator-Klasse nicht so gut.

user890
quelle
Ist das in 9.3 oder 10?
Jason Scheirer
Wir haben Probleme, dies hier bei Esri zu reproduzieren. Wenn wir die Ursache isolieren, können wir einen Fix für 10.0 SP3 zurückportieren. In der Zwischenzeit stecken Sie vermutlich beim ersteren und nicht beim letzteren Verwendungsmuster fest.
Jason Scheirer

Antworten:

3

So starte ich nach dem Starten von ArcGIS oder ArcCatalog zunächst ein Dummy-Tool ("Einmal ausführen"), das ein Dummy.py-Skript aufruft. Danach können Sie Python-Skripte mit sys.argv [0] in den Validator importieren. Dies zeigt auf den Ordner, in dem sich das erste Skript befand. Danach können Sie das benötigte Skript in die Validator-Klasse importieren.

Das Dummy.py-Skript, das vom Tool "Einmal ausführen" aufgerufen wird:

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

Entschuldigung, ich kann die Formatierung nicht richtig einstellen. Grüße, Maarten Tromp

Maarten Tromp
quelle
3

Endlich diesen schrecklichen Käfer geknackt! Wenn Sie beispielsweise versuchen, Änderungen zum Importieren eines relativen Moduls oder Pakets anzuwenden, wird möglicherweise der folgende Fehler angezeigt:

Geben Sie hier die Bildbeschreibung ein

Option 1: Fügen Sie
nur für Entwickler den vollständigen Pfad zum Modul zum PYTHONPATH hinzu . Sie müssen ArcMap / ArcCatalog neu starten, bevor es wirksam wird. Verwenden Sie den folgenden Code, um das Modul aus einem relativen Pfad zu importieren (für die Bereitstellung). Keine Sorge, der Endbenutzer benötigt keine Ergänzungen zu seiner PYTHONPATH-Variablen, es wird funktionieren!

Option 2:
Fügen Sie dem folgenden Code eine zusätzliche Zeile hinzu, um den fest codierten Pfad anzuhängen, z. B.: Sys.path.append (r "c: \ temp \ test \ scripts")
Wenn Sie zur Bereitstellung bereit sind, haben Sie eine Fremdverzeichnis, aber es spielt keine Rolle, alles sollte auf dem Computer des Endbenutzers funktionieren, da der erste Pfad, den Sie hinzugefügt haben, das relative Verzeichnis war (unser Ziel war es, nur den Fehlerdialog zu überwinden).

Code, der beiden Optionen gemeinsam ist:

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

Aktualisieren

Leb wohl, böses Ausschneiden und Einfügen! Ich habe das Codebeispiel aktualisiert, damit die ToolValidator-Klasse aus der Bibliothek importiert wird. Ich schneide und füge nur einmal ein, wenn die Werkzeugparameter zum ersten Mal eingestellt werden. Ich speichere diesen Code-Snippit in der Dokumentzeichenfolge des zu importierenden ToolValidator.

In diesem Beispiel basiert der Name des Quellverzeichnisses auf dem TBX-Namen. Dieser Ansatz vermeidet Kollisionen, wenn Sie zwei Toolboxen mit unterschiedlichen Quellverzeichnissen haben. Der Standard, den ich für die Benennung von
Quellordnern verwendet habe, lautet wie folgt: TOOLBOXNAME__anything.tbx -> TOOLBOXNAME.src

Warum das "__anything"? Da Binärdateien in unserem DVCS nicht zusammengeführt werden können, können wir Einzelpersonen Tools zuweisen, ohne Änderungen zu verlieren. Wenn das Werkzeug fertiggestellt ist, wird es ausgeschnitten und in den Master eingefügt.

Ich musste auch auf Dateien im Quellordner zugreifen, um ein Dropdown-Menü zu füllen. Verwenden Sie diese Methode , um den Pfad zur Toolbox aus Ihrem importierten Modul abzurufen :

import __main__
tbxPath = __main__.__file__.split("#")[0]
Michael Jackson
quelle
Könnte es sein, dass der ToolValidator-Code den Standardwert des Parameters festlegt? Überprüfen Sie die Einstellung 'Standardwert' des Parameters in den Eigenschaften des Skriptwerkzeugs.
blah238
Danke für den Vorschlag. Ich habe überprüft, und der Standardwert ist nicht in der Toolbox festgelegt ... aber ich habe die Toolbox kopiert und alles umbenannt, und der Wert blieb in beiden Kopien erhalten. Daher werde ich meine Cache-Idee aufgeben und vorschlagen, dass sie tatsächlich in der .tbx-Datei gespeichert wird, was immer noch ein seltsames Verhalten ist.
MJ
2

Das Einfügen der Importe an die Spitze des Validierungsmoduls außerhalb der ToolValidatorKlasse scheint für mich in Ordnung zu sein - ich bin auf 10.0 SP2. Ich mache jedoch nichts anderes mit dem importierten Modul als in 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
quelle
Ich habe versucht, außerhalb der ToolValidator-Klasse zu importieren, aber die Importanweisung schlug fehl. Haben Sie einen frisch geöffneten ArcCatalog verwendet, bevor Skripte ausgeführt wurden? Ich würde mir vorstellen, dass ESRI aus diesem Grund Schwierigkeiten hat, den Fehler zu reproduzieren. Er tritt nur in einer neu geöffneten Anwendung auf, bevor Skripte ausgeführt werden.
MJ
Es funktioniert bei mir mit einem neu geöffneten ArcCatalog. Ich frage mich, ob es eine Klasse gegen eine Funktion importiert, die das Problem ist.
blah238
Vielen Dank, vielleicht haben Sie etwas vor ... Ich erinnere mich vage an einen Fall, in dem es funktioniert hat, als ich eine Funktion direkt importiert habe. Ich werde weitere Tests durchführen.
MJ
Sehr seltsames Verhalten ... es würde funktionieren, bis ich es geschafft habe, es zu brechen. Nach dem Brechen würde es konsequent einen Fehler auslösen. Die Verwendung von PYTHONPATH auf dem Entwicklercomputer oder das Anhängen eines zweiten fest codierten Pfads, wie oben beschrieben, hat den Trick ausgeführt.
MJ
0

Ich konnte meine Validierung in eine py-Datei verschieben, indem ich sie importierte und aus der vorhandenen TBX-Toolvalidierung heraus aufrief. Der Schlüssel rief den Import im Konstruktor auf. Wenn ich es von außerhalb der ToolValidator-Klasse aufgerufen habe, ist der Import fehlgeschlagen. Hier ist, was ich auf der Registerkarte Validierung der TBX hatte.

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

Meine Validierungslogik lebte dann in ExportParcelIntersected.ToolValidator (). Wo es einfacher gewartet werden könnte.

TurboGus
quelle