NXOpen Python API二次开发环境配置
NXOpen Python API二次开发环境配置
前言
最近在工作中遇到一些NX二次开发的工作,有一些深度学习与NX交互的工作内容,尝试使用使用python进行二次开发。在网上搜了几个环境配置的教程,都没有实现代码提示的功能,最近找到生成.pyi文件的方法才实现了代码提示功能,把完整的配置流程给大家分享。
1 安装NX和对应版本的python
1)在NX安装目录下D:\安装目录\NX1946\NXBIN\python找到python37.dll文件,查看文件属性。如安装的是 NX1946,可以查看到对应的python版本是3.7.5。
2)下载对应的python版本安装到将要用于项目开发的文件夹中,直接使用安装环境,避免虚拟环境的各种问题。如直接安装在D:\NXOpenPython中。
2 添加nxopen库
1)在D:\NXOpenPython\python37\Lib\site-packages目录中新建一个nxopen.pth的文件在文件中添加D:\安装目录\NX1946\NXBIN\python。
2)添加UGII目录到系统变量PATH下。
PATH = D:\安装目录\NX1946\UGII; D:\安装目录\NX1946\NXBIN\
3)进入IDLE进行测试。
3 在Pycharm中新建项目
1)python解释器使用刚安装的python,并添加NXBIN目录。
已经能调到NXOpen的API了,但是只能提示到安装包,没有函数提示功能。
4 IED中实现代码提示功能
PyCharm 本身并不直接“解析” .pyd 文件(Windows 下的 Python 扩展模块,本质上是 DLL 文件),因为 .pyd 是编译后的二进制文件,不是源代码。但如果你希望在 PyCharm 中获得代码补全、类型提示、跳转定义等 IDE 功能支持,那么你需要采取一些额外措施,因为 PyCharm 无法从 .pyd 文件中读取函数签名或文档。
为了让 PyCharm 提供智能提示(自动补全、参数提示、跳转等),你需要提供 类型存根(stub files),即 .pyi 文件。(在方头狮https://www.cnblogs.com/unm001/p/16259771.html?_refluxos=a10找打了生成.pyi的代码)
- 先运行这个代码,生成 NXOpen 文件夹,
- 再把这个文件夹移动到 NXBIN/python目录下。
__version__ ="0.0.4" __author__ ="unmht"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:breakfor ff in files:# print(ff)ifstr(ff).startswith("NXOpen_")andstr(ff).endswith(".pyd"): module_name =str(ff)[:-4]# print(module_name)try: ms = module_name.split("_")[-1] imp_mds[ms]= import_module(module_name)except Exception as e:print("error:", module_name, e)classpyibase: 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]=[]defdcts(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 defaddmember(self, m, dm: List[str]=None): dirm =dir(m)if dm isnotNone: dirm =[i for i in dirm if i notin dm]for i in dirm:if i =="GetSession":print(i)ifnot i.startswith("_"): att =getattr(m, i) self.nn.append(i)ifisinstance(att, types.ModuleType): self.chdmdls[i]= nxoModuleType(att)elifisinstance(att,type):if i != att.__name__: self.tpsinvoke[i]=[att.__module__, att.__name__]elif i notin pyibase.ecpts: self.chdtps[i]= nxoType(att)elifisinstance(att, types.MethodDescriptorType): self.MdDescriptor[i]= nxoMethodDescriptorType(att)elifisinstance(att, types.GetSetDescriptorType): self.GSDescriptor[i]= nxoGetSetDescriptorType(att)elifisinstance(att, types.MemberDescriptorType): self.mberDescriptor[i]= nxoMemberDescriptorType(att)else: _md = att.__class__.__module__ if"NXOpen"instr(_md): self.nxc[i]= nxoClsAliasType(att, i)elif i =="ValueOf": self.mthdorbutin[i]= nxoBuiltinFunctionType(att)elifisinstance(att, types.BuiltinFunctionType): self.mthdorbutin[i]= nxoBuiltinFunctionType( att, cname=self.name )else:assert TypeError(i, att,type(att))defdocs(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 *4yield(lv +1)*"\t"+"'''"yield(lv +1)*"\t"+f"### {self.__class__.__name__}"for i in dd2:for k inrange(0,len(i), ll):yield(lv +1)*"\t"+ i[k :min(len(i), k + ll)]yield(lv +1)*"\t"+"'''"deftoStr(self, lv:int=0, mx:int=130):yield lv *"\t"+f"{self.name}:-->"classnxoModuleType(pyibase):def__init__(self, m: types.ModuleType)->None:super().__init__()assertisinstance(m, types.ModuleType) self.doc = m.__doc__ self.name = m.__name__.split(".")[-1] self.addmember(m)deftoStr(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}"classbasetps(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)deftoStr(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}"classnxoType(pyibase):def__init__(self, m:type)->None:super().__init__()assertisinstance(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 indir(k):ifnot s.startswith("_"): ll.append(s) self.addmember(m, ll)deftoStr(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)deffindrtype(ss:str): rtp ="" r = p1.search(ss)if r isnotNone: rtp = r.groups()[0]if rtp =="": r = p2.search(ss)if r isnotNone: rtp =f"List[{r.groups()[0]}]"if rtp =="": r = p3.search(ss)if r isnotNone: rtp = r.groups()[0]if rtp notin["int","str","float","bool"]: rtp =""if rtp =="": r = p4.search(ss)if r isnotNone: rtp = r.groups()[0]if rtp notin["int","str","float","bool"]: rtp =""else: rtp =f"List[{rtp}]"return rtp classnxoMethodDescriptorType(pyibase):def__init__(self, m: types.MethodDescriptorType)->None:super().__init__()assertisinstance(m, types.MethodDescriptorType) self.doc = m.__doc__ self.name = m.__name__ deftoStr(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"+"..."classnxoGetSetDescriptorType(pyibase):def__init__(self, m: types.GetSetDescriptorType)->None:super().__init__()assertisinstance(m, types.GetSetDescriptorType) self.doc = m.__doc__ self.name = m.__name__ deftoStr(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""classnxoMemberDescriptorType(pyibase):def__init__(self, m: types.MemberDescriptorType)->None:super().__init__()assertisinstance(m, types.MemberDescriptorType) self.doc = m.__doc__ self.name = m.__name__ self._tp =type(m).__name__ deftoStr(self, lv:int=0, mx:int=130):yield lv *"\t"+f"{self.name}:{self._tp}=..."for i in self.docs(lv):yield i classnxoBuiltinFunctionType(pyibase):def__init__( self, m:"types.MemberDescriptorType |types.BuiltinMethodType", cname="")->None:super().__init__()assertisinstance(m, types.BuiltinFunctionType)andisinstance( m, types.BuiltinMethodType ) self.doc = m.__doc__ self.name = m.__name__ self.cname = cname deftoStr(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:ifhasattr(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"+"..."classnxoClsAliasType(pyibase):def__init__(self, m, name="")->None:super().__init__() _md = m.__class__.__module__ assert"NXOpen"instr(_md) self.doc = m.__doc__ self.name = m.__name__ ifhasattr(m,"__name__")else name self.attp =str(m.__class__.__name__) self.body = m deftoStr(self, lv=0, mx=130): nn = self.attp ifhasattr(NXOpen, nn): att =getattr(NXOpen, nn)ifisinstance(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 classmainModules(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)defpyi(self): mm = self ifnot os.path.exists("./NXOpen/"): os.mkdir("./NXOpen")ifnot os.path.exists("./NXOpen/clss/"): os.mkdir("./NXOpen/clss/") p11 ="./NXOpen/" p22 = os.path.join(p11,"clss/")withopen(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"withopen(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")withopen(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")withopen( 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__":defmain(): 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,可解决卡死问题。