明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 2227|回复: 18

[【PyCAD】] 【pycad】让Cpython在CAD中开发

  [复制链接]
发表于 2023-7-28 21:06 | 显示全部楼层 |阅读模式
本帖最后由 枫叶棋语 于 2023-8-22 22:37 编辑
继上次的实验,我们初步实现了Ironpython环境加载Cpython环境,经过一番苦战,实现了在cad上运行Cpython,包括CAD注册命令,兼容Ipy脚本,本次更新实现了Cpython在CAD上运行,并依靠第三方模块开发的功能。相当于80%成品,因为本人能力有限,未能脱离借助Ironpython,直接使用c#调用Cpython,望后续有看到的兄弟助理一把。
这个是Cpython引擎
  1. import clr
  2. import sys
  3. import os
  4. clr.AddReference("Python.Runtime")
  5. clr.AddReference("acmgd")
  6. clr.AddReference("AcCoreMgd")
  7. clr.AddReference("AcDbMgd")
  8. clr.AddReference("System")

  9. import System
  10. import Autodesk.AutoCAD.ApplicationServices as acap
  11. from Python.Runtime import *

  12. dllpath = System.Reflection.Assembly.GetExecutingAssembly().Location
  13. dlldir = os.path.dirname(dllpath)
  14. supportdir = os.path.join(dlldir, "support")
  15. extensionsdir = os.path.join(dlldir, "extensions")
  16. referencedir = os.path.join(dlldir,"Reference")


  17. class CpyEngine:
  18.     def __init__(self,pydll):
  19.         self.pydll =pydll
  20.         self.pyhome =os.path.dirname(pydll)
  21.         self.pypath = "{self.pyhome}\\Lib;{self.pyhome}\\Lib\\site-packages"
  22.         self.SetCpyEnviromnent()
  23.         self.Initilize()
  24.     @staticmethod
  25.     def prinf(msg):
  26.         acap.Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(f"\n{msg}\n")
  27.     def SetCpyEnviromnent(self):
  28.         '''配置Cpython环境变量'''
  29.         os.environ["PYTHONHOME"]=self.pyhome
  30.         os.environ["PYTHONNET_PYDLL"] = self.pydll
  31.         os.environ["PYTHONPATH"] = self.pypath
  32.         
  33.     def Initilize(self):
  34.         '''初始化Cpython引擎'''
  35.         if not PythonEngine.IsInitialized:
  36.             PythonEngine.Initialize()
  37.         PythonEngine.PythonPath =self.pypath

  38.     def RunCpyScript(self,scriptpath):
  39.         '''运行脚本'''
  40.         import traceback
  41.         self.scope = Py.CreateScope()
  42.         pysys=Py.Import("sys")
  43.         pysys.path.append(dlldir)
  44.         pysys.path.append(supportdir)
  45.         pysys.path.append(extensionsdir)
  46.         pysys.path.append(referencedir)
  47.         with open(scriptpath,"r",encoding="utf-8") as file:
  48.             pythoncode= file.read()
  49.         with Py.GIL():
  50.             try:
  51.                 self.scope.Exec(pythoncode)
  52.             except:
  53.                 error =traceback.format_exc()
  54.                 self.prinf(error)
复制代码



  1. import clr
  2. clr.AddReference("System.Windows.Forms")
  3. clr.AddReference("System.Drawing")
  4. import System
  5. from System.Drawing import *
  6. from System.Windows.Forms import *
  7. import os
  8. from pycad.system.acad import acap, prinf
  9. from pycad.system.CpyEngine import CpyEngine
  10. def check_and_create_file(filename):
  11.     '''判断文件是否存在,不存在就创建空文件'''
  12.     if not os.path.exists(filename):
  13.         open(filename, 'w')
  14. class MainForm(Form):
  15.     def __init__(self):
  16.         self.InitializeComponent()
  17.         self.Resize += self.FormChanged
  18.         self.MinimumSize = Size(500, 500)
  19.         self.dllpath = System.Reflection.Assembly.GetExecutingAssembly().Location
  20.         self.dlldir = os.path.dirname(self.dllpath)
  21.         self.history = self.dlldir+"\CpyHistory.txt"
  22.         check_and_create_file(self.history)
  23.         self.HistoryPaths = self.ReadHitory()
  24.         self.ImportPaths(self.HistoryPaths)
  25.         self.engine = CpyEngine(r"D:\Program Files\Python310\python310.dll")
  26.     @staticmethod
  27.     def SetButton(button,anchor, x, y,  name, text, func):
  28.         button.Anchor =anchor
  29.         button.Location = Point(x, y)
  30.         button.Size = Size(75, 25)
  31.         button.Name = name
  32.         button.Text = text
  33.         button.UseVisualStyleBackColor = True
  34.         button.Click += func
  35.         
  36.     @staticmethod
  37.     def AddItem(listview: ListView, lst):
  38.         item = ListViewItem(items=tuple(lst))
  39.         listview.Items.Add(item)
  40.         
  41.     def InitializeComponent(self):
  42.         self._Add = Button()
  43.         self._Delete = Button()
  44.         self._Clear = Button()
  45.         self._Apply = Button()
  46.         self._Cancel = Button()
  47.         self._Reload = Button()
  48.         self._ScriptPaths = ListView()
  49.         self._FileName = ColumnHeader()
  50.         self._FilePath = ColumnHeader()
  51.         self.Size = Size(500, 550)
  52.         self.ButtonWidth = 75
  53.         self.ButtonHeight = 25
  54.         self.ButtonX = 425
  55.         self.ListViewWidth =self.ButtonX-75
  56.         self.ListViewHeight =self.Size.Height -200
  57.         
  58.         # ScriptPaths
  59.         self._ScriptPaths.View = View.Details
  60.         self._ScriptPaths.Anchor = AnchorStyles.Left|AnchorStyles.Top|AnchorStyles.Bottom|AnchorStyles.Right
  61.         self._ScriptPaths.Location = Point(30, 50)
  62.         self._ScriptPaths.Name = "ScriptPaths"
  63.         self._ScriptPaths.Size = Size(self.ListViewWidth,self.ListViewHeight)
  64.         self._ScriptPaths.GridLines =True
  65.         self._ScriptPaths.UseCompatibleStateImageBehavior = False
  66.         self._ScriptPaths.Columns.Add("文件")
  67.         self._ScriptPaths.Columns.Add("路径")
  68.         self._ScriptPaths.Columns[0].Width= 150
  69.         self._ScriptPaths.Columns[1].Width = self.ListViewWidth-150
  70.         self._ScriptPaths.BorderStyle = BorderStyle.FixedSingle
  71.         self._ScriptPaths.FullRowSelect =True
  72.             
  73.         self.SetButton(self._Add,    AnchorStyles.Right| AnchorStyles.Top,  400,  70,    "Add", "添加", self.AddClick)
  74.         self.SetButton(self._Delete, AnchorStyles.Right| AnchorStyles.Top,  400, 140, "Delete", "删除", self.DeleteClick)
  75.         self.SetButton(self._Clear,  AnchorStyles.Right| AnchorStyles.Top,  400, 210,  "Clear",  "清空", self.ClearClick)
  76.         self.SetButton(self._Reload, AnchorStyles.Bottom|AnchorStyles.Right,200, 450,  "Reload", "重载", self.ReloadClick)
  77.         self.SetButton(self._Apply,  AnchorStyles.Bottom|AnchorStyles.Right,300, 450,  "Apply",  "确定", self.ApplyClick)
  78.         self.SetButton(self._Cancel, AnchorStyles.Bottom|AnchorStyles.Right,400, 450, "Cancel",  "取消", self.CancelClick)

  79.         # 窗体添加控件
  80.         self.Controls.Add(self._ScriptPaths)
  81.         self.Controls.Add(self._Cancel)
  82.         self.Controls.Add(self._Apply)
  83.         self.Controls.Add(self._Clear)
  84.         self.Controls.Add(self._Reload)
  85.         self.Controls.Add(self._Delete)
  86.         self.Controls.Add(self._Add)
  87.         self.Text = "加载CPython脚本"
  88.         self.ResumeLayout(False)
  89.         
  90.     #读取history文本内容
  91.     def ReadHitory(self):
  92.         with open(self.history,"r",encoding="utf-8") as file:
  93.             existing_paths = file.read().splitlines()
  94.             return existing_paths
  95.     #读取listview内容
  96.     def ReadListView(self):
  97.         paths = []
  98.         for item in self._ScriptPaths.Items:
  99.             path = item.SubItems[1].Text
  100.             paths.append(path)
  101.         return paths
  102.     #导入路径
  103.     def ImportPaths(self,paths):
  104.         for path in paths:
  105.             basename = os.path.basename(path)
  106.             self.AddItem(self._ScriptPaths,[basename,path])
  107.     #导出路径   
  108.     def ExportPaths(self,paths):
  109.         with open(self.history, "w") as file:
  110.             for item in self._ScriptPaths.Items:
  111.                 path = item.SubItems[1].Text
  112.                 if path not in existing_paths:
  113.                     file.write(path + '\n')

  114.     def AddClick(self, sender, e):
  115.         openFileDialog = OpenFileDialog()
  116.         openFileDialog.Multiselect = True
  117.         if openFileDialog.ShowDialog() == DialogResult.OK:
  118.             self._ScriptPaths.Items.Clear()
  119.             file_names = openFileDialog.FileNames
  120.             result = set(file_names)|set(self.HistoryPaths)
  121.             self.ImportPaths(result)
  122.             self.HistoryPaths =list(result)
  123.             for path in file_names:
  124.                 self.engine.RunCpyScript(path)
  125.             
  126.     def DeleteClick(self, sender, e):
  127.         selected_items = self._ScriptPaths.SelectedItems
  128.         for item in selected_items:
  129.             self._ScriptPaths.Items.Remove(item)
  130.         self.HistoryPaths =self.ReadListView()

  131.     def ClearClick(self, sender, e):
  132.         self._ScriptPaths.Items.Clear()
  133.         self.HistoryPaths = []
  134.         
  135.     def ApplyClick(self, sender, e):
  136.         with open(self.history, "w") as file:
  137.             for path in self.HistoryPaths:
  138.                 file.write(f"{path}\n")
  139.         self.Close()
  140.             
  141.     def ReloadClick(self, sender, e):
  142.         selected_items = self._ScriptPaths.SelectedItems
  143.         for item in selected_items:
  144.             path = item.SubItems[1].Text
  145.             self.engine.RunCpyScript(path)
  146.             
  147.     def CancelClick(self, sender, e):
  148.         self.Close()

  149.     def FormChanged(self, sender, e):
  150.         self._ScriptPaths.Columns[1].Width = self.Size.Width - 200
  151.         
  152. loadform = MainForm()
  153. acap.Application.ShowModelessDialog(loadform)
与此同时,我们建立一个CpyReload脚本,用于命令重载所有脚本命令,代码如下:
  1. from pycad.system import prinf
  2. from pycad.system.CpyEngine import CpyEngine
  3. import System
  4. import os,traceback
  5. import re
  6. def check_and_create_file(filename):
  7.     '''判断文件是否存在,不存在就创建空文件'''
  8.     if not os.path.exists(filename):
  9.         open(filename, 'w')
  10. class Reload:
  11.     def __init__(self):
  12.         self.engine = CpyEngine(r"D:\Program Files\Python310\python310.dll")
  13.         self.dllpath = System.Reflection.Assembly.GetExecutingAssembly().Location
  14.         self.dlldir = os.path.dirname(self.dllpath)
  15.         self.history = self.dlldir+"\CpyHistory.txt"
  16.         check_and_create_file(self.history)
  17.         self.reload()

  18.     def reload(self):
  19.         with open(self.history,"r",encoding="utf-8") as file:
  20.             paths = file.read().splitlines()
  21.             for path in paths:
  22.                 self.engine.RunCpyScript(path)  

  23. Reload()
复制代码
至此,CpyCAD的框架的python代码基本搭设完成,由于权限问题,暂时不能上传文件。有需要的可以联系。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x

评分

参与人数 1明经币 +1 收起 理由
caoliu023 + 1 很给力!

查看全部评分

本帖被以下淘专辑推荐:

  • · python|主题: 1, 订阅: 0
 楼主| 发表于 2023-7-29 10:46 | 显示全部楼层
本帖最后由 枫叶棋语 于 2023-8-22 21:23 编辑

同样我们可以调用python的opencv,scipy,matplot,这里就不一一展示,到这一步,我们就基本完成了cpython 开发CAD方法,同时避免了com方式
向CAD中添加图元

  1. from pycad.system import *
  2. from pycad.runtime import *
  3. from typing import *

  4.                
  5. @Command()
  6. def addline(doc):
  7.     pt1 = getpoint().value
  8.     pt2 = getpoint().value
  9.     line = Line(pt1,pt2)
  10.     with dbtrans(doc) as tr:
  11.         tr.addentity(tr.btr,line)

  12. @Command()
  13. def addcircle(doc):
  14.     center = getpoint().value
  15.     radius = getreal().value
  16.     cir = Circle()
  17.     cir.Center ,cir.Radius =center,radius
  18.     with dbtrans(doc) as tr:
  19.         tr.addentity(tr.btr,cir)
  20. import numpy as np
  21. @Command()
  22. def testnp(doc):
  23.     lst = np.array([1,2,3,4])
  24.     prinf(f"numpy数组{lst}")

复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2023-7-29 21:41 | 显示全部楼层
枫叶棋语 发表于 2023-7-29 00:47
这里面还是有一些问题需要处理,这个只是一个开始

我的pycad使用Ironpython3.4,除了c#构造一个简单的环境加载,其余所有功能全部由py代码构成,注册命令方式也不一样,目前方向往Cpython进行拓展
回复 支持 1 反对 0

使用道具 举报

发表于 2023-7-28 22:06 | 显示全部楼层
如果能用py第三方库,那就有有很多想象空间了,就看有没有人发掘出潜力让我涨涨知识,还有个期待是希望有人能把Javascript API重新带回cad

桌子好像是放弃Javascript API了

[飞马系列] AutoCAD 2014 之Javascript API编程初探
http://bbs.mjtd.com/forum.php?mo ... &fromuid=287566
(出处: 明经CAD社区)


发表于 2023-7-28 22:06 | 显示全部楼层
谢谢分享!认真学习,想自己搞一个自己熟悉的常用的工具
 楼主| 发表于 2023-7-29 00:47 | 显示全部楼层
caoliu023 发表于 2023-7-28 22:06
如果能用py第三方库,那就有有很多想象空间了,就看有没有人发掘出潜力让我涨涨知识,还有个期待是希望有人 ...

这里面还是有一些问题需要处理,这个只是一个开始
发表于 2023-7-29 15:46 | 显示全部楼层
不知道和版主的pycad有什么区别  http://bbs.mjtd.com/thread-180587-1-1.html

点评

在于能否使用Python第三方模块  发表于 2023-7-29 18:17
发表于 2023-7-30 10:42 | 显示全部楼层
edata 发表于 2023-7-29 15:46
不知道和版主的pycad有什么区别  http://bbs.mjtd.com/thread-180587-1-1.html

我还以为就是基于版主的pycad呢?如果不是,那枫总一开始所有能嵌入cad的ironpython是从哪来的呢?难道是自己用C#写了个dll,通过c#传递参数给ironpython并调用他?
 楼主| 发表于 2023-7-30 18:44 | 显示全部楼层
陨落 发表于 2023-7-30 10:42
我还以为就是基于版主的pycad呢?如果不是,那枫总一开始所有能嵌入cad的ironpython是从哪来的呢?难道是 ...

c#写了一个dll,实现cad能够加载运行第一个py脚本,其他功能全部用py脚本实现,相当于99%的Ironpython
发表于 2023-8-22 21:38 | 显示全部楼层
楼主也可以发网盘链接,发个百度网盘永久的链接
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-4-29 00:09 , Processed in 0.821143 second(s), 31 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表