Danielm103 Posted 3 hours ago Posted 3 hours ago (edited) When you call a method against an entity in Lisp or ActiveX, AutoCAD manages the state of the entity for you. The sequence is: - Lock the document - Open the Object kForRead/kForWrite - Perform the operation - Close the Object - Unlock the document The overhead starts to build up if you’re setting lots of dbobject Properties Unlike Lisp or ActiveX, Db.DbObject, in Python, .NET, and ObjectARX you have to manage the object’s lifetime. .NET mainly uses the transaction manager for this, I won’t be getting into that Writing in Python(PyRx) is almost identical to modern C++ in that both systems use smart pointers to help manage the state for you. Consider C++ static void AcRxPyApp_idoit(void) { auto [ps, id, pnt] = entsel(L"\nSelect Entity: "); AcDbEntityPointer pEnt(id, AcDb::OpenMode::kForWrite); pEnt->setLayer(L"0"); } vs Python def doit(): try: ps, id, pnt = Ed.Editor.entSel("\nSelect Entity: ") ent = Db.Entity(id,Db.OpenMode.kForWrite) ent.setLayer("0") except Exception as err: traceback.print_exception(err) In Both C++ and Python, ent (pEnt) is closed at the end of the function scope Edited 3 hours ago by Danielm103 Quote
Danielm103 Posted 2 hours ago Author Posted 2 hours ago The Pros: You can leave the entities open for longer, set multiple properties without the overhead of multiple open/close. Consider adding a large number of entities to modelspace, with PyRx, you can leave modelspace opened for write for the entire operation @Ap.Command() def doit(): try: db = Db.curDb() space = Db.BlockTableRecord(db.currentSpaceId(), Db.OpenMode.kForWrite) for idx in range(20): nidx = idx + 100 space.appendAcDbEntity( Db.Line(Ge.Point3d(idx, idx, idx), Ge.Point3d(nidx, nidx, nidx))) except Exception as err: traceback.print_exception(err) Quote
Danielm103 Posted 2 hours ago Author Posted 2 hours ago The Cons: You have to consider the states of the objects when writing your code, for example function dobad throws an exception Db.ErrorStatusException: Exception!(eWasOpenForRead) because space and model are the same object, to be opened in different states @Ap.Command() def dobad(): try: db = Db.curDb() space = Db.BlockTableRecord(db.modelSpaceId()) for id in space.objectIds(Db.Line.desc()): line = Db.Line(id) # ...... model = Db.BlockTableRecord(db.modelSpaceId(), Db.OpenMode.kForWrite) model.appendAcDbEntity( Db.Line(Ge.Point3d(0, 0, 0), Ge.Point3d(100, 100, 0))) #Db.ErrorStatusException: Exception!(eWasOpenForRead) except Exception as err: traceback.print_exception(err) @Ap.Command() def dogood(): try: db = Db.curDb() space = Db.BlockTableRecord(db.modelSpaceId()) for id in space.objectIds(Db.Line.desc()): line = Db.Line(id) # ...... space.upgradeOpen() # change to write space.appendAcDbEntity( Db.Line(Ge.Point3d(0, 0, 0), Ge.Point3d(100, 100, 0))) except Exception as err: traceback.print_exception(err) @Ap.Command() def dobest(): try: db = Db.curDb() readlines(db) #... writelines(db) except Exception as err: traceback.print_exception(err) def readlines(db): space = Db.BlockTableRecord(db.modelSpaceId()) for id in space.objectIds(Db.Line.desc()): line = Db.Line(id) def writelines(db): model = Db.BlockTableRecord(db.modelSpaceId(), Db.OpenMode.kForWrite) model.appendAcDbEntity( Db.Line(Ge.Point3d(0, 0, 0), Ge.Point3d(100, 100, 0))) best practice is to separate out operations if you run into conflicts Quote
Danielm103 Posted 2 hours ago Author Posted 2 hours ago Checking Db.Object types and casting Every database object has a static description Db.Line.desc() https://help.autodesk.com/view/OARX/2025/ENU/?guid=OARX-RefGuide-AcRxObject__desc Every database object has an instance method to verify the type https://help.autodesk.com/view/OARX/2025/ENU/?guid=OARX-RefGuide-AcRxObject__isA examples: @Ap.Command() def doit(): try: # check types line = Db.Line() print(line.isA() == Db.Line.desc()) print(line.isA().isDerivedFrom(Db.Line.desc())) print(line.isKindOf(Db.Line.desc())) #check types print(line.isA().isDerivedFrom(Db.Curve.desc())) print(line.isKindOf(Db.Curve.desc())) ps, id, pnt = Ed.Editor.entSel("\nSelect Entity: ") curve = Db.Curve(id) if curve.isKindOf(Db.Line.desc()): #PyRx does not check in the cast cline = Db.Line.cast(curve) print(line.isA() == Db.Line.desc()) print(line.isA().isDerivedFrom(Db.Line.desc())) print(line.isKindOf(Db.Line.desc())) print(line.isA().isDerivedFrom(Db.Curve.desc())) print(line.isKindOf(Db.Curve.desc())) except Exception as err: traceback.print_exception(err) Command: DOIT True True True True True Select Entity: True True True True True Quote
Danielm103 Posted 2 hours ago Author Posted 2 hours ago Note with casting, the cast internally increases the reference count to the same C++ pointer. If you close one, the other will be closed. Like entangled particles, if Alice changes something, Bob will observe the change line = Db.Line() curve = Db.Curve.cast(line) print(line,curve) #<PyDb.Line object at 0x000001FB834B5940> <PyDb.Curve object at 0x000001FB83577240> Quote
Danielm103 Posted 1 hour ago Author Posted 1 hour ago (edited) Best practice for checking types is to use the ObjectId as the AcRxClass is cached when the drawing is opened in CAD. it’s much faster than opening the object db = Db.curDb() for id in db.modelSpace(): if id.isDerivedFrom(Db.Line.desc()): # check the id line = Db.Line(id) Edited 1 hour ago by Danielm103 Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.