如何以编程方式获取ArcMap使用的“ Python.exe”的路径


16

我正在使用C#中的ArcMap加载项。从C#代码中,我已经执行了一些Python脚本。现在,要运行这些脚本,我已对python路径进行了硬编码。但这不是便携式的。因此,我想从代码中获取Python可执行文件的路径并使用它。

题:

如何从C#代码获取ArcMap使用的Python可执行文件的路径?

编辑:

根据您的建议,现在我正在使用“路径环境”来获取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. 与python关联的“ True”表示Python已与ArcGIS一起安装
  2. ArcGIS 10.0和更高版本的注册表项将在同一过程中编写。

请帮助我澄清我的猜测。


5
您是否考虑过创建脚本工具从ArcObjects中执行脚本工具
blah238

3
您是否不仅可以将PATH环境变量设置为ArcGIS Python exe,还可以将其作为外接程序的安装要求?
乍得·库珀

所有人都说,@ ChadCooper的思想必须是最好的方法。与其尝试向后工作,不如在安装时一劳永逸地进行设置。
elrobis 2012年

@elrobis:我知道,在PATH环境中设置路径是一个好方法。但是我想知道是否有任何方法可以找到python并在不中断用户的情况下进行所有操作。
艾米(Emi)2012年

@ blah238感谢您的建议。我从未使用过脚本工具。也许我需要了解一下
Emi 2012年

Answers:


2

我以您的第二个代码示例为例,使其在64位和32位OS上均可工作,并对其进行了简化。在Windows 7 64位版本上对我来说是10.1,但显然您应该在尽可能多的环境中对其进行测试,然后添加您认为必要的防御性编程检查。

测试了不使用Python进行全新安装的ArcGIS Desktop 10.1后,我发现它不包含Python10.x子项,更不用说“ Python”的True / False值了(仍然不确定是什么意思,如果必须,可以联系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;
}

在装有Python的台式机10.1机器上,返回C:\Python27\ArcGIS10.1\python.exe。在没有Python的台式机10.1计算机上,由于不存在Python10.x密钥,因此引发了InvalidOperationException。

希望这可以帮助您完成实际要完成的任务,但令人惊讶的是,我仍然不清楚。


7

该帮助主题建议您不使用Python可执行文件,cmd.exe而直接使用python.exe它运行并在不限制其位置的情况下运行。但是请注意,这应该起作用,因为ArcGIS Desktop安装程序的设置 (编辑:最近在10.1上进行了测试,但没有)依赖于python.exe添加到用户PATH环境变量中的路径。

另一种方法是创建脚本工具从ArcObjects执行

如果您确实对ArcGIS版本的路径有所了解python.exe,可以通过扩展ArcObjects +脚本工具方法来创建Python脚本工具,其唯一输出为的值sys.exec_prefix。这是包含ArcGIS版本的Python的文件夹的路径,例如C:\Python27\ArcGIS10.1

旁注:在进程中运行时,sys.executable返回路径ArcMap.exe而不是路径python.exe,这就是为什么我不建议使用该变量的原因。

从ArcObjects调用脚本工具,并从返回的IGeoProcessorResult对象获取输出。

更新:这是一个示例ArcMap加载项项目(VS2010,.NET 3.5),该项目使用该加载项中打包的脚本工具,该脚本工具仅显示python.exeArcMap 使用的脚本路径: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();
    }

2
但是该文件并未说明,ArcGIS Desktop安装程序会将python.exe的路径设置为用户的PATH环境变量。因此,有可能python的路径不在PATH环境变量中。然后它将产生一个错误。因此,如何确定python可执行路径在用户的PATH环境变量中。
艾米(Emi)2012年

2
就像生活和计算中的大多数事物一样,您不能做任何假设,并希望事物能够起作用,并且在不起作用时制定回退计划(提供有关将其添加到PATH环境变量中的说明)。就是说,如果是正常安装,我相信ArcGIS Desktop安装程序会将该路径添加到PATH环境变量中。
blah238

2
我已经看到许多安装了arcgis安装的python不在路径中的安装。如果安装了两个版本,但路径错误,该怎么办?
blindjesse 2012年

我在第3段中提供了一个解决方案,无论PATH环境变量如何,都应该找到ArcGIS的Python安装。
blah238

@ blah238不会创建脚本工具使我的加载项不那么容易移植,或者使加载项在其他计算机上的安装过程很困难吗?
艾米(Emi)2012年

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答案。只需从程序中打开一个提示,然后在其中运行即可。我看不出为什么这行不通的原因。


2
这种方法也有缺陷,因为可能有多个Python安装,并且您没有直接的编程方式来确定ArcGIS使用的安装。如果您没有运行arcpy,则可能没有关系。不过,我认为最可靠的解决方案将涉及注册表和大量逻辑。我不会去那里。
blah238

那么逻辑必须从最新的2.7版本开始并向后进行。如果您安装了新版本的Python,然后又安装了旧版本的ArcGIS,而该版本将安装了旧版本的Python,则这当然会失败。所以,是的,我同意有这种潜力,但可能性不大(或者您可以构建一个arc10.1 = py2.7,arc10.0 = py26 ... etc的查询以确保100%的可靠性)。就像我说的那样,最好的方法可能只是将其输入命令提示符。
KHibma

@KHibma我在注册表中搜索。但是我认为,如果我通过“ PYTHON”键查看,那真的很麻烦。在我的机器上,安装了两个python版本,并且两个版本都返回。我认为,浏览“ ESRI”键是一个好主意,如果有一个带有真实值的“ Python”子键,那么我可以采用“ PythonDir”子键值。它在我的情况下有效:)
Emi

这是通过“ ESRI”键在注册表中搜索python路径的错误方法吗?还是有可能,esri用于在注册表中创建键和值的方式可以更改,并且代码可能无法读取它
Emi 2012年

当我说“搜索”注册表时,是指使用上面提供的实际路径。除非有人更了解,否则这些注册表项的位置(在注册表中)不会在计算机之间更改。因此,您只需对路径进行硬编码,以查看其中是否存在密钥,如果有的话,它的值是什么...
KHibma

5

[编辑]以set编程方式执行(在下面进行了删除)执行了我想要的操作时,可以使用Environment.GetEnvironmentVariables()更加轻松和更干净的代码来完成它。

一种选择是扫描系统上的每个环境变量,然后尝试证明以下内容:

1)环境变量值是目录吗?(如果是这样..)

2)该目录是否包含python.exe

我可以通过.Net Process API 执行set命令来以编程方式执行此操作。当不带参数使用该命令时,该命令将返回系统正在使用的所有环境变量。所以我可以进行剖析,然后整理从中发出的STDOUT结果setset,并对它们进行筛选,以查看通过系统环境最终指出的任何可用的东西(我的意思是任何东西python.exe

此页面上讨论set命令:

键入不带参数的SET以显示所有当前环境变量。

为了说明这一点,我写了一种方法(和一个辅助类)的组合来完成我上面讨论的内容。这些可以进行优化,并且可以使用一些防弹措施(Try..Catch等),但是如果计算机的ANY环境变量指向python.exe,则此方法应该可以找到它!我不在乎var是否被调用PATHABBRACADABBRA或其他名称。如果它指向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可以具有多个值,但是大多数其他var仅具有一个)。确保输入的所有字符串terms都大写!

(当我对此进行测试时,我只使用了“ PYTHON”, C:\Python27\python.exe在我的家庭系统中。但是,如果您想进一步检查python.exe返回的任何候选人的路径,则可以轻松地将其扩展为包括另一个字符串[])。例如,查看它们是否在ArcGIS bin中,等等。

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

1
这很脆弱,因为用户可以在ArcGIS Desktop安装程序中自定义Python安装目录。而且PYTHONPATH变量不是您想要的变量。
blah238

@ blah238,有时您会变得很脆弱。我真的很惊讶地看到Arc与PYTHONPATH连接起来。这是默认的9.2安装。不过,OP询问如何以编程方式获取ArcGIS python.exe,而我建议的方法(无论是否脆弱)都可以做到这一点。
elrobis 2012年

不能说我理解不赞成票,这个答案真的“ 没有用 ”吗?它可能并不出色,但它肯定是一个选项,可能对典型的Arc安装有效,并且至少为线程增加了一些帮助-特别是,它说明了默认的Arc安装选择链接其python。 exe的环境变量不是PATH
elrobis 2012年

抱歉,您不正确。Python使用PYTHONPATH变量来查找模块,而ArcGIS不使用PYTHONPATH变量来查找Python。检查链接。
blah238

@ blah238,我认为屏幕截图令人难以理解/掩盖了我要提出的观点。(特别是,我对OP的建议并不是要强调PYTHONPATH,只是碰巧是该特定系统上指向的唯一变量python.exe。)无论如何,我修改了我的答案以包括一个有效的C#代码示例,我将不胜感激知道您是否仍然不同意这种方法。谢谢/ E。
elrobis 2012年

4

根据以上问题,我想提出一个替代解决方案。对于当前的项目,我正在做非常相似的事情。我有一个.NET加载项,当用户单击ArcMap UI中的按钮时,将运行Python脚本。我要求将PATH环境变量设置为ArcGIS Python可执行文件,这样我就不必担心在.NET代码中包含Python exe的路径。

现在,在开发过程中,测试人员只需手动设置PATH变量。但是我最终将创建一个Windows安装程序(exe),它将安装加载项,安装所有Python依赖项并设置任何所需的PATH变量。为此,我使用了Nullsoft可脚本安装系统(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我完全同意这一建议-乍得在高层上说:“不要向后推论问题以推断出Arc的python实例,而要使用安装程序并将其交给SysAdmin来建立正确的python实例。” @ ChadCooper,NSIS是否为您提供任何UI控件,以便您选择可以覆盖这些默认路径?我没有看到代码所隐含的含义,但我敢打赌它在那里。
elrobis

@elrobis-我敢打赌,您可以覆盖/编辑/更改现有的-NSIS是可配置的,可以让您构建一个漂亮的安装程序-您只需要弄清楚编写它的代码即可。
乍得·库珀

为加载项创建安装程序似乎有些疯狂。另外,为了支持10.1、10.2等以及10.0,需要进行哪些修改?
blah238

@ blah238-是的,的确看起来很疯狂,但是正如我所谈论的,我为这个特定项目的安装程序将做更多的事情。我的加载项严格用于10.0。我猜对于不同版本的ArcGIS,您可以检查注册表以查看已安装的版本,然后采取相应措施。
乍得·库珀
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.