Hi all,
I have a little project to take care of that requires a batch of drawings to have their drawing frame blocks updated & attributes amended/truncated to a specific version and I'd rather not reinvent the wheel by resorting to programming anything using .NET.
I've searched the internet/forums and stumbled across a couple of threads/documents:
http://www.widom-assoc.com/AU-CP12-3L.pdf
None of which do exactly what I need.
What is the "from the horses mouth" workflow/method I should take? Data Extraction Tool/ATTOUT/Another approach I haven't thought of?
Thanks,
Alex.
Hi Henrique,
That's exactly the same realisation I just came to, except I simply put false where you have nil.
Thanks again one and all. Most of these past couple of posts are classic "should-have-talked-to-the-bear" moments.
Doh!
No doubt I'll be back with more questions later. 🙂
BTW here's my routine that calls the bns_attin command:
(defun c:atin (/ dn pa padn ss) (vl-load-com) (load "attout") (setq dn (vl-filename-base (getvar "dwgname"))) (setq pa (getvar "dwgprefix")) (setq padn (strcat pa dn ".txt")) ;(setq ss (ssget "_X" (list (cons 0 "INSERT")'(66.1)))) (bns_attin padn false) )
Okay, for my next question I need to call a .NET LispFunction I borrowed from here:
http://www.theswamp.org/index.php?topic=42890.0
(specifically the code posted by ChillMe1):
Imports System Imports System.Text Imports System.IO Imports System.Data Imports System.Linq Imports Autodesk.AutoCAD.Runtime Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.EditorInput Imports Autodesk.AutoCAD.Geometry Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.Interop Imports Autodesk.AutoCAD Imports Autodesk.AutoCAD.ApplicationServices.Application Imports Autodesk.AutoCAD.LayerManager Imports Autodesk.AutoCAD.Windows ' This line is not mandatory, but improves loading performances <Assembly: CommandClass(GetType(TransferAttributes.MyCommands))> Namespace TransferAttributes ' This class is instantiated by AutoCAD for each document when ' a command is called by the user the first time in the context ' of a given document. In other words, non static data in this class ' is implicitly per-document! Public Class MyCommands <CommandMethod("UpdateAtts")> _ Public Sub UpdateAtts() Dim myDoc As Document = Application.DocumentManager.MdiActiveDocument Dim myEd As Editor = myDoc.Editor Dim selEntID As ObjectId = myEd.GetEntity("Select Old Block:").ObjectId Dim selEntID2 As ObjectId '= myEd.GetEntity("Select New block:").ObjectId Dim myXML As New Xml.XmlDocument() myXML.Load("c:\i-cad\UpdateAtts_A.xml") Using myTrans As Transaction = myDoc.Database.TransactionManager.StartTransaction Dim myBrefA As BlockReference = selEntID.GetObject(OpenMode.ForWrite) Dim myBrefB As BlockReference '= selEntID2.GetObject(OpenMode.ForWrite) Dim myAttsA As AttributeCollection = myBrefA.AttributeCollection Dim myAttsB As AttributeCollection '= myBrefB.AttributeCollection Dim blockNameA As String = "" Dim blockNameB As String = "" If myBrefA.Name.StartsWith("*") Then Dim myBTR As BlockTableRecord = myBrefA.DynamicBlockTableRecord.GetObject(OpenMode.ForRead) blockNameA = myBTR.Name Else blockNameA = myBrefA.Name End If Dim oldNode As Xml.XmlNode = myXML.SelectSingleNode("//oldblock[@name='" & blockNameA.ToUpper & "']") Dim myBT As BlockTable = myDoc.Database.BlockTableId.GetObject(OpenMode.ForWrite) If myBT.Has(oldNode.Attributes("newname").Value) = False Then 'insert DWG file as a Block Dim myDWG As New IO.FileInfo(oldNode.Attributes("path").Value) If myDWG.Exists = False Then MsgBox("The file " & myDWG.FullName & " does not exist.") Exit Sub End If 'Create a blank Database Dim dwgDB As New Database(False, True) 'Read a DWG file into the blank database dwgDB.ReadDwgFile(myDWG.FullName, FileOpenMode.OpenForReadAndAllShare, True, "") 'insert the dwg file into the current file's block table myDoc.Database.Insert(oldNode.Attributes("newname").Value.ToUpper, dwgDB, True) 'close/dispose of the previously blank database. dwgDB.Dispose() End If selEntID2 = InsertBlock(myDoc.Database, myBrefA.BlockName, myBrefA.Position, oldNode.Attributes("newname").Value, myBrefA.ScaleFactors.X, myBrefA.ScaleFactors.Y, myBrefA.ScaleFactors.Z) myBrefB = selEntID2.GetObject(OpenMode.ForWrite) myAttsB = myBrefB.AttributeCollection For Each myNode As Xml.XmlNode In oldNode.SelectNodes("attribute") For Each myAttID As ObjectId In myAttsA Dim myAtt As AttributeReference = myAttID.GetObject(OpenMode.ForRead) If myAtt.Tag.ToUpper = myNode.Attributes("name").Value.ToUpper Then For Each myAttBID As ObjectId In myAttsB Dim myAttB As AttributeReference = myAttBID.GetObject(OpenMode.ForWrite) If myAttB.Tag.ToUpper = myNode.Attributes("newname").Value.ToUpper Then myAttB.TextString = myAtt.TextString End If Next End If Next Next myBrefA.Erase() myTrans.Commit() End Using End Sub Public Function InsertBlock(ByVal DatabaseIn As Database, _ ByVal BTRToAddTo As String, _ ByVal InsPt As Geometry.Point3d, _ ByVal BlockName As String, _ ByVal XScale As Double, _ ByVal YScale As Double, _ ByVal ZScale As Double) As DatabaseServices.ObjectId Using myTrans As Transaction = DatabaseIn.TransactionManager.StartTransaction Dim myBlockTable As BlockTable = DatabaseIn.BlockTableId.GetObject(OpenMode.ForRead) 'If the suppplied Block Name is not 'in the specified Database, get out gracefully. If myBlockTable.Has(BlockName) = False Then Return Nothing End If 'If the specified BlockTableRecord does not exist, 'get out gracefully If myBlockTable.Has(BTRToAddTo) = False Then Return Nothing End If Dim myBlockDef As BlockTableRecord = _ myBlockTable(BlockName).GetObject(OpenMode.ForRead) Dim myBlockTableRecord As BlockTableRecord = _ myBlockTable(BTRToAddTo).GetObject(OpenMode.ForWrite) 'Create a new BlockReference Dim myBlockRef As New BlockReference(InsPt, myBlockDef.Id) 'Set the scale factors myBlockRef.ScaleFactors = New Geometry.Scale3d(XScale, YScale, ZScale) 'Add the new BlockReference to the specified BlockTableRecord myBlockTableRecord.AppendEntity(myBlockRef) 'Add the BlockReference to the BlockTableRecord. myTrans.AddNewlyCreatedDBObject(myBlockRef, True) Dim myAttColl As DatabaseServices.AttributeCollection = _ myBlockRef.AttributeCollection 'Find Attributes and add them to the AttributeCollection 'of the BlockReference For Each myEntID As ObjectId In myBlockDef Dim myEnt As Entity = myEntID.GetObject(OpenMode.ForRead) If TypeOf myEnt Is DatabaseServices.AttributeDefinition Then Dim myAttDef As DatabaseServices.AttributeDefinition = myEnt Dim myAttRef As New DatabaseServices.AttributeReference myAttRef.SetAttributeFromBlock(myAttDef, myBlockRef.BlockTransform) myAttColl.AppendAttribute(myAttRef) myTrans.AddNewlyCreatedDBObject(myAttRef, True) End If Next myTrans.Commit() Return myBlockRef.Id End Using End Function
I have modified this using an example posted by Tony Tanzillo so that it is now a LispFunction that accepts a lisp selecionset:
'<CommandMethod("UpdateAtts")> _ <LispFunction("UpdateAtts")> _ Public Sub UpdateAtts(args As ResultBuffer) If args Is Nothing Then Throw New ArgumentException("Requires one argument") End If Dim values As TypedValue() = args.AsArray If values.Length <> 1 Then Throw New ArgumentException("Wrong number of arguments") End If If values(0).TypeCode <> CInt(LispDataType.SelectionSet) Then Throw New ArgumentException("Bad argument type - requires a selection set") End If Dim ss As SelectionSet = DirectCast(values(0).Value, SelectionSet) Dim myDoc As Document = Application.DocumentManager.MdiActiveDocument Dim myEd As Editor = myDoc.Editor Dim selEntIDs As ObjectId() = ss.GetObjectIds() Dim selEntID As ObjectId = selEntIDs(0) 'Dim selEntID As ObjectId = myEd.GetEntity("Select Old Block:").ObjectId 'Everything after this point is unchanged from the above - ergo no point in duplicating the code twice in the same post!
My Lisp routine that *should* call this .net lispfunction is:
(defun c:uatts (/ss) (command "netload" C:\Users\Alex\documents\visual studio 2012\Projects\BlockReplace\BlockReplace\bin\Debug\BlockReplace.dll ) (setq ss (ssget "_X" (list (cons 0 "INSERT")'(66.1)))) (updateatts ss) (princ) )
but all I see at the command line when I run it is:
; error: too few arguments
Any ideas what I'm missing?
(command "netload" "C:\\Users\\Alex\\documents\\visual studio 2012\\Projects\\BlockReplace\\BlockReplace\\bin\\Debug\\BlockReplace.dll" )
or
(command "netload" "C:/Users/Alex/documents/visual studio 2012/Projects/BlockReplace/BlockReplace/bin/Debug/BlockReplace.dll" )
HTH
Henrique
Thanks Henrique, all useful information.
Perhaps I should have been clearer though; If I've already netloaded the .dll file prior to running the uatts command, what is the correct syntax for loading the LispFunction with the ss selectionset passed to it?
Turns out I've answered my own question. The problem had to do with the LISP itself.
This is the code that now fires my LispFunction:
(defun c:uatts (/ ss) (command "netload" "C:\\Users\\Alex\\documents\\visual studio 2012\\Projects\\BlockReplace\\BlockReplace\\bin\\Debug\\BlockReplace.dll" ) (setq ss (ssget "_X" (list (cons 0 "INSERT")'(66 . 1)))) (updateatts ss)
The crucial step was the lack of a space in the first line:
(defun c:uatts (/<space was required here>ss)
coupled with I think the same lack of a space in this line:
(setq ss (ssget "_X" (list (cons 0 "INSERT")'(66<space-required-here>.<space-required-here>1))))
Doh!
Time for another coffee.
Just a heads-up: copying the contents of the Express Tools programs and posting them publicly violates the EULA
Lee
Thanks for the information Lee; as I suspected the time window to edit the offending post has passed.
If it needs to be removed someone else will have to do it.
Here's my latest set of scripts for this thread:
::Commented out the below as we need it to work on non-superseded files ::could use the following instead of blindly running AutoCAD "C:\Program Files (x86)\Autodesk\ScriptPro 2.0\bin\ScriptPro.exe" :: :: :: :: Need to make sure that blockreplace tool is installed and loaded in AutoCAD 2014! :: :: :: :: :: ::FOR /R .\1 %%f in (*.dwg) do start /wait "C:\Program Files\Autodesk\AutoCAD 2014\acad.exe" "%%f" /b .\atout.scr REM write the header to the Scriptpro.bpl file type scriptpro_header.txt > scriptpro_test.bpl REM go to the sub-sub-sub-folder pushd .\1\HR4C\1-1 REM find .dwgs and list them in dwglist.txt REM Need to create this file after having moved files in the root of the drawing folder to \CURRENT for /d %%f in (.\*) do (IF NOT EXIST "%%f"\SUPERCEDED (MKDIR "%%f"\SUPERCEDED) && IF NOT EXIST "%%f"\CURRENT (MKDIR "%%f"\CURRENT) && IF NOT EXIST "%%f"\WIP (MKDIR "%%f"\WIP)) for /d %%f in (.\*) do ( pushd %%f REM Move anything in the root to \CURRENT MOVE .\*.* .\CURRENT\ pushd .\WIP\ REM Move anything in WIP to \CURRENT MOVE .\*.* ..\CURRENT\ popd pushd .\CURRENT\ REM Copy .dwg to \SUPERCEDED COPY .\*.dwg ..\SUPERCEDED\ popd popd ) popd REM Create our drawing script pushd .\1 dir /s /b *.dwg >..\dwglist.txt popd REM filter out case-independent "superceded" and append to the scriptpro.bpl file. findstr /i /v superceded dwglist.txt >>scriptpro_test.bpl REM write the footer to the Scriptpro.bpl file type scriptpro_footer.txt >> scriptpro_test.bpl REM store the scriptpro_test.bpl file as a variable REM run the scriptpro.bpl file CHOICE /M "Do you want to continue dwg processing with AutoCAD?" IF ERRORLEVEL 1 GOTO :Process IF ERRORLEVEL 2 GOTO :Skip :Process FOR /R %%f in (*_test.bpl) do "C:\Program Files (x86)\Autodesk\ScriptPro 2.0\bin\ScriptPro.exe" "%%~f" "run" "exit" :Skip REM write the masterlist header to masterlist for each variation of the border(s) we're expecting. type 5.2masterlist_header.txt > 5.2masterlist.txt type 5.2ILmasterlist_header.txt > 5.2ILmasterlist.txt type 5.1masterlist_header.txt > 5.1masterlist.txt type 6.1masterlist_header.txt > 6.1masterlist.txt REM append the resultant .txt files to the masterlist.txt file ready for opening/processing in Excel. REM delete the tmplist.txt file del tmplist.txt REM turn the cmd /f switch off cmd /f:OFF REM cycle through the directory tree looking for .txt files, then cycle through each file preserving tabs as we go. for /r .\1 %%f in (*.txt) do for /f "tokens=* delims= " %%i in ("%%f") do type "%%i" >>tmplist.txt REM Turn the cmd /f switch back on cmd /F:ON REM filter and append the contents of list.txt to the newly created masterlist.txt removing rows containing HANDLE as we go. REM findstr /i /v "HANDLE 5.2 6.1" tmplist.txt >>5.1masterlist.txt REM gets 5.1 version frames REM findstr /i /v "HANDLE 5.1 6.1" tmplist.txt >>5.2masterlist.txt REM gets 5.2 version frames REM findstr /i /v "HANDLE 5.1 5.2" tmplist.txt >>6.1masterlist.txt REM gets 6.1 version frames REM gets 5.1 frames findstr /i /r /c:".*5.1(block).*" tmplist.txt >>5.1masterlist.txt REM gets 5.2 IL frames findstr /i /r /c:".*SB-IL_996-5.2(block).*" tmplist.txt >>5.2ILmasterlist.txt REM gets 5.2 frames findstr /i /v "HANDLE 5.1 IL 6.1" tmplist.txt >>5.2masterlist.txt REM gets 6.1 frames findstr /i /r /c:".*6.1(block).*" tmplist.txt >>6.1masterlist.txt REM Assumes Office 2010 for /r %%f in (*masterlist.txt) do "C:\Program Files (x86)\Microsoft Office\Office14\EXCEL.EXE" "%%f"
The above creates and runs a scriptpro.bpl format file with contents similar to the following:
[General_Start] Version*3 Product*2014 Script*C:\temp\attout.scr TimeOut*30 RestartCount*30 IniScript* LogFileName*C:\Temp\ AutoCADPath* Sleep*0 RunwithoutOpen*True [General_End] [DWGList_Start] C:\temp\1\123456\Current\123456.dwg C:\temp\1\789012\Current\789012.dwg [DWGList_End]
This script file separately calls the attout.scr file which has the line:
(load "CompanyNameCommands") atout
The file "CompanyNameCommands" lisp file contains the following:
(defun c:atout ( / dn pa padn ss) (vl-load-com) (load "attout") (setq dn (vl-filename-base (getvar "dwgname"))) (setq pa (getvar "dwgprefix")) (setq padn (strcat pa dn ".txt")) (setq ss (ssget "_X" (list (cons 0 "INSERT")'(66 . 1)))) (bns_attout padn ss) );atout (defun c:atin (/ dn pa padn) (vl-load-com) (load "attout") (setq dn (vl-filename-base (getvar "dwgname"))) (setq pa (getvar "dwgprefix")) (setq padn (strcat pa dn ".txt")) (bns_attin padn false) );atin (defun c:uatts (/ ss) (setq ss (ssget "_X" (list (cons 0 "INSERT")'(66 . 1)))) (updateatts ss) );uatts ;;;(defun c:client ( dwgnum sht rev / newdn ) (defun client ( dwgnum sht rev / newdn ) (if (not (= (type dwgnum) 'STR)) (setq dwgnum "DWGNUM") );if (if (not (= (type sht) 'STR)) (setq sht "001") );if (if (not (= (type rev) 'STR)) (setq rev "REV") );if (setq old-echo (getvar "CMDECHO")) (setvar "cmdecho" 1) (setvar "draworderctl" 1) ;;;Step 1. A quick save of the current drawing. (works) (command "qsave") ;;;Step 2. A save as to a drawing with file name "######_sht_001_iss-###.dwg" in the same directory as the current drawing. (setq newdn (strcat dwgnum "_sht_" sht "_iss-" rev ".dwg")) (setq DwgName (strcat (getvar "dwgprefix") newdn)) (if (findfile DwgName);Check file exists (command "_.saveas" "" DwgName "Y");If file exits then Overwrite (command "_.saveas" "" DwgName) );if (setvar "CMDECHO" old-echo) (setvar "DRAWORDERCTL" 3) )
the last step I have to piece together for this is a file saveas using the (client "dwgnum" "sht" "rev") command.
I've looked into running some of this with the accoreconsole.exe available as part of AutoCAD 2014 but it doesn't seem to like the "bns_attout" command from the attout.lsp file -< that file seems to load just fine if you run accoreconsole.exe at a command prompt. The (bns_attout padn ss) command fired from the above atout command just never does anything.
???
With express tools loaded?
Fair use?
http://en.wikipedia.org/wiki/Fair_use
Fair use is a limitation and exception to the exclusive right granted by copyright law to the author of a creative work. In United States copyright law, fair use is a doctrine that permits limited use of copyrighted material without acquiring permission from the rights holders. Examples of fair use include commentary, search engines, criticism, parody, news reporting, research, teaching, library archiving and scholarship. It provides for the legal, unlicensed citation or incorporation of copyrighted material in another author's work under a four-factor balancing test.
I never understand why people have qualms with this.
Criticism and parody are allowed, and although some of my attempts at re-writing express code surely falls under the parody catagorey, i believe most of what people who are taking express routines and legitimately having issues with them in the course of their daily work are indeed allowed to come and post their problems (with excerpts) onto these support forums.
If this was hackers anonymous, i wouldn't be saying this. But it's autodesk forums, support at that. Commentary, teaching, library archiving.
Perhaps i'm missing something, but...credit where credit is due, and i've never seen anyone try to claim an express routine as their own.
That last line, too:
It provides for the legal, unlicensed citation or incorporation of copyrighted material in another aurthor's work under a four-factor balancing test (which i have no idea what is. Will look up after submitting this post).
I feel obligated to post this because OP clearly stated the routine belonged to "express" as such this seems fair use, but perhaps not? 🙂
Goodmorning, too, by the way.