Как программно получить путь к «Python.exe», используемый ArcMap

16

Я работаю с надстройкой ArcMap в C #. Из кода C # я выполнил несколько скриптов Python. Теперь, чтобы запустить этот скрипт, у меня есть жестко запрограммированный путь к Python. Но это не портативно. Итак, я хочу получить путь к исполняемому файлу Python из кода и использовать его.

Вопрос:

Как я могу получить путь к исполняемому файлу Python, используемому ArcMap, из кода C #?

РЕДАКТИРОВАТЬ :

Из ваших предложений, сейчас я использую "путь среды", чтобы получить путь 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;
        }
    }
}

Но существует проблема:

Когда на моей машине установлена ​​другая версия python, нет никакой гарантии, что «python.exe», который я использую, ArcGIS также использует.

Я не ценю использование другого инструмента для получения пути "python.exe" . Итак, я действительно думаю, есть ли способ получить путь из раздела реестра. Для «ArcGIS10.0» реестр выглядит так: введите описание изображения здесь

И для этого я думаю о следующем пути, чтобы получить путь:

//get python path from registry key
string GetPythonPath()
{
    const string regKey = "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 ;
}

Но прежде чем использовать вторую процедуру, я должен быть уверен в своих догадках. Предположения:

  1. «True», связанный с python, означает, что python установлен с ArcGIS
  2. Ключ реестра ArcGIS 10.0 и более поздней версии будет записан в одном процессе.

Пожалуйста, помогите мне получить какие-либо разъяснения о моих догадках.

Emi
источник
5
Рассматривали ли вы создание инструмента-скрипта и запуск его из ArcObjects ?
blah238
3
Разве вы не можете просто установить переменную среды PATH в ArcGIS Python exe, которая будет требованием для установки вашего дополнения?
Чед Купер
Все говорят, мысль @ ChadCooper должна быть лучшим способом. Вместо того, чтобы пытаться работать в обратном направлении, просто установите его раз и навсегда во время установки.
elrobis
@elrobis: Я знаю, установка пути в среде PATH - хороший способ. Но я хотел узнать, есть ли способ найти питона и сделать все, не прерывая пользователя.
Эми
@ blah238 спасибо за ваше предложение. Я никогда не работал со скриптом. Может быть, мне нужно узнать об этом
Эми

Ответы:

2

Я взял ваш второй пример кода, заставил его работать как на 64, так и на 32-битных ОС и немного упростил его. У меня работает в версии 10.1 на 64-битной Windows 7, но, очевидно, вы должны протестировать его в максимально возможном количестве сред и добавить обратно в любые проверки защитного программирования, которые вы считаете необходимыми.

После тестирования чистой установки ArcGIS Desktop 10.1 без Python я обнаружил, что он не включает подраздел Python10.x, не говоря уже о значении True / False "Python" (все еще не уверен, для чего оно, возможно, обратитесь в службу поддержки ESRI, если необходимо знаю).

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;
}

На компьютере с Desktop 10.1 с Python это возвращается C:\Python27\ArcGIS10.1\python.exe. На компьютере Desktop 10.1 без Python это вызывает исключение InvalidOperationException из-за отсутствия ключа Python10.x.

Надеюсь, это поможет вам с тем, что вы пытаетесь достичь на самом деле, что - удивительно - до сих пор не ясно для меня.

blah238
источник
7

Вместо того, чтобы искать исполняемый файл Python, этот раздел справки предлагает обстреливать cmd.exeи запускать python.exeбез указания его местоположения. Однако обратите внимание, что это должно работать, потому что установщик ArcGIS Desktop устанавливает (редактировать: недавно протестировано в 10.1, это не так) зависит от пути к python.exeдобавлению в PATHпеременную окружения пользователя .

Другой подход - создать инструмент-скрипт и запустить его из ArcObjects .

Если вы действительно python.exeследуете пути к версии ArcGIS , с помощью расширения инструмента-скрипта ArcObjects + вы можете создать инструмент-скрипт Python, единственным выходным значением которого является значение sys.exec_prefix. Это путь к папке, содержащей версию Python ArcGIS, например C:\Python27\ArcGIS10.1.

Примечание : sys.executableвозвращает путь к ArcMap.exeи НЕ python.exeпри запуске в процессе, поэтому я не предлагаю использовать эту переменную.

Вызвать инструмент-скрипт из ArcObjects и получить выходные данные возвращаемого IGeoProcessorResultобъекта.

Обновление: вот пример проекта надстройки ArcMap (VS2010, .NET 3.5), в котором используется инструмент-скрипт, упакованный в надстройке, который просто отображает путь к python.exeиспользуемому ArcMap: http://wfurl.com/cbd5091

Это просто кнопка, которую вы нажимаете, и появляется окно сообщения с путем:

кнопка Окно сообщения

Интересные фрагменты кода:

  • Скрипт 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)
  • Функция 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();
    }
blah238
источник
2
Но в этом документе не говорится, что установщик ArcGIS Desktop устанавливает путь к python.exe для переменной окружения PATH пользователя. Таким образом, возможно, что путь к python отсутствует в переменной окружения PATH. Тогда это создаст ошибку. Итак, как я могу быть уверен, что путь к исполняемому файлу python находится в переменной окружения PATH пользователя.
Эми
2
Вы не можете, как и большинство вещей в жизни и вычислениях, все, что вы можете сделать, - это делать предположения и надеяться, что все будет работать, и иметь запасной план, если они этого не делают (предоставьте инструкции по добавлению его в переменную среды PATH). Тем не менее, если это обычная установка, я считаю, что установщик ArcGIS Desktop добавляет этот путь к переменной среды PATH.
blah238
2
Я видел много инсталляций, где установленный ArcGIS Python не был на пути. А что, если установлены две версии и «неправильная» находится на пути?
Blindjesse
В третьем абзаце я предложил решение, которое должно найти установку Python ArcGIS независимо от PATHпеременной среды.
blah238
@ blah238 не создает инструмент-скрипт, который делает мою надстройку менее переносимой или затрудняет процесс установки надстройки на другие компьютеры?
Эми
6

Будет ли у вас доступ к реестру?

При установке ArcMap он установит Python, если не сможет его найти. Он просматривает реестр, чтобы увидеть, установлен ли Python. Я считаю, что стандартное расположение реестра для этого: computer \ HKEY_LOCAL_MACHINE \ SOFTWARE \ PYTHON \ PythonCore \ 2.7 \ InstallPath со стандартным ключом расположения пути (2.7 - 10.1, 2.6 - 10.0)

Я не могу придумать причину, когда / почему значение этого ключа будет неправильным, но вы всегда можете пойти по этому пути: внутри куста Esri \ Desktop реестра находится расположение Python. Это простой путь, который вы можете получить, а затем создать дополнительные пути, чтобы обеспечить наличие Python.exe. Например, ключ на 64-битной машине устанавливается на: computer \ HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ ESRI \ Python10.1 с ключом PythonDir и соответствующим значением Path

Но мне нравится ответ @ blah238. Просто откройте подсказку из вашей программы и запустите ее там. Я не вижу причины, по которой это не сработает.

KHibma
источник
2
Этот подход также имеет недостатки, поскольку может быть несколько установок Python, и у вас нет простого способа программно определить, какой из них используется ArcGIS. Если вы не используете arcpy, это может не иметь значения. Тем не менее, я думаю, что наиболее надежное решение будет включать реестр и немного логики. Я не собираюсь идти туда, хотя.
blah238
Ну, логика должна начинаться с самой новой версии 2.7 и работать в обратном направлении. Это, конечно, может потерпеть неудачу, если вы установили новую версию Python, а затем пошли и установили более старую версию ArcGIS, которая установит старую версию Python. Так что да, я согласен, что есть этот потенциал, но он маловероятен (или вы можете просто создать поиск по arc10.1 = py2.7, arc10.0 = py26 ... и т. Д., Чтобы быть уверенным на 100%). Как я уже сказал, лучший способ, вероятно, просто раскошелиться на командную строку.
Хибма
@KHibma Я искал в реестре. Но я думаю, что это действительно утомительно, если я просматриваю "PYTHON" ключ. На моей машине установлены две версии Python, и она возвращает обе. Я думаю, это хорошая идея, чтобы просмотреть ключ "ESRI", и если есть подраздел "Python" с истинным значением, то я могу принять значение подраздела "PythonDir". Это работает в моем случае :)
Emi
Это неправильный способ поиска пути Python в реестре через ключ "ESRI"? Или есть шанс, что способ, которым esri использует для создания ключей и значений в реестре, может быть изменен, а код может не прочитать его
Emi
Когда я говорю «поиск» в реестре, я имею в виду использовать фактические пути, которые я указал выше. Если кто-то не знает лучше, расположение этих ключей реестра (в реестре) не будет меняться от компьютера к компьютеру. Таким образом, вы просто жестко закодировали пути, чтобы увидеть, существуют ли там ключи, и если да, то
какое
5

[Редактировать] Хотя setпрограммное выполнение (вычеркнуто ниже) сделало то, что я хотел, это можно сделать проще и с помощью более чистого кода с помощью Environment.GetEnvironmentVariables () .

Одним из вариантов будет сканирование каждой переменной среды в системе и попытка доказать следующее:

1) Является ли значение переменной среды каталогом? (и если это так..)

2) Содержит ли этот каталог python.exe?

Я смог сделать это программно, выполнив setкоманду через .Net Process API . Команда set, если она используется без параметра, возвращает ВСЕ переменные среды, используемые системой. Таким образом, я мог бы проанализировать, затем упорядочить результаты STDOUT, полученные из set, и просмотреть их, чтобы увидеть, на что в конечном итоге указывалось что-либо (и я имею в виду НИЧЕГО ), доступное через систему python.exe.

С этой страницы обсуждаем setкоманду:

Введите SET без параметров, чтобы отобразить все текущие переменные среды.

Чтобы проиллюстрировать это, я написал комбинацию методов (и вспомогательный класс ), которые делают то, что я обсуждал выше. Они могут быть оптимизированы, и они могут использовать некоторые пуленепробиваемые (Try..Catch и т. Д.), Но если на компьютере есть ЛЮБАЯ переменная среды, указывающая на python.exe, этот подход должен найти его! Меня не волнует , если переменная называется PATH, ABBRACADABBRAили любой .. если он указывает на python.exeэто должно найти его.

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

Вот termsмассив строк, которые вы передаете в подпрограмму для поиска в имени переменной среды или в ее nзначениях (т. PATHЕ. Может иметь несколько значений, но большинство других переменных будет иметь только одно). Убедитесь, что все строки в termsверхнем регистре!

(Когда я проверял это, я использовал просто «PYTHON», который был найден C:\Python27\python.exeв моей домашней системе. Но вы могли бы легко расширить его, добавив еще одну строку [] терминов, если вы хотите дополнительно проверить путь любых python.exeвозвращаемых кандидатов - для например, чтобы увидеть, были ли они в бункере ArcGIS и т. д.)

// 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;
}    

И в нижней части моего основного класса я включил следующий класс помощника :

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

    public string key;
    public string[] values;
}
elrobis
источник
1
Это хрупко, поскольку пользователь может настроить каталог установки Python в установщике ArcGIS Desktop. Также PYTHONPATHпеременная НЕ та, которую вы хотите.
blah238
@ blah238, иногда хрупкое - это все, что у тебя есть. Я был действительно удивлен, увидев, что Арк настроен против PYTHONPATH. Это установка по умолчанию 9.2. Тем не менее, ОП спросил, как программно добраться до ArcGIS python.exe, и подход, который я рекомендовал, хрупкий или нет, делает это.
elrobis
Не могу сказать, что понимаю отрицательное голосование, действительно ли этот ответ " бесполезен "? Это может быть не удивительно , но, безусловно, это вариант, который, вероятно, будет работать для типичной установки Arc, и, по крайней мере, он добавляет что-то полезное в поток - в частности, он иллюстрирует установку Arc по умолчанию с возможностью связать свой python. exe с переменной среды, отличной от PATH.
Elrobis
Извините, но вы не правы. Переменная PYTHONPATH используется Python для поиска модулей, а не ArcGIS для поиска Python. Проверьте ссылку.
blah238
@ blah238, я думаю, что скриншот был ошеломляющим / запутывающим момент, который я пытался подчеркнуть. (В частности, мое предложение оператору не было намерено подчеркнуть PYTHONPATH, что это единственная переменная в этой конкретной системе, указывающая на это python.exe.) В любом случае, я пересмотрел свой ответ, включив в него работающий пример кода C #, и я был бы признателен зная, если вы все еще не согласны с этим подходом. Спасибо / E.
elrobis
4

Я хотел бы предложить альтернативное решение, основываясь на моем комментарии в вопросе выше. Для текущего проекта я делаю нечто очень похожее; У меня есть надстройка .NET, которая, когда пользователь нажимает кнопку в пользовательском интерфейсе ArcMap, запускает скрипт Python. Я сделал требование иметь переменную среды PATH, установленную на исполняемый файл ArcGIS Python, таким образом, мне не нужно беспокоиться о включении пути к файлу Python в мой код .NET.

Сейчас, в процессе разработки, тестеры просто настраивают переменную PATH вручную. Но со временем у меня будет создан установщик Windows (exe), который установит надстройку, установит все зависимости Python и установит все необходимые переменные PATH. Для этого я использую Nullsoft Scriptable Install System (NSIS) , систему с открытым исходным кодом для создания установщиков Windows. Вот некоторый код, который я разработал до сих пор, который довольно грубый. По сути, он просматривает реестр, чтобы увидеть, есть ли переменные PATH, представляющие интерес, и если нет, то добавляет их. Конечно, должен быть запущен от имени администратора.

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

Итак, опять же, это не находит путь к ArcGIS Python exe, но позволяет вам дать конечному пользователю возможность установить его правильно и легко.

Чед Купер
источник
+1 Я полностью согласен с этой рекомендацией - где на высоком уровне Чад говорит: «Не работайте над проблемой назад, чтобы вывести экземпляр Python Arc, вместо этого используйте установщик и оставьте его SysAdmin для установки правильного Python. экземпляр." @ChadCooper, предоставляет ли NSIS какой-либо элемент управления пользовательским интерфейсом, чтобы вы могли переопределить эти пути по умолчанию, если хотите? Я не вижу того, что подразумевается под кодом, но держу пари, что он есть.
elrobis
@elrobis - Могу поспорить, что вы можете переопределить / отредактировать / изменить существующие - NSIS очень настраиваемый и может позволить вам создать довольно удобный установщик - вам просто нужно найти код, чтобы написать его.
Чед Купер
Создание установщика для надстройки кажется немного сумасшедшим. Кроме того, какие изменения вам нужно было бы сделать для поддержки 10.1, 10.2 и т. Д., А также 10.0?
blah238
@ blah238 - да, это кажется сумасшедшим, но мой инсталлятор для этого конкретного проекта будет делать гораздо больше, о чем говорили. Моя надстройка строго для 10.0. Я предполагаю, что для разных версий ArcGIS вы можете проверить реестр, чтобы увидеть, какая версия установлена, а затем действовать соответственно.
Чед Купер