【pycad】让Cpython在CAD中开发
本帖最后由 枫叶棋语 于 2023-8-22 22:37 编辑继上次的实验,我们初步实现了Ironpython环境加载Cpython环境,经过一番苦战,实现了在cad上运行Cpython,包括CAD注册命令,兼容Ipy脚本,本次更新实现了Cpython在CAD上运行,并依靠第三方模块开发的功能。相当于80%成品,因为本人能力有限,未能脱离借助Ironpython,直接使用c#调用Cpython,望后续有看到的兄弟助理一把。
这个是Cpython引擎
import clr
import sys
import os
clr.AddReference("Python.Runtime")
clr.AddReference("acmgd")
clr.AddReference("AcCoreMgd")
clr.AddReference("AcDbMgd")
clr.AddReference("System")
import System
import Autodesk.AutoCAD.ApplicationServices as acap
from Python.Runtime import *
dllpath = System.Reflection.Assembly.GetExecutingAssembly().Location
dlldir = os.path.dirname(dllpath)
supportdir = os.path.join(dlldir, "support")
extensionsdir = os.path.join(dlldir, "extensions")
referencedir = os.path.join(dlldir,"Reference")
class CpyEngine:
def __init__(self,pydll):
self.pydll =pydll
self.pyhome =os.path.dirname(pydll)
self.pypath = "{self.pyhome}\\Lib;{self.pyhome}\\Lib\\site-packages"
self.SetCpyEnviromnent()
self.Initilize()
@staticmethod
def prinf(msg):
acap.Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(f"\n{msg}\n")
def SetCpyEnviromnent(self):
'''配置Cpython环境变量'''
os.environ["PYTHONHOME"]=self.pyhome
os.environ["PYTHONNET_PYDLL"] = self.pydll
os.environ["PYTHONPATH"] = self.pypath
def Initilize(self):
'''初始化Cpython引擎'''
if not PythonEngine.IsInitialized:
PythonEngine.Initialize()
PythonEngine.PythonPath =self.pypath
def RunCpyScript(self,scriptpath):
'''运行脚本'''
import traceback
self.scope = Py.CreateScope()
pysys=Py.Import("sys")
pysys.path.append(dlldir)
pysys.path.append(supportdir)
pysys.path.append(extensionsdir)
pysys.path.append(referencedir)
with open(scriptpath,"r",encoding="utf-8") as file:
pythoncode= file.read()
with Py.GIL():
try:
self.scope.Exec(pythoncode)
except:
error =traceback.format_exc()
self.prinf(error)
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
import System
from System.Drawing import *
from System.Windows.Forms import *
import os
from pycad.system.acad import acap, prinf
from pycad.system.CpyEngine import CpyEngine
def check_and_create_file(filename):
'''判断文件是否存在,不存在就创建空文件'''
if not os.path.exists(filename):
open(filename, 'w')
class MainForm(Form):
def __init__(self):
self.InitializeComponent()
self.Resize += self.FormChanged
self.MinimumSize = Size(500, 500)
self.dllpath = System.Reflection.Assembly.GetExecutingAssembly().Location
self.dlldir = os.path.dirname(self.dllpath)
self.history = self.dlldir+"\CpyHistory.txt"
check_and_create_file(self.history)
self.HistoryPaths = self.ReadHitory()
self.ImportPaths(self.HistoryPaths)
self.engine = CpyEngine(r"D:\Program Files\Python310\python310.dll")
@staticmethod
def SetButton(button,anchor, x, y,name, text, func):
button.Anchor =anchor
button.Location = Point(x, y)
button.Size = Size(75, 25)
button.Name = name
button.Text = text
button.UseVisualStyleBackColor = True
button.Click += func
@staticmethod
def AddItem(listview: ListView, lst):
item = ListViewItem(items=tuple(lst))
listview.Items.Add(item)
def InitializeComponent(self):
self._Add = Button()
self._Delete = Button()
self._Clear = Button()
self._Apply = Button()
self._Cancel = Button()
self._Reload = Button()
self._ScriptPaths = ListView()
self._FileName = ColumnHeader()
self._FilePath = ColumnHeader()
self.Size = Size(500, 550)
self.ButtonWidth = 75
self.ButtonHeight = 25
self.ButtonX = 425
self.ListViewWidth =self.ButtonX-75
self.ListViewHeight =self.Size.Height -200
# ScriptPaths
self._ScriptPaths.View = View.Details
self._ScriptPaths.Anchor = AnchorStyles.Left|AnchorStyles.Top|AnchorStyles.Bottom|AnchorStyles.Right
self._ScriptPaths.Location = Point(30, 50)
self._ScriptPaths.Name = "ScriptPaths"
self._ScriptPaths.Size = Size(self.ListViewWidth,self.ListViewHeight)
self._ScriptPaths.GridLines =True
self._ScriptPaths.UseCompatibleStateImageBehavior = False
self._ScriptPaths.Columns.Add("文件")
self._ScriptPaths.Columns.Add("路径")
self._ScriptPaths.Columns.Width= 150
self._ScriptPaths.Columns.Width = self.ListViewWidth-150
self._ScriptPaths.BorderStyle = BorderStyle.FixedSingle
self._ScriptPaths.FullRowSelect =True
self.SetButton(self._Add, AnchorStyles.Right| AnchorStyles.Top,400,70, "Add", "添加", self.AddClick)
self.SetButton(self._Delete, AnchorStyles.Right| AnchorStyles.Top,400, 140, "Delete", "删除", self.DeleteClick)
self.SetButton(self._Clear,AnchorStyles.Right| AnchorStyles.Top,400, 210,"Clear","清空", self.ClearClick)
self.SetButton(self._Reload, AnchorStyles.Bottom|AnchorStyles.Right,200, 450,"Reload", "重载", self.ReloadClick)
self.SetButton(self._Apply,AnchorStyles.Bottom|AnchorStyles.Right,300, 450,"Apply","确定", self.ApplyClick)
self.SetButton(self._Cancel, AnchorStyles.Bottom|AnchorStyles.Right,400, 450, "Cancel","取消", self.CancelClick)
# 窗体添加控件
self.Controls.Add(self._ScriptPaths)
self.Controls.Add(self._Cancel)
self.Controls.Add(self._Apply)
self.Controls.Add(self._Clear)
self.Controls.Add(self._Reload)
self.Controls.Add(self._Delete)
self.Controls.Add(self._Add)
self.Text = "加载CPython脚本"
self.ResumeLayout(False)
#读取history文本内容
def ReadHitory(self):
with open(self.history,"r",encoding="utf-8") as file:
existing_paths = file.read().splitlines()
return existing_paths
#读取listview内容
def ReadListView(self):
paths = []
for item in self._ScriptPaths.Items:
path = item.SubItems.Text
paths.append(path)
return paths
#导入路径
def ImportPaths(self,paths):
for path in paths:
basename = os.path.basename(path)
self.AddItem(self._ScriptPaths,)
#导出路径
def ExportPaths(self,paths):
with open(self.history, "w") as file:
for item in self._ScriptPaths.Items:
path = item.SubItems.Text
if path not in existing_paths:
file.write(path + '\n')
def AddClick(self, sender, e):
openFileDialog = OpenFileDialog()
openFileDialog.Multiselect = True
if openFileDialog.ShowDialog() == DialogResult.OK:
self._ScriptPaths.Items.Clear()
file_names = openFileDialog.FileNames
result = set(file_names)|set(self.HistoryPaths)
self.ImportPaths(result)
self.HistoryPaths =list(result)
for path in file_names:
self.engine.RunCpyScript(path)
def DeleteClick(self, sender, e):
selected_items = self._ScriptPaths.SelectedItems
for item in selected_items:
self._ScriptPaths.Items.Remove(item)
self.HistoryPaths =self.ReadListView()
def ClearClick(self, sender, e):
self._ScriptPaths.Items.Clear()
self.HistoryPaths = []
def ApplyClick(self, sender, e):
with open(self.history, "w") as file:
for path in self.HistoryPaths:
file.write(f"{path}\n")
self.Close()
def ReloadClick(self, sender, e):
selected_items = self._ScriptPaths.SelectedItems
for item in selected_items:
path = item.SubItems.Text
self.engine.RunCpyScript(path)
def CancelClick(self, sender, e):
self.Close()
def FormChanged(self, sender, e):
self._ScriptPaths.Columns.Width = self.Size.Width - 200
loadform = MainForm()
acap.Application.ShowModelessDialog(loadform)
与此同时,我们建立一个CpyReload脚本,用于命令重载所有脚本命令,代码如下:
from pycad.system import prinf
from pycad.system.CpyEngine import CpyEngine
import System
import os,traceback
import re
def check_and_create_file(filename):
'''判断文件是否存在,不存在就创建空文件'''
if not os.path.exists(filename):
open(filename, 'w')
class Reload:
def __init__(self):
self.engine = CpyEngine(r"D:\Program Files\Python310\python310.dll")
self.dllpath = System.Reflection.Assembly.GetExecutingAssembly().Location
self.dlldir = os.path.dirname(self.dllpath)
self.history = self.dlldir+"\CpyHistory.txt"
check_and_create_file(self.history)
self.reload()
def reload(self):
with open(self.history,"r",encoding="utf-8") as file:
paths = file.read().splitlines()
for path in paths:
self.engine.RunCpyScript(path)
Reload()至此,CpyCAD的框架的python代码基本搭设完成,由于权限问题,暂时不能上传文件。有需要的可以联系。
本帖最后由 枫叶棋语 于 2023-8-22 21:23 编辑
同样我们可以调用python的opencv,scipy,matplot,这里就不一一展示,到这一步,我们就基本完成了cpython 开发CAD方法,同时避免了com方式
向CAD中添加图元
from pycad.system import *
from pycad.runtime import *
from typing import *
@Command()
def addline(doc):
pt1 = getpoint().value
pt2 = getpoint().value
line = Line(pt1,pt2)
with dbtrans(doc) as tr:
tr.addentity(tr.btr,line)
@Command()
def addcircle(doc):
center = getpoint().value
radius = getreal().value
cir = Circle()
cir.Center ,cir.Radius =center,radius
with dbtrans(doc) as tr:
tr.addentity(tr.btr,cir)
import numpy as np
@Command()
def testnp(doc):
lst = np.array()
prinf(f"numpy数组{lst}")
枫叶棋语 发表于 2023-7-29 00:47
这里面还是有一些问题需要处理,这个只是一个开始
我的pycad使用Ironpython3.4,除了c#构造一个简单的环境加载,其余所有功能全部由py代码构成,注册命令方式也不一样,目前方向往Cpython进行拓展 如果能用py第三方库,那就有有很多想象空间了,就看有没有人发掘出潜力让我涨涨知识,还有个期待是希望有人能把Javascript API重新带回cad
桌子好像是放弃Javascript API了
[飞马系列] AutoCAD 2014 之Javascript API编程初探
http://bbs.mjtd.com/forum.php?mod=viewthread&tid=108034&fromuid=287566
(出处: 明经CAD社区)
谢谢分享!认真学习,想自己搞一个自己熟悉的常用的工具 caoliu023 发表于 2023-7-28 22:06
如果能用py第三方库,那就有有很多想象空间了,就看有没有人发掘出潜力让我涨涨知识,还有个期待是希望有人 ...
这里面还是有一些问题需要处理,这个只是一个开始 不知道和版主的pycad有什么区别http://bbs.mjtd.com/thread-180587-1-1.html 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 10:42
我还以为就是基于版主的pycad呢?如果不是,那枫总一开始所有能嵌入cad的ironpython是从哪来的呢?难道是 ...
c#写了一个dll,实现cad能够加载运行第一个py脚本,其他功能全部用py脚本实现,相当于99%的Ironpython 楼主也可以发网盘链接,发个百度网盘永久的链接
页:
[1]
2