¿Cómo puedo obtener mediante programación la ruta de "Python.exe" que usa ArcMap?

15

Estoy trabajando con un complemento de ArcMap en C #. Desde el código C #, he ejecutado algunos scripts de Python. Ahora, para ejecutar esos scripts, tengo una ruta de Python codificada. Pero esto no es portátil. Por lo tanto, quiero obtener la ruta del ejecutable de Python desde el código y usarlo.

Pregunta :

¿Cómo puedo obtener la ruta del ejecutable de Python que usa ArcMap desde el código C #?

EDITAR:

Por tus sugerencias, por ahora estoy usando "entorno de ruta" para obtener la ruta de Python.

//get python path from environtment variable
string GetPythonPath()
{
    IDictionary environmentVariables = Environment.GetEnvironmentVariables();
    string pathVariable = environmentVariables["Path"] as string;
    if (pathVariable != null)
    {
        string[] allPaths = pathVariable.Split(';');
        foreach (var path in allPaths)
        {
            string pythonPathFromEnv = path + "\python.exe";
            if (File.Exists(pythonPathFromEnv))
                return pythonPathFromEnv;
        }
    }
}

Pero hay un problema:

Cuando se instala una versión diferente de python en mi máquina, no hay garantía de que, el "python.exe" que estoy usando, ArcGIS también use eso.

No aprecio el uso de otra herramienta para obtener la ruta "python.exe" . Entonces, realmente creo que hay alguna forma de obtener la ruta desde la clave de registro. Para "ArcGIS10.0" el registro se ve así:

Yparaeso,estoypensandoenlasiguientemaneradeobtenerelcamino:

//getpythonpathfromregistrykeystringGetPythonPath(){conststringregKey="Python";
    string pythonPath = null;
    try
    {
        RegistryKey registryKey = Registry.LocalMachine;
        RegistryKey subKey = registryKey.OpenSubKey("SOFTWARE");
        if (subKey == null)
            return null;

        RegistryKey esriKey = subKey.OpenSubKey("ESRI");
        if (esriKey == null)
            return null;

        string[] subkeyNames = esriKey.GetSubKeyNames();//get all keys under "ESRI" key
        int index = -1;
     /*"Python" key contains arcgis version no in its name. So, the key name may be 
     varied version to version. For ArcGIS10.0, key name is: "Python10.0". So, from
     here I can get ArcGIS version also*/
        for (int i = 0; i < subkeyNames.Length; i++)
        {
            if (subkeyNames[i].Contains("Python"))
            {
                index = i;
                break;
            }
        }
        if(index < 0)
            return null;
        RegistryKey pythonKey = esriKey.OpenSubKey(subkeyNames[index]);

        string arcgisVersion = subkeyNames[index].Remove(0, 6); //remove "python" and get the version
        var pythonValue = pythonKey.GetValue("Python") as string;
        if (pythonValue != "True")//I guessed the true value for python says python is installed with ArcGIS.
            return;

        var pythonDirectory = pythonKey.GetValue("PythonDir") as string;
        if (pythonDirectory != null && Directory.Exists(pythonDirectory))
        {
            string pythonPathFromReg = pythonDirectory + "ArcGIS" + arcgisVersion + "\python.exe";
            if (File.Exists(pythonPathFromReg))
                pythonPath = pythonPathFromReg;
        }  
    }
    catch (Exception e)
    {
        MessageBox.Show(e + "\r\nReading registry " + regKey.ToUpper());
        pythonPath = null;
    }
    return pythonPath ;
}

Pero antes de usar el segundo procedimiento, necesito estar seguro de mis suposiciones. Las suposiciones son:

  1. el "Verdadero" asociado con python significa que python se instala con ArcGIS
  2. ArcGIS 10.0 y la clave de registro de la versión superior se escribirán en el mismo proceso.

Por favor, ayúdame a obtener alguna aclaración sobre mis suposiciones.

    
pregunta Emi 20.12.2012 - 08:53

5 respuestas

2

Tomé su segundo ejemplo de código, lo hice funcionar en sistemas operativos de 64 y 32 bits, y lo simplifiqué un poco. Funciona para mí en 10.1 en Windows 7 de 64 bits, pero obviamente debería probarlo en tantos entornos como sea posible, y agregar de nuevo cualquier verificación defensiva de programación que considere necesaria.

Después de probar la instalación limpia de ArcGIS Desktop 10.1 sin Python, descubrí que no incluye la subclave Python10.x, y mucho menos el valor verdadero / falso de "Python" (aún no estoy seguro de qué se trata, quizás contacte al soporte de ESRI Si debes saber).

string GetPythonPath()
{
    string pythonPath = null;
    var localmachineKey = Registry.LocalMachine;
    // Check whether we are on a 64-bit OS by checking for the Wow6432Node key (32-bit version of the Software registry key)
    var softwareKey = localmachineKey.OpenSubKey(@"SOFTWARE\Wow6432Node"); // This is the correct key for 64-bit OS's
    if (softwareKey == null) {
        softwareKey = localmachineKey.OpenSubKey("SOFTWARE"); // This is the correct key for 32-bit OS's
    }
    var esriKey = softwareKey.OpenSubKey("ESRI");
    var realVersion = (string)esriKey.OpenSubKey("ArcGIS").GetValue("RealVersion"); // Get the "real", canonical version of ArcGIS
    var shortVersion = String.Join(".", realVersion.Split('.').Take(2).ToArray()); // Get just the Major.Minor part of the version number, e.g. 10.1
    var pythonKey = esriKey.OpenSubKey("Python" + shortVersion); // Open the Python10.x sub-key
    if (pythonKey == null) {
        throw new InvalidOperationException("Python not installed with ArcGIS!");
    }
    var pythonDirectory = (string)pythonKey.GetValue("PythonDir");
    if (Directory.Exists(pythonDirectory))
    {
        // Build path to python.exe
        string pythonPathFromReg = Path.Combine(Path.Combine(pythonDirectory, "ArcGIS" + shortVersion), "python.exe");
        if (File.Exists(pythonPathFromReg)) {
            pythonPath = pythonPathFromReg;
        }
    }
    return pythonPath;
}

En una máquina Desktop 10.1 con Python, esto devuelve C:\Python27\ArcGIS10.1\python.exe . En una máquina Desktop 10.1 sin Python, esto genera una InvalidOperationException debido a que la clave Python10.x no está presente.

Esperemos que esto te ayude con lo que estés tratando de lograr, lo cual es, sorprendentemente, todavía no está claro para mí.

    
respondido por el blah238 22.01.2013 - 08:11
7

En lugar de buscar el ejecutable de Python, este tema de ayuda sugiere descartar a cmd.exe y ejecutar python.exe sin calificar su ubicación. Sin embargo, tenga en cuenta que este debería funcionar porque el instalador de ArcGIS Desktop configura (edición: recientemente probado en 10.1, no lo hace) se basa en La ruta a python.exe se agrega a la variable de entorno PATH del usuario.

Otro enfoque es crear una herramienta de script y ejecútelo desde ArcObjects .

Si realmente busca la ruta a la versión de ArcGIS de python.exe , por extensión del enfoque de la herramienta de script ArcObjects +, puede crear una herramienta de script de Python cuyo único resultado sea el valor de sys.exec_prefix . Esta es la ruta de la carpeta que contiene la versión de ArcGIS de Python, por ejemplo. C:\Python27\ArcGIS10.1 .

Nota al margen : sys.executable devuelve la ruta a ArcMap.exe y NO a python.exe cuando se ejecuta en el proceso, por lo que no sugiero usar esa variable.

Llame a la herramienta de secuencia de comandos de ArcObjects y obtenga el resultado de IGeoProcessorResult objeto.

Actualización: Aquí se muestra un proyecto de complemento de ArcMap de ejemplo (VS2010, .NET 3.5) que utiliza una herramienta de script empaquetada dentro del complemento que simplemente muestra la ruta al python.exe utilizado por ArcMap: enlace

Es solo un botón en el que haces clic y aparece un mensaje con la ruta:

Los bits de código interesantes:

  • Script de Python:

    import sys
    import os
    import arcpy
    
    def getPythonPath():
        pydir = sys.exec_prefix
        pyexe = os.path.join(pydir, "python.exe")
        if os.path.exists(pyexe):
            return pyexe
        else:
            raise RuntimeError("No python.exe found in {0}".format(pydir))
    
    if __name__ == "__main__":
        pyexe = getPythonPath()
        arcpy.AddMessage("Python Path: {0}".format(pyexe))
        arcpy.SetParameterAsText(0, pyexe)
    
  • Función C #:

    public string GetPythonPath()
    {
        // Build the path to the PythonPathToolbox
        string toolboxPath = Path.Combine(Path.GetDirectoryName(this.GetType().Assembly.Location), "PythonPath.tbx");
    
        // Initialize the geoprocessor.
        IGeoProcessor2 gp = new ESRI.ArcGIS.Geoprocessing.GeoProcessorClass();
    
        // Add the PythonPath toolbox.
        gp.AddToolbox(toolboxPath);
    
        // Need an empty array even though we have no input parameters
        IVariantArray parameters = new VarArrayClass();
    
        // Execute the model tool by name.
        var result = gp.Execute("GetPythonPath", parameters, null);
        return result.GetOutput(0).GetAsText();
    }
    
respondido por el blah238 20.12.2012 - 09:15
6

¿Tendrá acceso al registro?

Al instalar ArcMap, instalará Python si no puede encontrarlo. Busca en el registro para ver si Python ya está instalado. Creo que la ubicación de registro estándar para esto es: computadora \ HKEY_LOCAL_MACHINE \ SOFTWARE \ PYTHON \ PythonCore \ 2.7 \ InstallPath Con una clave predeterminada de la ubicación de la ruta (2.7 siendo 10.1, 2.6 siendo 10.0)

No puedo pensar en una razón cuando / por qué el valor de esta clave sería incorrecto, pero siempre podría ir de esta manera: Dentro de la sección Esri \ Desktop del registro hay una ubicación de Python. Es la ruta simple que puede obtener y luego construir otras rutas para asegurarse de que haya un Python.exe. Por ejemplo, la clave en una máquina de 64 bits se instala para: computadora \ HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ ESRI \ Python10.1 Con una clave de PythonDir y un valor de ruta asociado

Pero me gusta la respuesta @ blah238. Simplemente abre un aviso desde tu programa y ejecútalo allí. No puedo ver una razón por la que esto no funcionaría.

    
respondido por el KHibma 20.12.2012 - 18:49
5

[Editar] Al ejecutar set programáticamente (tachado, a continuación) hizo Lo que quería, se puede lograr más fácilmente y con un código más limpio utilizando Environment.GetEnvironmentVariables () .

Una opción sería escanear cada Variable de entorno en el sistema e intentar probar lo siguiente:

1) ¿El valor de la variable de entorno es un directorio? (y si es así ...)

2) ¿Ese directorio contiene python.exe ?

Pude hacer esto mediante programación ejecutando el comando set a través de .Net API de proceso . El comando set , cuando se usa sin un parámetro, devuelve TODAS las variables de entorno que usa el sistema. Así que podría eliminar, luego organizar los resultados de STDOUT emitidos desde set , y evaluarlos para ver si hay algo (y me refiero a ANYTHING ) disponible a través del entorno del sistema que en última instancia apunta a python.exe .

De esta página que analiza el comando set :

  

Escriba SET sin parámetros para mostrar todo el entorno actual   variables.

Para ilustrar escribí una combinación de métodos (y una clase de ayuda ) que hace lo que discutí encima. Estos pueden optimizarse, y podrían usar algunas pruebas de bala (Try..Catch, etc.), pero si la computadora tiene CUALQUIER Variable de entorno que apunta a python.exe , ¡este enfoque debería encontrarlo! No me importa si la var se llama PATH , ABBRACADABBRA , o lo que sea ... si apunta a python.exe , esto debería encontrarlo.

// C#, you'll need these using statements:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;

Aquí terms es una matriz de cadenas que pasa a la rutina para buscar el nombre de la variable de entorno o sus valores n (es decir, PATH puede tener varios valores, pero la mayoría de las otras vars tener solo uno). Asegúrese de que todas las cadenas en terms sean MAYÚSCULAS!

(Cuando probé esto, usé simplemente "PYTHON", que encontró C:\Python27\python.exe en mi sistema doméstico. Pero podría extenderlo fácilmente para incluir otra cadena [] de términos si quisiera inspeccionar más la ruta de cualquiera python.exe candidatos devueltos --- por ejemplo, para ver si estaban en la bandeja de ArcGIS, etc.)

// Top-level method that organizes everything below..
private void scrapeEnvironmentVariables(string[] terms)
{
    // !! ValueObject !! This is a Helper Class, find it at the bottom..
    List<ValueObject> voList = buildListOfEnvironmentObjects();

    foreach (ValueObject vo in voList)
    {
        bool candidateFound = ObjectMatchesSearchTerms(vo, terms);

        if (candidateFound)
        {    
            string exeCandidate = "";
            foreach (string unlikelyPath in vo.values)
            {
                if (Directory.Exists(unlikelyPath))
                {
                    string unlikelyExe = unlikelyPath + "\python.exe";
                    if(File.Exists(unlikelyExe))
                        exeCandidate = unlikelyExe;
                }

                if (exeCandidate != "")
                {
                    break;
                    // At this point, exeCandidate is a fully-qualified
                    // path to python.exe..
                }
            }

            // If you only want the first hit, break here..
            // As-is, the code will look for even more matches.
            //if (breakOnFirstHit)
            //    break;
        }
    }
}


// Execute Environment.GetEnvironmentVariables() and organize the 
// key..value pairs into 1:n ValueObjects (see Helper Class below).
private List<ValueObject> buildListOfEnvironmentObjects()
{
    // Return a List of 1:n key..value objects.
    List<ValueObject> voList = new List<ValueObject>();

    IDictionary variableDictionary = Environment.GetEnvironmentVariables();
    foreach (DictionaryEntry entry in variableDictionary)
    {
        // Explode multi-values into a List of values (n).
        List<string> values = new List<string>();
        string[] rawValues = ((string)entry.Value).Split(';');
        foreach (string value in rawValues)
            if (value != "") values.Add(value.ToUpper());

        ValueObject valueObject = new ValueObject();
        valueObject.key = ((string)entry.Key).ToUpper();
        valueObject.values = values.ToArray();

        voList.Add(valueObject);
    }
    return voList;
}


// Compare the key and any value(s) in a ValueObject with all the
// terms submitted to the top-level method. If **ALL** the terms
// match (against any combination of key..value), it returns true.
private bool ObjectMatchesSearchTerms(ValueObject vo, string[] terms)
{
    int matchCount = 0;

    foreach (string term in terms)
    {
        if (vo.key.Contains(term))              // screen the key
            matchCount++;

        foreach (string value in vo.values)     // screen N values
        {
            if (value.Contains(term))
                matchCount++;
        }
    }

    // Test against >= because it's possible the match count could
    // exceed the terms length, like if a match occurred in both the
    // key and the value(s). So >= avoids omiting that possibility.
    return (matchCount >= terms.Length) ? true : false;
}    

Y al final de mi clase principal, incluí la siguiente Helper Class :

class ValueObject : Object
{
    public ValueObject() { } // default constructor

    public string key;
    public string[] values;
}
    
respondido por el elrobis 20.12.2012 - 18:05
4

Me gustaría ofrecer una solución alternativa, en base a mi comentario en la pregunta anterior. Para un proyecto actual, estoy haciendo algo muy similar; Tengo un complemento .NET que, cuando el usuario hace clic en un botón en la interfaz de usuario de ArcMap, se ejecuta una secuencia de comandos de Python. Hice que el requisito de una variable de entorno PATH configurada en el ejecutable de ArcGIS Python no me tiene que preocupar por incluir la ruta al exe de Python en mi código .NET.

Ahora, mientras están en desarrollo, los probadores están configurando la variable PATH manualmente. Pero finalmente tendré un instalador de Windows (exe) creado que instalará el complemento, instalará las dependencias de Python y establecerá las variables PATH necesarias. Para esto, estoy usando el Nullsoft Scriptable Install System (NSIS) , un sistema de código abierto para crear instaladores de Windows. Aquí está algo del código que he trabajado hasta ahora, que es bastante áspero. Básicamente, busca en el registro para ver si las variables PATH de interés están ahí, y si no lo están, las agrega. Debe ejecutarse como administrador, por supuesto.

include "StrFunc.nsh"
!include "LogicLib.nsh"

/*
  Name: VIESORE_Installer.nsi
  Author: Chad Cooper, CAST
  Date: 7/16/2012
  Purpose: NSIS installer script for .exe creation by NSIS. Installs VIESORE components and sets up environment.
*/

Name "VIESORE"
Caption "VIESORE Installer"
Outfile "VIESOREInstaller.exe"

RequestExecutionLevel admin

# Initialize functions
${StrLoc}
# Initialize user variables
Var path

Section "Set SYSTEM PATH environmental variables"
    ReadRegStr $0 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path"
    ${StrLoc} $1 $0 "C:\Python26\ArcGIS10.0" ">"
    ${StrLoc} $2 $0 "C:\Python26\ArcGIS10.0\Scripts" ">"
        ${StrLoc} $3 $0 "C:\Python26\ArcGIS10.0\Lib\site-packages" ">"
        ${StrLoc} $4 $0 "C:\Program Files\e-on software\Vue 10 Infinite\Application" ">"
        # Test to see if env vars exist in current system PATH, if not add them to $path variable
        ${If} $3 == ""
                StrCpy $path "C:\Python26\ArcGIS10.0\Lib\site-packages"
        ${EndIf}
        ${If} $2 == ""
                StrCpy $path "C:\Python26\ArcGIS10.0\Scripts;$path"
        ${EndIf}
        ${If} $1 == ""
                StrCpy $path "C:\Python26\ArcGIS10.0;$path"
        ${EndIf}
        ${If} $4 == ""
                StrCpy $path "C:\Program Files\e-on software\Vue 10 Infinite\Application;$path"
        ${EndIf}
        DetailPrint "$path written to system PATH"
    WriteRegStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path" "$0;$path"
    ReadRegStr $5 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path"
    DetailPrint "New Path: $5"
SectionEnd

Nuevamente, esto no encuentra la ruta al exe de ArcGIS Python, pero le permite darle a su usuario final la capacidad de configurarlo de manera adecuada y sencilla.

    
respondido por el Chad Cooper 24.12.2012 - 19:02

Lea otras preguntas en las etiquetas