TheSwamp
Code Red => .NET => Topic started by: lightname on August 22, 2008, 10:19:15 AM
-
Couldn't someone use IronPython to program AutoCAD?
I've seen examples programming using F# (by Kean Walmsley, http://through-the-interface.typepad.com/).
If yes, could someone post an example?
I know that there are some examples programming Autocad with Python/IronPython, but they don't do any real drawing they only pop up windows or count layers.
I'd like very much like to see an example of drawing a line or a circle.
-
'Poping up windows or counting layers' is conceptually no different to drawing a line or circle really.
-
Have you had a look at tjr's (Tim's) Pyacad.NET (http://code.google.com/p/pyacaddotnet/) yet? It should be just what you're looking for.
It uses IronPython and the acad .net arx managed wrappers.
cheers.
-
Yes I think it can be done. But one may need to write a few more helper classes in C#.
One issue I found is you can’t open objects for write with ironpython (I don’t know why).
But it would be easy enough for one to write helper functions for adding objects to records or the database
Another issue I found is that there is no casting in Python, so if one were to use a method like
tr.GetObject(id , AcDb.OpenMode.ForRead ,False)
what type of object does python see? How would one cast from a DbObject to a BlockTableRecord?
Again one could write helper functions to get objects ie GetBlockTableRecord(…);
So in the end, it certainly is possible
maybe someone could look at my attemp and point out my errors
#thanks to kwb for the C# code to translate
import System
import Autodesk.AutoCAD.DatabaseServices as AcDb
import Autodesk.AutoCAD.Geometry as AcGe
import Autodesk.AutoCAD.ApplicationServices as AcAp
try:
ed = AcAp.Application.DocumentManager.MdiActiveDocument.Editor
ed.WriteMessage("\n Hello")
db = AcDb.HostApplicationServices.WorkingDatabase
tm = db.TransactionManager
tr = tm.StartTransaction()
line = AcDb.Line(AcGe.Point3d(0,0,0),AcGe.Point3d(100,100,0))
id = AcDb.SymbolUtilityServices.GetBlockModelSpaceId(db)
# fails to open
# can I cast this way???
btrArray = System.Array[AcDb.BlockTableRecord]([tr.GetObject(id , AcDb.OpenMode.ForWrite ,False)])
# btrArray = System.Array[AcDb.BlockTableRecord]([tr.GetObject(id , AcDb.OpenMode.ForRead ,False)])
# btrArray = System.Array.CreateInstance(AcDb.BlockTableRecord, 1)
# btrArray[0] = tr.GetObject(id , AcDb.OpenMode.ForRead ,False)
btrArray[0].AppendEntity(line)
tr.AddNewlyCreatedDBObject(line, true)
tr.Commit()
tr.Dispose()
except:
tr.Dispose()
line.Dispose()
-
Looks like I'm going to have to fire up my windows laptop and have a look at this. I don't see what would prevent one from opening the block table for write, I'll have to take a look at that.
Another issue I found is that there is no casting in Python, so if one were to use a method like
tr.GetObject(id , AcDb.OpenMode.ForRead ,False)
what type of object does python see? How would one cast from a DbObject to a BlockTableRecord?
Try something like this to find out the objects type. If you're using PyAcad.NET it should just write it to the command line.
i = tr.GetObject(id , AcDb.OpenMode.ForRead ,False)
print type(i)
-
lightname:
Sorry you weren't pleased with my other examples. You owe me a beer. :)
Warning: This is ugly code that was hacked up very quickly. It's Saturday, I have tons of yard work to do and I don't have the patience to make this look good. But it is functional if you use my PyAcad.NET package.
#Draws a circle.
#Note: This code is very sloppy. Just an example of how one would
# draw a circle. Not good coding style.
import Autodesk.AutoCAD.DatabaseServices as dbs
import Autodesk
import Autodesk.AutoCAD.Geometry as geo
doclock = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.LockDocument()
db = dbs.HostApplicationServices.WorkingDatabase
tm = db.TransactionManager
tr = tm.StartTransaction()
bt = tm.GetObject(dbs.HostApplicationServices.WorkingDatabase.BlockTableId, dbs.OpenMode.ForRead)
btr = tm.GetObject(dbs.HostApplicationServices.WorkingDatabase.CurrentSpaceId, dbs.OpenMode.ForWrite )
circle = dbs.Circle(geo.Point3d(10,10,0),geo.Vector3d.ZAxis, 2)
btr.AppendEntity(circle)
tm.AddNewlyCreatedDBObject(circle, True)
tr.Commit()
btr.Dispose()
bt.Dispose()
tr.Dispose()
tm.Dispose()
db.Dispose()
doclock.Dispose()
-
One issue I found is you can’t open objects for write with ironpython (I don’t know why).
Just a note. I was able to get around this by locking the document. See my code above.
-
One issue I found is you can’t open objects for write with ironpython (I don’t know why).
Just a note. I was able to get around this by locking the document. See my code above.
Ah! Excellent 8-)
-
my first working Python code :lol:
#thanks to kwb for the C# code to translate
import System
import Autodesk.AutoCAD.DatabaseServices as AcDb
import Autodesk.AutoCAD.Geometry as AcGe
import Autodesk.AutoCAD.ApplicationServices as AcAp
try:
ed = AcAp.Application.DocumentManager.MdiActiveDocument.Editor
doclock = AcAp.Application.DocumentManager.MdiActiveDocument.LockDocument()
db = AcDb.HostApplicationServices.WorkingDatabase
tm = db.TransactionManager
tr = tm.StartTransaction()
line = AcDb.Line(AcGe.Point3d(0,0,0),AcGe.Point3d(100,100,0))
id = AcDb.SymbolUtilityServices.GetBlockModelSpaceId(db)
btr = tr.GetObject(id , AcDb.OpenMode.ForWrite ,False)
btr.AppendEntity(line)
tr.AddNewlyCreatedDBObject(line, True)
tr.Commit()
tr.Dispose()
doclock.Dispose()
ed.WriteMessage("\n done")
except:
tr.Dispose()
line.Dispose()
doclock.Dispose()
-
In case it wasn't already know.
using Autodesk.AutoCAD.DatabaseServices;
Would translate to:
from Autodesk.AutoCAD.DatabaseServices import *
Then you wouldn't have to use the 'AcDb' stuff if you didn't want to. I personally prefer it.
-
my first working Python code :lol:
#thanks to kwb for the C# code to translate
import System
import Autodesk.AutoCAD.DatabaseServices as AcDb
import Autodesk.AutoCAD.Geometry as AcGe
import Autodesk.AutoCAD.ApplicationServices as AcAp
try:
ed = AcAp.Application.DocumentManager.MdiActiveDocument.Editor
doclock = AcAp.Application.DocumentManager.MdiActiveDocument.LockDocument()
db = AcDb.HostApplicationServices.WorkingDatabase
tm = db.TransactionManager
tr = tm.StartTransaction()
line = AcDb.Line(AcGe.Point3d(0,0,0),AcGe.Point3d(100,100,0))
id = AcDb.SymbolUtilityServices.GetBlockModelSpaceId(db)
btr = tr.GetObject(id , AcDb.OpenMode.ForWrite ,False)
btr.AppendEntity(line)
tr.AddNewlyCreatedDBObject(line, True)
tr.Commit()
tr.Dispose()
doclock.Dispose()
ed.WriteMessage("\n done")
except:
tr.Dispose()
line.Dispose()
doclock.Dispose()
Dan:
Very nice but you can't you try except like that, it makes it impossible to debug.
-
Yeah, I am not sure about how to do this in Python, but in the case of an exception, the transaction must be closed or AutoCAD will crash hard,
doing it the way I did stopped that . Since there using no using statement in Python, maybe someone can post an example of try catch finally
-
<from the Py doc's> is this what you need?
>>> def divide(x, y):
... try:
... result = x / y
... except ZeroDivisionError:
... print "division by zero!"
... else:
... print "result is", result
... finally:
... print "executing finally clause"
...
>>> divide(2, 1)
result is 2
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'
-
Cool Thanks, Well now that I can draw a line in AutoCAD with twelve different programming languages, I guess I need to pick one and get some work done :laugh:
-
Cool Thanks, Well now that I can draw a line in AutoCAD with twelve different programming languages, I guess I need to pick one and get some work done :laugh:
Here's another one for you to learn how to draw a line in AutoCAD in http://www.codeplex.com/IronScheme ;-)
So you can put off getting some work done a little longer.
-
i tried to draw a circle with COM via PyAcad.Net and it worked.
from System import Array,Type
import clr
clr.AddReferenceToFileAndPath(r"c:\Program Files\IronPython\Tutorial\AutoCAD.dll") #path to the assembly generated from acax17enu.tlb by tlbimp.exe from netsdk
from AutoCAD import *
import Autodesk.AutoCAD.ApplicationServices as ap
doc = ap.Application.DocumentManager.MdiActiveDocument.AcadDocument
acadApp = doc.Application
doc.ActiveSpace == AcActiveSpace.acModelSpace
pnt = Array.CreateInstance(Type.GetType("System.Double"),3)
for i,coord in enumerate(doc.Utility.GetPoint(Prompt = "\ncenter of the circle: ")):
pnt.SetValue(coord,i)
circle = doc.ModelSpace.AddCircle(pnt,100)
circle.color = ACAD_COLOR.acMagenta
acadApp.ZoomExtents()
-
i tried to draw a circle with COM via PyAcad.Net and it worked.
...
Ah You’re Awesome! 8-)
…And Welcome To theSwamp :-)
-
i tried to draw a circle with COM via PyAcad.Net and it worked.
from System import Array,Type
import clr
clr.AddReferenceToFileAndPath(r"c:\Program Files\IronPython\Tutorial\AutoCAD.dll") #path to the assembly generated from acax17enu.tlb by tlbimp.exe from netsdk
from AutoCAD import *
import Autodesk.AutoCAD.ApplicationServices as ap
doc = ap.Application.DocumentManager.MdiActiveDocument.AcadDocument
acadApp = doc.Application
doc.ActiveSpace == AcActiveSpace.acModelSpace
pnt = Array.CreateInstance(Type.GetType("System.Double"),3)
for i,coord in enumerate(doc.Utility.GetPoint(Prompt = "\ncenter of the circle: ")):
pnt.SetValue(coord,i)
circle = doc.ModelSpace.AddCircle(pnt,100)
circle.color = ACAD_COLOR.acMagenta
acadApp.ZoomExtents()
Very nice work.
Also very nice to see some people doing some exploring with the PyAcad.NET code.
-
Something like this would work for Bricscad as well
-
Thank you. I'm going to rewrite some VBA examples (AutoCAD ActiveX and VBA Reference) in Python later in my spare time
I think PyAcad.Net is cool but
we need full-featured IronPython IDE (with visual UI designer) integrated in AutoCAD. A script launcher is not enough.
-
There are a couple of options for an IronPython IDE, SharpDevelop has an IronPython addin and you can also integrate IronPython into VS2005 and VS2008 (you can just ue the vs2008 ide shell for free for this one).
I have a few links at work, I'll post them tomorrow if you're interested.
-
Thank you. I'm going to rewrite some VBA examples (AutoCAD ActiveX and VBA Reference) in Python later in my spare time
I think PyAcad.Net is cool but
we need full-featured IronPython IDE (with visual UI designer) integrated in AutoCAD. A script launcher is not enough.
As Mick said you can always visually layout your UI using SharDevelop 3.0 which as IronPython support. You could also create all the from skeleton in C# or VB.NET, compile it to a dll and inherit it in an IronPython class. That is very trivial.
Also if anyone has the time and wants to try their hand at a simple pyacad.net script editor that runs in AutoCAD as a separate project I certainly wouldn't object. Might even be easy with dotnetfireball (http://www.codeplex.com/dotnetfireball).
-
After looking at the code, I think it would be easy enough for Tim to add references to AutoCAD’s COM Api, so you wouldn’t have to do this
clr.AddReferenceToFileAndPath(…)
-
Check this out, I added a new layout “MyLayout” to Bricscad, using this code, my DRXNET and PyBCadDotNet :laugh:
#thanks to kwb just because
import System
import BricscadApp as OdAp
import BricscadDb as OdDb
import DRXNET.OdAp as ObApB
application = ObApB.Application.AcadApplication
document = application.ActiveDocument
myLayout = document.Layouts.Add("MyLayout")
-
and a line :-o
#thanks to kwb
import System
import BricscadApp as OdAp
import BricscadDb as OdDb
import DRXNET.OdAp as ObApB
from System import Array,Type
application = ObApB.Application.AcadApplication
document = application.ActiveDocument;
pt1 = Array.CreateInstance(Type.GetType("System.Double"),3)
pt1.SetValue(0, 0)
pt1.SetValue(0, 1)
pt1.SetValue(0, 2)
pt2 = Array.CreateInstance(Type.GetType("System.Double"),3)
pt2.SetValue(100, 0)
pt2.SetValue(100, 1)
pt2.SetValue(0, 2)
line = document.ModelSpace.AddLine(pt1,pt2);
line.Update();
-
After looking at the code, I think it would be easy enough for Tim to add references to AutoCAD’s COM Api, so you wouldn’t have to do this
clr.AddReferenceToFileAndPath(…)
I'm still having trouble understanding why anyone would want to get at AutoCAD through COM in PyAcad.NET when you have the entire .NET API available to you.
-
Check this out, I added a new layout “MyLayout” to Bricscad, using this code, my DRXNET and PyBCadDotNet :laugh:
#thanks to kwb just because
import System
import BricscadApp as OdAp
import BricscadDb as OdDb
import DRXNET.OdAp as ObApB
application = ObApB.Application.AcadApplication
document = application.ActiveDocument
myLayout = document.Layouts.Add("MyLayout")
This is a perfect example of the kind of stuff I love to see. It's the true hacker spirit.
You never cease to amaze or inspire Dan.
-
After looking at the code, I think it would be easy enough for Tim to add references to AutoCAD’s COM Api, so you wouldn’t have to do this
clr.AddReferenceToFileAndPath(…)
I'm still having trouble understanding why anyone would want to get at AutoCAD through COM in PyAcad.NET when you have the entire .NET API available to you.
Why not? For simple scripts, like adding a layout, it’s much easier to use COM. While I don’t use COM much, I know it’s in the toolbox
-
Check this out, I added a new layout “MyLayout” to Bricscad, using this code, my DRXNET and PyBCadDotNet :laugh:
#thanks to kwb just because
import System
import BricscadApp as OdAp
import BricscadDb as OdDb
import DRXNET.OdAp as ObApB
application = ObApB.Application.AcadApplication
document = application.ActiveDocument
myLayout = document.Layouts.Add("MyLayout")
This is a perfect example of the kind of stuff I love to see. It's the true hacker spirit.
You never cease to amaze or inspire Dan.
Thanks I just wanted to see If I could do it :-)
-
here is ths C# code
-
After looking at the code, I think it would be easy enough for Tim to add references to AutoCAD’s COM Api, so you wouldn’t have to do this
clr.AddReferenceToFileAndPath(…)
I'm still having trouble understanding why anyone would want to get at AutoCAD through COM in PyAcad.NET when you have the entire .NET API available to you.
Why not? For simple scripts, like adding a layout, it’s much easier to use COM. While I don’t use COM much, I know it’s in the toolbox
I see your point, and it goes against what I had envisioned for PyAcad.NET, but if it's a direction people want to go then I guess it wouldn't hurt to add.
My vision for PyAcad.NET was first and foremost the thin C#/IronPython Wrapper followed by a pure python library for making it easy to work with the .NET API. For example if one wanted to draw a circle like I posted previously all they would need to do was something like:
import pyacad
pyacad.geo.draw_circle((5.55, 6.21, 0), (100,100,0))
-
My vision for PyAcad.NET was first and foremost the thin C#/IronPython Wrapper followed by a pure python library for making it easy to work with the .NET API. For example if one wanted to draw a circle like I posted previously all they would need to do was something like:
import pyacad
pyacad.geo.draw_circle((5.55, 6.21, 0), (100,100,0))
Ah! I see, so in the end it will be a simple interface to the more complex .NET API.
It’s a great idea.
As far as Bricscad goes, I have only wrapped the 100 or so SDS functions, ie , entget, entmake…
I have started wrapping the DRX geometry classes as they seemed to be the best place to start. It’s a big API so it might take a while
so com and sds are the only choice now
-
OT -
That's why I'm keen on wrapping the api (ARX/BRX) to straight python, Bricscad is not likely to go .net in a hurry and if they go native linux... :)
\OT
-
My vision for PyAcad.NET was first and foremost the thin C#/IronPython Wrapper followed by a pure python library for making it easy to work with the .NET API. For example if one wanted to draw a circle like I posted previously all they would need to do was something like:
import pyacad
pyacad.geo.draw_circle((5.55, 6.21, 0), (100,100,0))
Ah! I see, so in the end it will be a simple interface to the more complex .NET API.
It’s a great idea.
Yes so in the end it would provide a simple interface for executing common tasks but still give them the ability to break away and hack at the .NET api in IronPython, C# or VB.NET if they needed/wanted to. Yet all would usable from the IronPython. IronPython is really an awesome package if you dig into it.
As far as Bricscad goes, I have only wrapped the 100 or so SDS functions, ie , entget, entmake…
I have started wrapping the DRX geometry classes as they seemed to be the best place to start. It’s a big API so it might take a while
so com and sds are the only choice now
Keep at it. In my eyes bringing the .NET API to another platform is more than a big deal.
-
OT -
That's why I'm keen on wrapping the api (ARX/BRX) to straight python, Bricscad is not likely to go .net in a hurry and if they go native linux... :)
\OT
Mick:
Don't kid yourself. You know if Bricscad were to go native linux you'd be one of the first in line hacking away trying to get Dan's .NET stuff working with Mono (http://www.mono-project.com/Main_Page). It's in you and you know you wouldn't/couldn't turn down the challenge. :)
-
I don't know about that Tim, call me old fashioned, but give me plain C and something to script it with and you have a winning combination, only my opinion of course :)
-
It seems to be impossible to pass a python function pointer to Autocad because IronPython doesn't produce normal msil assemblies. I think this is the reason why exceptions occurs in Autocad when you run a command after execution of this script:
import clr
import System
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.EditorInput import *
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.Geometry import *
doc = Application.DocumentManager.MdiActiveDocument
ed = doc.Editor
db = doc.Database
def docCommandWillStart(sender,e):
print 'hello'
doc.CommandWillStart += CommandEventHandler(docCommandWillStart)
How can you say entire Net api is available if you can't use reactors?
-
It seems to be impossible to pass a python function pointer to Autocad because IronPython doesn't produce normal msil assemblies. I think this is the reason why exceptions occurs in Autocad when you run a command after execution of this script:
import clr
import System
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.EditorInput import *
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.Geometry import *
doc = Application.DocumentManager.MdiActiveDocument
ed = doc.Editor
db = doc.Database
def docCommandWillStart(sender,e):
print 'hello'
doc.CommandWillStart += CommandEventHandler(docCommandWillStart)
How can you say entire Net api is available if you can't use reactors?
PyAcad.NET doesn't generate any .NET assemblies from IronPython so that's not the issue here. I haven't tried doing reactors at all either. I will explore and get back to you.
-
One more problem with pyacad.net
I cannot define a subclass of the arx.net api classes:
from Autodesk.AutoCAD.EditorInput import *
class BlockJig(EntityJig):
pass
Exception occurs
What's wrong?
-
One more problem with pyacad.net
I cannot define a subclass of the arx.net api classes:
from Autodesk.AutoCAD.EditorInput import *
class BlockJig(EntityJig):
pass
Exception occurs
What's wrong?
The way PyAcad.net is written now, Python code is executed then immediately disposed of.
Any code that needs recourses from the executed code after the code is disposed of will throw an exception. This is why your reactors failed and maybe why your new jig class does not work.
I think some of the work Mick is doing may resolve some of these issues.
PS: You seem very knowledgeable about both Python and .NET and since this is an open source project, you might want to have a look and see if there is something others have missed
-
One more problem with pyacad.net
I cannot define a subclass of the arx.net api classes:
from Autodesk.AutoCAD.EditorInput import *
class BlockJig(EntityJig):
pass
Exception occurs
What's wrong?
The way PyAcad.net is written now, Python code is executed then immediately disposed of.
Any code that needs recourses from the executed code after the code is disposed of will throw an exception. This is why your reactors failed and maybe why your new jig class does not work.
I think some of the work Mick is doing may resolve some of these issues.
PS: You seem very knowledgeable about both Python and .NET and since this is an open source project, you might want to have a look and see if there is something others have missed
Unfortunately, I know very little about programming, I'm not realy a programmer, I'm just a CAD user, who wants to customize AutoCAD in the most effective way. And I'm not fluent in English and have to use online translators. I doubt I can help PyAcad.net developers.
I think also that to make IronPython work with AutoCAD they have to modify it's source code. I don't know if it's legal and anyway it will demand thorough knowledges of theory of compilers, MSIL and so on.
I only hope PyAcad.net will be finally a success
-
I understand, just so you know how the project evolved, We thought it would be nice if the code could run dynamically, I.e. without having to restart AutoCAD every time as you do when testing C# modules. This is an unforeseen problem, but I am sure it will be resolved. :-)
-
I understand, just so you know how the project evolved, We thought it would be nice if the code could run dynamically, I.e. without having to restart AutoCAD every time as you do when testing C# modules. This is an unforeseen problem, but I am sure it will be resolved. :-)
I see. I read that Python is good for prototyping.
There is one method you probably know. With the help of PyAcad.Net:
import clr
from System.CodeDom.Compiler import CompilerParameters
from Microsoft.CSharp import CSharpCodeProvider
from System.IO import Path
pathToSource = ur'D:\Documents\through-the-interface.typepad.com\Samples\ListAttributes.cs'
cp = CompilerParameters()
cp.GenerateInMemory = True
cp.GenerateExecutable = False
pathToArxInc = r"c:\Program Files\ObjectARX 2008\inc"
for r in ['AcDbMgd.dll','AcMgd.dll']:
cp.ReferencedAssemblies.Add(Path.Combine(pathToArxInc,r))
cp.ReferencedAssemblies.Add('System.Windows.Forms.dll')
provider = CSharpCodeProvider()
cr = provider.CompileAssemblyFromFile( cp, pathToSource );
if cr.Errors.HasErrors:
print 'error compiling'
for ce in cr.Errors:
print ce.ToString()
else:
clr.AddReference(cr.CompiledAssembly)
You can modify the source file and run this script again without a need to restart AutoCAD.
I think that another possible way is to use AppDomain class, but I don't know how to.