Ausgeben von Meldungen in Skriptwerkzeugen
In Ihren Skripten müssen Sie ArcPy wie folgt importieren:
import arcpy
Wenn das Skript als Skriptwerkzeug ausgeführt wird, verfügt ArcPy über die Informationen zur Anwendung, in der es aufgerufen wurde, z. B. ArcMap oder ArcCatalog. Es verwendet daher dieselben Objekte wie die Anwendung und erstellt keine neuen Objekte. Alle in der Anwendung vorgenommenen Umgebungseinstellungen wie overwriteOutput und scratchWorkspace sind verfügbar. Dies hat einen großen Vorteil: Sie können Meldungen mit ArcPy schreiben, die dann automatisch im Verlaufsdialogfeld, in den Werkzeugergebnissen und im Python-Fenster angezeigt werden. Außerdem haben in diesem Fall auch alle Modelle und Skriptwerkzeuge, die Ihr Skriptwerkzeug aufrufen, Zugang zu den von Ihnen ausgegebenen Meldungen.
Vergleichen Sie dies mit einem eigenständigen Skript. Bei einem eigenständigen Skript wird Ihr Skript nicht in einer ArcGIS-Anwendung aufgerufen. Daher gibt es für die Anzeige der Meldungen kein Verlaufsdialogfeld, keine Ergebnisse und kein Python-Fenster. Falls Sie in Ihrem Skript ein anderes (eigenständiges) Skript aufrufen, ist das aufgerufene Skript (wenn es Geoverarbeitungswerkzeuge verwendet) vollständig separat und hat nichts mit dem von Ihnen erstellten zu tun, weshalb auch keine Meldungen gemeinsam verwendet werden. Einer der Hauptvorteile von Skriptwerkzeugen ist die gemeinsame Verwendung von Meldungen und Umgebungseinstellungen.
Es gibt vier ArcPy-Funktionen für das Schreiben von Meldungen:
- AddMessage("Meldung"): Für allgemeine Informationsmeldungen (Gewichtung = 0).
- AddWarning("Meldung"): Für Warnungen (Gewichtung = 1).
- AddError("Meldung"): Für Fehlermeldungen (Gewichtung = 2).
- AddIDMessage(MessageType, MessageID, AddArgument1, AddArgument2): Für Fehler- und Warnmeldungen (die Gewichtung wird durch das Argument MessageType vorgegeben). Bei einem Aufruf von AddIDMessage() wird eine kurze Meldung angezeigt. Die Meldungs-ID ist ein Link zu einer Erläuterung der Ursache des Problems mit Lösungsvorschlägen. Wenn Sie eine Fehlermeldung (entweder mit AddError() oder mit AddIDMessage()) hinzufügen, geschieht Folgendes:
- Die Skriptausführung wird fortgesetzt. Sie müssen die entsprechende Logik für die Fehlerverarbeitung hinzufügen und die Ausführung des Skriptes stoppen. So müssen Sie eventuell Zwischendateien und -cursor löschen.
- Nach der Rückkehr aus Ihrem Skript geht beim aufrufenden Skript oder Modell ein Systemfehler ein und die Ausführung wird gestoppt.
Beispiel für das Hinzufügen von Meldungen
Im folgenden Beispiel wird eine Feature-Class-Liste aus einem Workspace in einen anderen kopiert. Bei Workspaces verschiedenen Typs erfolgt eine automatische Konvertierung, z. B. von einer Geodatabase in einen Ordner. Bei der Fehlerbehandlung werden Probleme abgefangen und Meldungen zurückgegeben. Andernfalls werden während der Ausführung Informationsmeldungen zurückgegeben, dass die Ausführung erfolgreich verläuft. Das folgende Beispiel nutzt die Direktive from <module> import. In diesem Fall handelt es sich um das Modul ScriptUtils mit dem unten stehenden Code. In diesem Code werden auch try/except-Blöcke verwendet.
Weitere Informationen zu try/except-Blöcken
# ConvertFeatures.py # Converts feature classes by copying them from one workspace to another # # Import the ScriptUtils utility module which, in turn, imports the # standard library modules and imports arcpy # from ScriptUtils import * # Get the list of feature classes to be copied to the output workspace # in_feature_classes = arcpy.GetParameterAsText(0) # Establish an array of input feature classes using the ScriptUtils routine. # in_feature_classes = SplitMulti(in_feature_classes) # Get the output workspace # out_folder = arcpy.GetParameterAsText(1) # Loop through the array copying each feature class to the output workspace # for in_feature_class in in_feature_classes: try: # Create the output name # feature_class_name = arcpy.ValidateTableName(in_feature_class, out_folder) # Add an output message # arcpy.AddMessage("Converting: " + in_feature_class + " To " +\ feature_class_name + ".shp") # Copy the feature class to the output workspace using the ScriptUtils routine # CopyFeatures(in_feature_class, out_folder + os.sep + \ feature_class_name) # If successful, add another message # arcpy.AddMessage("Successfully converted: " + in_feature_class + \ " To " + out_folder) except StandardError, ErrDesc: arcpy.AddWarning("Failed to convert: " + in_feature_class) arcpy.AddWarning(ErrDesc) except: arcpy.AddWarning("Failed to convert: " + in_feature_class) if not arcpy.GetMessages(2) == "": arcpy.AddError(arcpy.GetMessages(2))
Im Folgenden finden Sie das oben verwendete Modul ScriptUtils.
# ScriptUtils.py # Import required modules # import arcpy import sys import string import os def SplitMulti(multi_input): try: # Split the multivalue on the semi-colon delimiter # multi_as_list = string.split(multi_input, ";") return multi_as_list except: ErrDesc = "Error: Failed in parsing the inputs." raise StandardError, ErrDesc def CopyFeatures(in_table, out_table): try: ErrDesc = "CopyFeatures failed" # Copy each feature class to the output workspace # arcpy.CopyFeatures_management(in_table, out_table) except: if arcpy.GetMessages(2) != "": ErrDesc = arcpy.GetMessages(2) raise StandardError, ErrDesc
Hinweise zum obigen Skript
Funktionen, Module und Objekte können aus anderen Python-Skripten importiert werden, um Skripte vereinfachen und Code zentral verwalten zu können. Mit der import-Anweisung kann der gesamte Inhalt eines anderen Skriptes oder die gewünschten Elemente kopiert werden.
Ausnahmen sind Ereignisse, mit denen der Ablauf eines Programms geändert werden können. Diese können in einem Skript mit try- und raise-Anweisungen ausgelöst bzw. abgefangen werden. StandardError ist eine integrierte Ausnahme, auf der die meisten Ausnahmetypen in Python beruhen. Diese Ausnahme wird aufgerufen, wenn ein Werkzeugfehler auftritt, wobei die ErrDesc-Variable auf die Fehlermeldung des Werkzeuges festgelegt wird.
Weitere Informationen zur Fehlerbehandlung in Python
Zeichenfolgen, die im string-Modul definiert sind, bilden einen integrierten Typ zum Speichern und Darstellen von Text. Zum Bearbeiten von Zeichenfolgen werden mehrere Operationen unterstützt, z. B. Verbinden, Aufteilen und Indizieren.
Das os-Modul (Betriebssystem) bietet eine allgemeine Schnittstelle für die grundlegenden Werkzeuge des Betriebssystems.
Rückgabe aller Meldungen aus einem Werkzeug
Gelegentlich ist es sinnvoll, alle Meldungen eines aufgerufenen Werkzeuges unabhängig von der Gewichtung abzurufen. Die Funktion AddReturnMessage gibt eine durch einen Indexparameter angegebene Meldung aus dem Meldungs-Array von ArcPy zurück. Im folgenden Beispiel wird veranschaulicht, wie sämtliche Meldungen eines Werkzeuges zurückgegeben werden:
arcpy.Clip_analysis("roads","urban_area","urban_roads") # Return the resulting messages as script tool output messages # x = 0 while x < arcpy.MessageCount: arcpy.AddReturnMessage(x) x = x + 1
Meldungen in Skripten mit zwei Verwendungszwecken
Sie können Ihr Skript so entwerfen, dass es zwei Verwendungszwecke erfüllt: Verwendung als eigenständiges Skript im Betriebssystem oder Verwendung als Skriptwerkzeug. Dabei gilt es, drei Punkte zu bedenken:
- Wenn das Skript eigenständig ausgeführt wird, können Meldungen nicht angezeigt werden (da es keine Anwendung wie etwa ArcMap für die Anzeige der Meldungen gibt). Sie können stattdessen eine print-Anweisung verwenden, sodass die Meldungen im Befehlszeilenfenster angezeigt werden (Standardausgabe).
- Bei Ausführung des Skriptes als Werkzeug werden die Meldungen im Verlaufsdialogfeld, im Fenster Ergebnisse und im Python-Fenster angezeigt (wenn das Werkzeug im Python-Fenster ausgeführt wird). Hier muss keine Print-Anweisung verwendet werden. Falls Sie ausschließlich mit Print-Anweisungen arbeiten, müssen Sie das Kontrollkästchen Beim Ausführen eines Skripts Befehlsfenster anzeigen auf der Registerkarte Quelle in den Werkzeugeigenschaften aktivieren. Die Anzeige des Befehlsfensters während der Skriptausführung ist jedoch nicht empfehlenswert. Es sieht nicht schön aus und wird häufig ungewollt ausgeblendet.
- Bei Ausführung als eigenständiges Skript sollten Sie eine Ausnahme auslösen, die vom aufrufenden Programm abgefangen werden kann. Bei Ausführung als Skriptwerkzeug lösen Sie die Ausnahme mit der Funktion AddError() aus.
In der Regel schreiben Sie eine Routine für die Fehlermeldung, die Meldungen in die Standardausgabe (mit einer Print-Anweisung) und in ArcPy (mit den Funktionen AddMessage, AddWarning und AddError) schreibt. Im Folgenden finden Sie eine solche Routine aus dem Werkzeug Mehrere Pufferringe, einem Skriptwerkzeug in der Toolbox "Analysis". Sie können das Skript Multiple Ring Buffer anzeigen, indem Sie in der Toolbox "Analysis" nach dem Werkzeug suchen, mit der rechten Maustaste auf das Werkzeug klicken und dann Bearbeiten auswählen.
def AddMsgAndPrint(msg, severity=0): # Adds a Message (in case this is run as a tool) # and also prints the message to the screen (standard output) # print msg # Split the message on \n first, so that if it's multiple lines, # a GPMessage will be added for each line try: for string in msg.split('\n'): # Add appropriate geoprocessing message # if severity == 0: arcpy.AddMessage(string) elif severity == 1: arcpy.AddWarning(string) elif severity == 2: arcpy.AddError(string) except: pass
Steuern des Verlaufsdialogfeldes
Da Skriptwerkzeuge die Anwendung gemeinsam verwenden, müssen Sie das Verlaufsdialogfeld steuern. Sie können das Aussehen des Dialogfeldes vorgeben, indem Sie wie unten abgebildet entweder die standardmäßige Verlaufsanzeige oder die schrittweise Verlaufsanzeige wählen.
Zum Steuern des Verlaufsdialogfeldes und der Verlaufsanzeige stehen vier Funktionen zur Verfügung.
Funktion |
Beschreibung |
---|---|
Legt die Art der Verlaufsanzeige (standardmäßig oder schrittweise), die Beschriftung, den Mindest- und den Maximalwert sowie bei der schrittweisen Verlaufsanzeige das Intervall fest. |
|
Setzt die Verlaufsanzeige zurück. |
|
Verschiebt die Verlaufsanzeige um einen Schritt. |
|
Ändert die Beschriftung der Verlaufsanzeige. |
Der folgende Code zeigt die vollständige Verwendung der standardmäßigen und der schrittweisen Verlaufsanzeige. Kopieren Sie den Code in Ihren Python-Editor, speichern Sie ihn, und erstellen Sie ein Skriptwerkzeug für den Code. Das Skriptwerkzeug verfügt, wie in den Code-Kommentaren beschrieben, über zwei "Long"-Eingabeparameter. Führen Sie dann das Skriptwerkzeug aus, und stellen Sie dabei verschiedene Werte für die Parameter bereit. (Beginnen Sie mit n = 10 und p = 1, und versuchen Sie es dann mit n = 101 und p = 3.)
# Demonstration script showing examples of using the progressor # Parameters: # n - number to count to (a good first choice is 10) # p - interval to count by (a good first choice is 1) # The various time.sleep() calls are just to slow the dialog down # so you can view messages and progressor labels. # import arcpy import time n = int(arcpy.GetParameterAsText(0)) p = int(arcpy.GetParameterAsText(1)) readTime = 2.5 # Pause to read what's written on dialog loopTime = 0.3 # Loop iteration delay arcpy.AddMessage("Running demo with: " + str(n) + " by " + str(p)) # Start by showing the default progress dialog, where the # progress bar goes back and forth. Note how the progress label # mimics working through some "phases", or chunks of work that # a script may perform. # arcpy.SetProgressor("default", "This is the default progressor") time.sleep(readTime) for i in range(3): arcpy.SetProgressorLabel("Working on \"phase\" " + str(i + 1)) arcpy.AddMessage("Messages for phase " + str(i+1)) time.sleep(readTime) arcpy.AddMessage("-------------------------") # Setup the progressor with its initial label, min, max, and interval # arcpy.SetProgressor("step", "Step progressor: Counting from 0 to " + str(n), 0, n, p) time.sleep(readTime) # Loop issuing a new label when the increment is divisible by the # value of countBy (p). The "%" is python's modulus operator - we # only update the position every p'th iteration # for i in range(n): if (i % p) == 0: arcpy.SetProgressorLabel("Iteration: " + str(i)) arcpy.SetProgressorPosition(i) time.sleep(loopTime) # Update the remainder that may be left over due to modulus operation # arcpy.SetProgressorLabel("Iteration: " + str(i+1)) arcpy.SetProgressorPosition(i+1) arcpy.AddMessage("Done counting up") arcpy.AddMessage("-------------------------") time.sleep(readTime) # Just for fun, make the progressor go backwards. # arcpy.SetProgressor("default", "Default progressor: Now we'll do a countdown") time.sleep(readTime) arcpy.AddMessage("Here comes the countdown...") arcpy.SetProgressor("step", "Step progressor: Counting backwards from " + str(n), 0, n, p) time.sleep(readTime) arcpy.AddMessage("Counting down now...") for i in range(n, 0, -1): if (i % p) == 0: arcpy.SetProgressorLabel("Iteration: " + str(i)) arcpy.SetProgressorPosition(i) time.sleep(loopTime) # Update for remainder # arcpy.SetProgressorLabel("Iteration: " + str(i-1)) arcpy.SetProgressorPosition(i-1) time.sleep(readTime) arcpy.AddMessage("-------------------------") arcpy.AddMessage("All done") arcpy.ResetProgressor()
Wahl eines guten Intervalls bei einem potenziell großen Maximalwert
Bei vielen Skripten wissen Sie zunächst nicht, wie oft die Ausführung wiederholt wird. Beispiel: Ihr Skript läuft mit SearchCursor über die Zeilen in einer Tabelle. Die Anzahl der Zeilen ist Ihnen zunächst nicht bekannt. Ihr Skript kann für Tabellen beliebiger Größe verwendet werden, unabhängig davon, ob eine Tabelle nur einige Tausend oder Millionen von Zeilen aufweist. Wenn die schrittweise Verlaufsanzeige bei großen Tabellen um jeweils eine Zeile voranschreitet, kann dies zu einem Engpass bei der Performance führen, gegen den Sie vorbeugen sollten.
Um sich der möglichen Performance-Probleme bei der schrittweisen Verlaufsanzeige bewusst zu werden, kopieren Sie den folgenden Code in Ihren Python-Editor, speichern ihn und erstellen dann ein Skriptwerkzeug für den Code. Das Werkzeug hat zwei Eingaben: einen Tabellenparameter und einen Feldparameter. Führen Sie das Skriptwerkzeug mit verschiedenen Tabellengrößen aus. Verwenden Sie dabei eine Tabelle oder Feature-Class mit mindestens 10.000 Zeilen, damit die Performance-Unterschiede deutlich werden. (Sie können das Werkzeug auch innerhalb und außerhalb des Prozesses ausführen, um die bessere Performance bei der Ausführung im Prozess zu überprüfen.)
Das Skript durchläuft drei getrennte Schleifen und bei jeder Schleife werden alle Zeilen in der Tabelle abgerufen. Die schrittweise Verlaufsanzeige wird bei den einzelnen Schleifen unterschiedlich aktualisiert. Bei der ersten und zweiten Schleife wird die Verlaufsanzeige in großen Schritten aktualisiert, bei der letzten mit jeder Zeile. Wenn Sie das Werkzeug ausführen, werden Sie feststellen, dass die letzte Schleife länger dauert.
Sie können die in diesem Code verwendeten Vorgehensweisen auch für Ihre Skriptwerkzeuge nutzen.
# Demonstrates a step progressor by looping through records # on a table. Use a table with 10,000 or so rows - smaller tables # just whiz by. # 1 = table name # 2 = field on the table import arcpy try: inTable = arcpy.GetParameterAsText(0) inField = arcpy.GetParameterAsText(1) # Determine n, number of records on the table # arcpy.AddMessage("Getting row count") n = arcpy.GetCount_management(inTable) if n == 0: raise "no records" arcpy.AddMessage("Number of rows = " + str(n)) arcpy.AddMessage("") arcpy.AddMessage("---------------------------------") # Method 1: Calculate and use a suitable base 10 increment # =================================== import math p = int(math.log10(n)) if not p: p = 1 increment = int(math.pow(10, p-1)) arcpy.SetProgressor("step", "Incrementing by " + str(increment) + " on " + \ inTable, 0, n, increment) rows = arcpy.SearchCursor(inTable) i = 0 beginTime = time.clock() for row in rows: if (i % increment) == 0: arcpy.SetProgressorPosition(i) fieldValue = row.getValue(inField) i = i + 1 arcpy.SetProgressorPosition(i) arcpy.AddMessage("Method 1") arcpy.AddMessage("Increment = " + str(increment)) arcpy.AddMessage("Elapsed time: " + str(time.clock() - beginTime)) arcpy.AddMessage("---------------------------------") del rows del row # Method 2: let's just move in 10 percent increments # =================================== increment = int(n/10.0) arcpy.SetProgressor("step", "Incrementing by " + str(increment) + " on " + inTable, \ 0, n, increment) rows = arcpy.SearchCursor(inTable) i = 0 beginTime = time.clock() for row in rows: if (i % increment) == 0: arcpy.SetProgressorPosition(i) fieldValue = row.getValue(inField) i = i + 1 arcpy.SetProgressorPosition(i) arcpy.AddMessage("Method 2") arcpy.AddMessage("Increment = " + str(increment)) arcpy.AddMessage("Elapsed time: " + str(time.clock() - beginTime)) arcpy.AddMessage("---------------------------------") del rows del row # Method 3: use increment of 1 # =================================== increment = 1 arcpy.SetProgressor("step", "Incrementing by 1 on " + inTable, 0, n, increment) rows = arcpy.SearchCursor(inTable) beginTime = time.clock() while row: arcpy.SetProgressorPosition() fieldValue = row.getValue(inField) arcpy.SetProgressorPosition(n) arcpy.ResetProgressor() arcpy.AddMessage("Method 3") arcpy.AddMessage("Increment = " + str(increment)) arcpy.AddMessage("Elasped time: " + str(time.clock() - beginTime)) arcpy.AddMessage("---------------------------------") arcpy.AddMessage("") arcpy.AddMessage("Pausing for a moment to allow viewing...") time.sleep(2.0) # Allow viewing of the finished progressor del rows del row except "no records": arcpy.AddWarning(inTable + " has no records to count") except: if rows: del rows if row: del row arcpy.AddError("Exception occurred")