跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonAI

NXOpen Python API 二次开发环境配置

综述由AI生成介绍如何在 Windows 环境下配置 NXOpen Python API 二次开发环境。主要步骤包括安装对应版本的 Python、添加 nxopen 库路径、配置系统环境变量 PATH、在 PyCharm 中设置解释器及生成.pyi 类型存根文件以实现代码提示。此外还说明了如何在 NX 软件中运行外部 Python 脚本,需修改 ugii_env.dat 配置文件并设置线程模式避免卡死。

山野来信发布于 2026/3/30更新于 2026/5/2431 浏览
NXOpen Python API 二次开发环境配置

NXOpen Python API 二次开发环境配置

前言

最近在工作中遇到一些 NX 二次开发的工作,有一些深度学习与 NX 交互的工作内容,尝试使用 Python 进行二次开发。在网上搜索了几个环境配置的教程,都没有实现代码提示的功能,最近找到生成.pyi 文件的方法才实现了代码提示功能,把完整的配置流程分享给大家。

1 安装 NX 和对应版本的 Python

1)在 NX 安装目录下找到 python37.dll 文件,查看文件属性。如安装的是 NX1946,可以查看到对应的 Python 版本是 3.7.5。

![图片]

2)下载对应的 Python 版本安装到将要用于项目开发的文件夹中,直接使用安装环境,避免虚拟环境的各种问题。如直接安装在 D:\NXOpenPython 中。

2 添加 nxopen 库

1)在 D:\NXOpenPython\python37\Lib\site-packages 目录中新建一个 nxopen.pth 的文件,在文件中添加 NXBIN 下的 python 路径。

2)添加 UGII 目录到系统变量 PATH 下。

PATH = D:\安装目录\NX1946\UGII; D:\安装目录\NX1946\NXBIN\

3)进入 IDLE 进行测试。

![图片]

![图片]

3 在 PyCharm 中新建项目

1)Python 解释器使用刚安装的 Python,并添加 NXBIN 目录。

![图片]

![图片]

已经能调用 NXOpen 的 API 了,但是只能提示到安装包,没有函数提示功能。

![图片]

4 IDE 中实现代码提示功能

PyCharm 本身并不直接'解析' .pyd 文件(Windows 下的 Python 扩展模块,本质上是 DLL 文件),因为 .pyd 是编译后的二进制文件,不是源代码。但如果你希望在 PyCharm 中获得代码补全、类型提示、跳转定义等 IDE 功能支持,那么你需要采取一些额外措施,因为 PyCharm 无法从 .pyd 文件中读取函数签名或文档。

为了让 PyCharm 提供智能提示(自动补全、参数提示、跳转等),你需要提供类型存根(stub files),即 .pyi 文件。

  • 先运行下面的代码,生成 NXOpen 文件夹。
  • 再把这个文件夹移动到 NXBIN/python 目录下。
__version__ = "0.0.4"
from typing import Dict, Generator, List, Optional
import NXOpen
import types
import os
import re
from importlib import import_module

ugii_base_dir = os.getenv("UGII_BASE_DIR")
pp = os.path.join(str(ugii_base_dir), "nxbin", "python")
imp_mds = {}
for root, dirs, files in os.walk(pp, topdown=True):
    if root != pp:
        break
    for ff in files:
        if str(ff).startswith("NXOpen_") and str(ff).endswith(".pyd"):
            module_name = str(ff)[:-4]
            try:
                ms = module_name.split("_")[-1]
                imp_mds[ms] = import_module(module_name)
            except Exception as e:
                print("error:", module_name, e)

class pyibase:
    ecpts = ["NXObject", "TaggedObject", "INXObject"]

    def __init__(self) -> None:
        self.name: str = ""
        self.doc: Optional[str] = ""
        self.chdmdls: Dict = {}
        self.chdtps: Dict = {}
        self.MdDescriptor: Dict = {}
        self.GSDescriptor: Dict = {}
        self.unct: Dict = {}
        self.nxc: Dict[str, "nxoClsAliasType | pyibase"] = {}
        self.mthdorbutin: Dict = {}
        self.mberDescriptor: Dict = {}
        self.tpsinvoke: Dict[str, List] = {}
        self.nn: List[str] = []

    def dcts(self) -> Generator["pyibase", None, None]:
        for dc in [self.chdmdls, self.chdtps, self.MdDescriptor, self.GSDescriptor, self.unct, self.nxc, self.mthdorbutin, self.mberDescriptor]:
            for v in dc.values():
                yield v

    def addmember(self, m, dm: List[str] = None):
        dirm = dir(m)
        if dm is not None:
            dirm = [i for i in dirm if i not in dm]
        for i in dirm:
            if i == "GetSession":
                print(i)
            if not i.startswith("_"):
                att = getattr(m, i)
                self.nn.append(i)
                if isinstance(att, types.ModuleType):
                    self.chdmdls[i] = nxoModuleType(att)
                elif isinstance(att, type):
                    if i != att.__name__:
                        self.tpsinvoke[i] = [att.__module__, att.__name__]
                    elif i not in pyibase.ecpts:
                        self.chdtps[i] = nxoType(att)
                elif isinstance(att, types.MethodDescriptorType):
                    self.MdDescriptor[i] = nxoMethodDescriptorType(att)
                elif isinstance(att, types.GetSetDescriptorType):
                    self.GSDescriptor[i] = nxoGetSetDescriptorType(att)
                elif isinstance(att, types.MemberDescriptorType):
                    self.mberDescriptor[i] = nxoMemberDescriptorType(att)
                else:
                    _md = att.__class__.__module__
                    if "NXOpen" in str(_md):
                        self.nxc[i] = nxoClsAliasType(att, i)
                    elif i == "ValueOf":
                        self.mthdorbutin[i] = nxoBuiltinFunctionType(att)
                    elif isinstance(att, types.BuiltinFunctionType):
                        self.mthdorbutin[i] = nxoBuiltinFunctionType(att, cname=self.name)
                    else:
                        assert TypeError(i, att, type(att))

    def docs(self, lv: int = 0, mx: int = 130):
        dd = str(self.doc)
        dd = dd.strip()
        dd2 = dd.splitlines()
        dd2 = [i.strip() for i in dd2]
        ll = mx - lv * 4
        yield (lv + 1) * "\t" + "'''"
        yield (lv + 1) * "\t" + f"### {self.__class__.__name__}"
        for i in dd2:
            for k in range(0, len(i), ll):
                yield (lv + 1) * "\t" + i[k:min(len(i), k + ll)]
        yield (lv + 1) * "\t" + "'''"

    def toStr(self, lv: int = 0, mx: int = 130):
        yield lv * "\t" + f"{self.name}:-->"

class nxoModuleType(pyibase):
    def __init__(self, m: types.ModuleType) -> None:
        super().__init__()
        assert isinstance(m, types.ModuleType)
        self.doc = m.__doc__
        self.name = m.__name__.split(".")[-1]
        self.addmember(m)

    def toStr(self, lv: int = 0, mx: int = 130):
        yield lv * "\t" + f"class {self.name}:"
        for i in self.docs(lv):
            yield i
        for dc in self.dcts():
            for i in dc.toStr(lv + 1):
                yield i
        for i, v in self.tpsinvoke.items():
            _a = ".".join(v)
            yield "\t" + f"{i}={_a}"

class basetps(pyibase):
    def __init__(self, m: type, name="") -> None:
        super().__init__()
        assert name in pyibase.ecpts
        self.name = name
        self.doc = m.__doc__
        self.mo = type.mro(m)
        self.addmember(m)

    def toStr(self, lv=0, mx=130):
        if self.name in ["TaggedObject", "INXObject"]:
            yield lv * "\t" + f"class {self.name}(object):"
        elif self.name == "NXObject":
            yield lv * "\t" + f"class {self.name}(NXOpen.TaggedObject,NXOpen.INXObject):"
        else:
            yield lv * "\t" + f"class {self.name}(object):"
        for i in self.docs(lv):
            yield i
        for v in self.dcts():
            for ss in v.toStr(lv + 1):
                yield ss
        for i, v in self.tpsinvoke.items():
            _a = ".".join(v)
            yield lv * "\t" + "\t" + f"{i}={_a}"

class nxoType(pyibase):
    def __init__(self, m: type) -> None:
        super().__init__()
        assert isinstance(m, type)
        self.doc = m.__doc__
        self.name = m.__name__
        self.mo = type.mro(m)
        self.sp = self.mo[1]
        ll = []
        for k in self.mo[1:]:
            for s in dir(k):
                if not s.startswith("_"):
                    ll.append(s)
        self.addmember(m, ll)

    def toStr(self, lv=0, mx=130):
        yield lv * "\t" + f"class {self.name}({str(self.sp)[8:-2]}):"
        for i in self.docs(lv):
            yield i
        for v in self.dcts():
            for i in v.toStr(lv + 1):
                yield i
        for i, v in self.tpsinvoke.items():
            _a = ".".join(v)
            yield lv * "\t" + "\t" + f"{i}={_a}"

pt1 = r":returns:[ \S]*[\n]?\s*:rtype: :py:class:`([\S]+)`"
p1 = re.compile(pt1)
pt2 = r":returns:[ \S]*[\n]?\s*:rtype: list of :py:class:`([\S]+)`"
p2 = re.compile(pt2)
pt3 = r":returns:[ \S]*[\n]?\s*:rtype: ([\S]+)"
p3 = re.compile(pt3)
pt4 = r":returns:[ \S]*[\n]?\s*:rtype: list of ([\S]+)"
p4 = re.compile(pt4)

def findrtype(ss: str):
    rtp = ""
    r = p1.search(ss)
    if r is not None:
        rtp = r.groups()[0]
    if rtp == "":
        r = p2.search(ss)
        if r is not None:
            rtp = f"List[{r.groups()[0]}]"
    if rtp == "":
        r = p3.search(ss)
        if r is not None:
            rtp = r.groups()[0]
        if rtp not in ["int", "str", "float", "bool"]:
            rtp = ""
    if rtp == "":
        r = p4.search(ss)
        if r is not None:
            rtp = r.groups()[0]
        if rtp not in ["int", "str", "float", "bool"]:
            rtp = ""
    else:
        rtp = f"List[{rtp}]"
    return rtp

class nxoMethodDescriptorType(pyibase):
    def __init__(self, m: types.MethodDescriptorType) -> None:
        super().__init__()
        assert isinstance(m, types.MethodDescriptorType)
        self.doc = m.__doc__
        self.name = m.__name__

    def toStr(self, lv: int = 0, mx: int = 130):
        rtp = findrtype(str(self.doc))
        if rtp == "":
            rr = ""
        else:
            rr = f"->{rtp}"
        if self.name == "Dispose":
            yield lv * "\t" + f"def {self.name}(self){rr}:"
        else:
            yield lv * "\t" + f"def {self.name}(self,*args,**kw){rr}:"
        for i in self.docs(lv):
            yield i
        yield lv * "\t" + "\t" + "..."

class nxoGetSetDescriptorType(pyibase):
    def __init__(self, m: types.GetSetDescriptorType) -> None:
        super().__init__()
        assert isinstance(m, types.GetSetDescriptorType)
        self.doc = m.__doc__
        self.name = m.__name__

    def toStr(self, lv: int = 0, mx: int = 130):
        rtp = findrtype(str(self.doc))
        yield lv * "\t" + f"@property"
        if rtp == "":
            yield lv * "\t" + f"def {self.name}(self):"
        else:
            yield lv * "\t" + f"def {self.name}(self)->{rtp}:"
        for i in self.docs(lv):
            yield i
        yield lv * "\t" + "\t" + "..."
        yield lv * "\t" + f"@{self.name}.setter"
        if rtp == "":
            yield lv * "\t" + f"def {self.name}(self,value):..."
        else:
            yield lv * "\t" + f"def {self.name}(self,value:{rtp}):..."
        yield ""

class nxoMemberDescriptorType(pyibase):
    def __init__(self, m: types.MemberDescriptorType) -> None:
        super().__init__()
        assert isinstance(m, types.MemberDescriptorType)
        self.doc = m.__doc__
        self.name = m.__name__
        self._tp = type(m).__name__

    def toStr(self, lv: int = 0, mx: int = 130):
        yield lv * "\t" + f"{self.name}:{self._tp}=..."
        for i in self.docs(lv):
            yield i

class nxoBuiltinFunctionType(pyibase):
    def __init__(self, m: "types.MemberDescriptorType | types.BuiltinMethodType", cname="") -> None:
        super().__init__()
        assert isinstance(m, types.BuiltinFunctionType) and isinstance(m, types.BuiltinMethodType)
        self.doc = m.__doc__
        self.name = m.__name__
        self.cname = cname

    def toStr(self, lv: int = 0, mx: int = 130):
        rtp = findrtype(str(self.doc))
        yield lv * "\t" + f"@classmethod"
        if self.name == "ValueOf":
            yield lv * "\t" + f"def {self.name}(cls,value:int):"
        elif rtp != "":
            yield lv * "\t" + f"def {self.name}(cls,*args,**kw)->{rtp}:"
        elif self.cname != "" and self.name == "Get" + self.cname:
            if hasattr(NXOpen, self.cname):
                yield lv * "\t" + f"def {self.name}(cls,*args,**kw)->NXOpen.{self.cname}:"
            else:
                yield lv * "\t" + f"def {self.name}(cls,*args,**kw)->{self.cname}:"
        else:
            yield lv * "\t" + f"def {self.name}(cls,*args,**kw):"
        for i in self.docs(lv):
            yield i
        yield lv * "\t" + "\t" + "..."

class nxoClsAliasType(pyibase):
    def __init__(self, m, name="") -> None:
        super().__init__()
        _md = m.__class__.__module__
        assert "NXOpen" in str(_md)
        self.doc = m.__doc__
        self.name = m.__name__ if hasattr(m, "__name__") else name
        self.attp = str(m.__class__.__name__)
        self.body = m

    def toStr(self, lv=0, mx=130):
        nn = self.attp
        if hasattr(NXOpen, nn):
            att = getattr(NXOpen, nn)
            if isinstance(att, type):
                nn = f"NXOpen.{nn}"
            else:
                nn = self.body.__class__.__module__ + "." + self.body.__class__.__name__
        if self.name == "KeyPerformanceInterface":
            print(self.name, f"{self.name}:{nn}=...")
        yield lv * "\t" + f"{self.name}:{nn}=..."
        for i in self.docs(lv - 1):
            yield i

class mainModules(pyibase):
    def __init__(self) -> None:
        super().__init__()
        m = NXOpen
        self.doc = m.__doc__
        self.name = m.__name__
        self.addmember(m)
        for i in pyibase.ecpts:
            self.chdtps[i] = basetps(getattr(NXOpen, i), i)
        for k, v in imp_mds.items():
            self.chdmdls[k] = nxoModuleType(v)

    def pyi(self):
        mm = self
        if not os.path.exists("./NXOpen/"):
            os.mkdir("./NXOpen/")
        if not os.path.exists("./NXOpen/clss/"):
            os.mkdir("./NXOpen/clss/")
        p11 = "./NXOpen/"
        p22 = os.path.join(p11, "clss/")
        with open(os.path.join(p11, "__init__.py"), "w", encoding="utf8") as f:
            m: nxoModuleType
            for v in mm.chdmdls.values():
                m = v
                mn = m.name.split(".")[-1]
                mName = mn + ".pyi"
                with open(os.path.join(p11, mName), "w") as f2:
                    f2.write("import NXOpen\n")
                    f2.write("from typing import List\n")
                    for _s in m.toStr():
                        f2.write(_s + "\n")
                f.write(f"from .{mn} import {mn} as {mn}\n")
                f.write(f"from ._nxopen import *\n")
        with open(os.path.join(p11, "_nxopen.pyi"), "w", encoding="utf8") as f2:
            m2: nxoType
            f2.write("import NXOpen\n")
            f2.write("from typing import List\n")
            for k, v in mm.chdtps.items():
                m2 = v
                nn = m2.name
                f2.write(f"from .clss.{nn} import {nn} as {nn}\n")
        with open(os.path.join(p22, f"{nn}.pyi"), "w", encoding="utf8") as f3:
            f3.write("import NXOpen\n")
            f3.write("from typing import List\n")
            for _s in m2.toStr():
                f3.write(f"{_s}\n")

if __name__ == "__main__":
    def main():
        mainModules().pyi()
    main()

生成的 NXOpen 文件夹放到这里:

![图片]

已经可以正常提示了:

![图片]

5 在 NX 中运行外部 Python

主要是两个环境变量的设置,修改 NX1946\UGII\ugii_env.dat 文件。

UGII_PYTHON_LIBRARY_DIR="D:\Python37"

UGII_PYTHONPATH=D:\Python37;D:\Python37\DLLs;D:\Python37\Lib;D:\Python37\Lib\site-packages;D:\Python37\libs"D:\Siemens\NX 1946\NXBIN\python"

UGII_PYTHON_LIBRARY_DIR指向的是python37.dll文件所在的目录。

![图片]

注:运行第三方库的时候需要在代码前增加#nx:threaded,可解决卡死问题。

目录

  1. NXOpen Python API 二次开发环境配置
  2. 前言
  3. 1 安装 NX 和对应版本的 Python
  4. 2 添加 nxopen 库
  5. 3 在 PyCharm 中新建项目
  6. 4 IDE 中实现代码提示功能
  7. 5 在 NX 中运行外部 Python
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • C++ 从零实现 string 类详解
  • VSCode 中彻底关闭 GitHub Copilot 功能的方法
  • AI绘画报错
  • LangChain PyPDFLoader 实战:PDF 加载、分块与图片 OCR 提取
  • 使用 Python + Flask + Tailwind 快速搭建个人博客
  • Java IO 流进阶:字符流与字节流的核心应用
  • Visual Studio 使用 GitHub Copilot 与 IntelliCode 辅助编码
  • Dify 大模型应用开发实战:Agent 与 RAG 架构解析
  • 双指针算法实战:快乐数与盛最多水的容器
  • 基于 Python Vue3 Django 的大学生学习互助与任务接单系统
  • Vue3 Composition API 方法调用失效排查:script setup 暴露机制解析
  • Windows 下 Node.js 安装指南:多方法与开发环境配置
  • Git 与 TortoiseGit 安装配置实战指南
  • YOLOv8 推理与训练图片尺寸选择指南
  • DeepSeek 20 个实用建议:普通人如何高效使用大模型
  • 为什么 Python 代码在函数中运行得更快
  • 灵感画廊:基于 Stable Diffusion XL 的极简 AI 绘画体验
  • 线性表、顺序表与链表详解(C 语言实现)
  • Python 3.12.0 在 Windows 系统下的安装与配置指南
  • Git 分布式版本控制系统:安装、配置与实战指南

相关免费在线工具

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online