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:

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:

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.

Standardmäßige und schrittweise Verlaufsanzeige

Zum Steuern des Verlaufsdialogfeldes und der Verlaufsanzeige stehen vier Funktionen zur Verfügung.

Funktion

Beschreibung

SetProgressor

Legt die Art der Verlaufsanzeige (standardmäßig oder schrittweise), die Beschriftung, den Mindest- und den Maximalwert sowie bei der schrittweisen Verlaufsanzeige das Intervall fest.

ResetProgressor

Setzt die Verlaufsanzeige zurück.

SetProgressorPosition

Verschiebt die Verlaufsanzeige um einen Schritt.

SetProgressorLabel

Ändert die Beschriftung der Verlaufsanzeige.

Funktionen der Verlaufsanzeige der Geoverarbeitung

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

7/10/2012