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.
If this was me I would set up a routine that gets all the attributes from oldblock, delete/purge it, insert new block, modify/fill in attributes as neccessary, save.
Then I would create a script that opens a drawing, loads the routine, executes routine, opens next drawing, loads routine, etc, as many times as I needed for whatever drawings I needed.
I would also make backups of all the drawings beforehand because I'd likely put a space somewhere it wasn't suppose to be and end up with 100's of empty drawings....not that it's happened to me before or anything...
Well if the blocks all have the same attributes in them then you can globall edit every last dwg in one shot via the program written by Lee-Mac which is found at the following link:
easy enough to include in a script if necessary.
Thanks, but the blocks have the same attribute names; the information contained within is different- plus I keep getting an error when I try and select a block with that tool:
Error: ActiveX Server returned an error: Library not registered
Any ideas what might be causing that error? (it does the same thing in both AutoCAD 2013 & AutoCAD 2014!)
Thanks, I'm not entirely convinced it is his code that's to blame- I think there's something else causing this to break because other lisp routines I've been trying to run are producing the same error message. When I get time I'll run a repair on both AutoCAD versions.
Back to finding a solution with the built-in tools I think:
Using the code from here: http://www.widom-assoc.com/AU-CP12-3L.pdf I plan to run the following lisp:
(defun c:atout () (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")))) ;(setq ss (ssget '((0 . "INSERT") (66 . 1)))) (bns_attout padn ss) )
then append each of the .txt files to a master .txt file which I can open in Excel; make the changes there and it should be a simple matter of writing back each line from excel to the original .txt file, run the ATTIN command and voila.
Incidentally, does the line:
(setq ss (ssget "_X" (list (cons 0 "INSERT"))))
have the correct syntax to select all the blocks in the drawing its run against? Is there a more comprehensive lisp method that will only grab blocks that have attributes in them?
EDIT: **** formatting goes all to hell when I use Google Chrome... WTF!?
Yes, there is. Via the dxf group 66, please take a look at this thread for a more lengthy explination
http://www.cadtutor.net/forum/showthread.php?54369-SSget-for-only-Blocks-of-attributes
Ok, thanks.
I can see from the lisp code I already had that it listed the 66 code already- I have just commented that line out because it was prompting me every time.
So this is the code that works now:
(defun c:atout () (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)))) ;(setq ss (ssget '((0 . "INSERT") (66 . 1)))) (bns_attout padn ss) )
which is called from a script within ScriptPro that simply runs the atout command against a list of files.
Now to tackle the command line/combining text files into one file aspect of this.
Thanks again for your help bhull1985 🙂
Not a problem...but here's a few comments that may help you in your programming.
(defun c:atout () ;<<-- no localised variables defined. Check here for what this means/how to handle this: http://www.lee-mac.com/localising.html (load "attout") ; <<--does not check for the existence of "attout", here's how you'd do so: (if (setq x (findfile "attout.lsp"))(load x)) (setq dn (vl-filename-base (getvar "dwgname"))) ;<<--starting to use visual lisp here, need to make sure (vl-load-com) is at the top of your routine. This loads the visual lisp functions and should be included at the top of every routine using (vl-) functions. (setq pa (getvar "dwgprefix")) (setq padn (strcat pa dn ".txt")) ^^^| |--these are fine, but need their variables localised. FYI, alternatively to accomplish this you could have done: (setq truepath (strcat (getvar "dwgprefix")(vl-filename-base (getvar "dwgname")) ".txt")) which to me is cleaner, but your method is superior if you're reusing the 'pa' or 'dn' variables elsewhere. This will make more sense after reading the localizing variables page given above. (setq ss (ssget "_X" (list (cons 0 "INSERT")'(66 . 1)))) ;<<--getting selection set, time for processing your selection set. ;(setq ss (ssget '((0 . "INSERT") (66 . 1)))) (bns_attout padn ss) ;^^|--processing );end routine
HTH
Ok, I see what the localisation thing is doing and follows the adage I heard from someone on these or other forums to do with .NET which went:
"If you new it, be sure to dispose of it"
Thanks for the comments on the LISP.
When I have a working solution I'll stick all the code on this thread.
🙂
here's my latest batch file:
REM turn the cmd /f switch off cmd /f:OFF REM write the header to the Scriptpro.bpl file type scriptpro_header.txt > scriptpro_test.bpl REM go to the subfolder cd .\1 REM find .dwgs and list them in dwglist.txt dir /s /b *.dwg >..\dwglist.txt REM filter out case-independent "superceded" and append to the scriptpro.bpl file. findstr /i /v superceded ..\dwglist.txt >>..\scriptpro_test.bpl REM go back to the parent folder cd ..\ 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 PAUSE REM Wait for user input to continue FOR /R %%f in (*_test.bpl) do "C:\Program Files (x86)\Autodesk\ScriptPro 2.0\bin\ScriptPro.exe" "%%~f" "run" "exit" REM write the masterlist header to masterlist type masterlist_header.txt > masterlist.txt REM append the resultant .txt files to the masterlist.txt file ready for opening/processing in Excel. REM FOR /R .\1 %%f in (*.txt) do more +1 "%%f" >>masterlist.txt 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 filter and append the contents of list.txt to the newly created masterlist.txt removing rows containing HANDLE as we go. findstr /i /v HANDLE tmplist.txt >>masterlist.txt REM delete the tmplist.txt file del tmplist.txt REM Turn the cmd /f switch back on cmd /F:ON REM Assumes Office 2010 start /wait "C:\Program Files (x86)\Microsoft Office\Office14\EXCEL.EXE" masterlist.txt
I had been struggling with retaining the tabs in the text files the scriptpro script was generating but swapping "type "%%i" >>tmplist.txt for "more +1" works much better. I also notice that the cmd /f:OFF & cmd /f:ON commands both need an exit after them to return to the original thread?
My next step is to write a small piece of vba to output each edited row back to its respective txt file and then run the attin command on each of the affected drawings. (Does anyone know how to get the cmd prompt to write the full path of each file to a textfile - I assume it's some variation of "dir /b..."?)
After that I need to run the block replace command and then it should be done.
This has turned out to be a lot more complicated than I originally thought (what a surprise ) but does anyone know how I can use the attin command stored in this location:
C:\Program Files\Autodesk\AutoCAD 2014\Express\attout.lsp
and modify it to accept a selectionset?
It puzzles me that the bns_attout command does this, but I can't seem to see the same thing for the attin command.
Thanks,
Alex
Hi again.
Please post the routine
And I can likely show you how to modify it in such a way that if you have objects selected and then issue the command, it will work on your selected objects
Hi,
No sooner had I posted than I took a look at the attout lisp file and had an epiphany that the attin command doesn't in fact need a selectionset; this is because the text file created with attout has a handle for each of the blocks it is updating.
Here's what I have so far and I think I just to get the syntax correct to pull in the file I earlier extracted (and subsequently modified with Excel):
(defun c:atin (/ dn pa padn) (vl-load-com) (load "attout") (setq dn (vl-filename-base (getvar "dwgname"))) (setq pa (getvar "dwgprefix")) (setq padn (strcar pa dn ".txt")) (bns_attin padn ) )
What am I missing in terms of what I need to pass to bns_attin to forego the prompt to select blocks to update?
You're still not posting the relevant parts of any routine that should be modified 😛
I can determine which statement is requiring the user input and explain a method to bypass the user-input of block selection but I can't do it without the relevant code.
My apologies, I didn't realise you meant the full attout.lsp file.
Here you go:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;ATTIN - ;Imports attribute data from selected file. The file must be of ;the same format as created with ATTOUT. ; (defun c:attin ( / fna lklay ) (acet-error-init (list (list "cmdecho" 0) 0 '(progn (if lklay (progn (acet-sysvar-set '("cmdecho" 0)) (command "_.layer" "_lock" lklay "") (acet-sysvar-restore) ) );if need to re-lock (princ (strcat "\n" (itoa #bns_attin_modified) " Block inserts modified.")) );progn ) ) (acet-autoload '("yes_no.lsp" "(bns_get_yes_no a b)")) (setq #bns_attin_modified 0) (cond ((not (setq fna (acet-ui-getfile "Enter input filename" (strcat (getvar "dwgprefix") (acet-filename-path-remove (acet-filename-ext-remove (getvar "dwgname"))) ".txt" );strcat "txt" "Acet:Att" 1664 ) ) ) (princ "\nNo input file selected.") );cond #1 ((setq fna (findfile fna)) (setq lklay (acet-layer-unlock-all)) (if (equal 4 (logand 4 (getvar "cmdactive"))) (bns_attin fna nil);a script is running so no interactive placement (bns_attin fna T);Allow interactive placement );if (if lklay (command "_.layer" "_lock" lklay "") );if (princ (strcat "\n" (itoa #bns_attin_modified) " Block inserts modified.")) );cond #2 );cond close (acet-error-restore) );defun c:attin ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;bns_attin takes a file name and a flag (interact) that specifies ;if the user should be prompted to select block inserts when unmatched ;data is found. ; (defun bns_attin ( fna interact / fh lst lst2 lst3 lst4 lst5 n j na e1 a delim ans ) (setq delim "\t") (if (setq fh (open fna "r")) (progn (princ "\nReading the input file...") (while (setq a (read-line fh)) (if (not lst) ;change first record to upper case (setq a (xstrcase a)) );if (setq lst (cons (acet-str-to-list delim a) lst));setq );while (setq fh (close fh)) (princ " Done.") (setq lst (reverse lst) lst2 (cdr lst) ;the data lst (car lst) ;the column headers );setq (acet-ui-progress-init "Importing data" (length lst2)) (setq n 0) (repeat (length lst2) (setq lst3 (nth n lst2) lst4 nil );setq (acet-ui-progress-safe n) (setq j 0) ;match the header row with the current record (repeat (min (length lst) (length lst3)) (setq a (list (nth j lst) (nth j lst3) );list lst4 (cons a lst4) );setq (setq j (+ j 1));setq );repeat (setq lst4 (reverse lst4));setq (if (and (setq a (cadr (assoc "HANDLE" lst4))) (equal "'" (substr a 1 1)) );and (setq a (substr a 2)) );if (if (and a (setq na (handent a)) (setq e1 (entget na)) (equal "INSERT" (cdr (assoc 0 e1))) (equal 1 (cdr (assoc 66 e1))) (or (not (assoc "BLOCKNAME" lst4)) (equal (xstrcase (cadr (assoc "BLOCKNAME" lst4))) (xstrcase (cdr (assoc 2 e1))) );equal );or );and (progn (if (not (equal 8 (logand 8 (getvar "undoctl")))) (acet-undo-begin) );if (setq lst4 (vl-remove (assoc "BLOCKNAME" lst4) lst4) lst4 (vl-remove (assoc "HANDLE" lst4) lst4) );setq (if (acet-insert-attrib-set na lst4 nil) (setq #bns_attin_modified (+ #bns_attin_modified 1)) (progn (if interact (setq lst5 (cons lst4 lst5));setq add to interactive list. (princ "\nNo matching attribute tags.") );if );progn else );if (acet-undo-end) );progn then (progn ;Print what's going on... (if (not interact) (cond ((not a) (princ "\nNo Handle specification.") ) ((not na) (princ (strcat "\nNo entity with specified handle \"" a "\" exists.")) ) (T (princ (strcat "\nSpecified entity: \"" a "\" has been deleted or is invalid. ")) ) );cond then non-interactive execution (setq lst5 (cons lst4 lst5));setq else gather a list of attribs with ;no insert home. );if );progn else no home for this record );if (setq n (+ n 1));setq );repeat (acet-ui-progress-done) (if (and lst5 interact (setq ans (bns_get_yes_no (list "ATTIN - Select blocks?" (strcat "" "\n One or more records of data could not be" "\n matched to block inserts within this drawing." "\n" "\n Do you want to select the block inserts " "\n and assign this data interactively?" );strcat );list (list 40 10) );bns_get_yes_no );setq (equal ans 1) );and (progn (princ "\nSelect the desired blocks to assign the data to and press ENTER when done.") (bns_attin_interactive (reverse lst5)) );progn then );if );progn then (acet-alert "\nError opening input file for read.") );if );defun bns_attin ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun bns_attin_interactive ( lst / na e1 ) (while lst (if (not (equal 8 (logand 8 (getvar "undoctl")))) (acet-undo-begin) );if (bns_print_att_list (car lst)) (princ "\nPress [Enter] to skip or [Esc] to finish.") (if (and (setq na (acet-ui-single-select '((0 . "INSERT") (66 . 1)) nil)) (setq e1 (entget na)) );and (progn (if (acet-insert-attrib-set na (car lst) nil) (setq #bns_attin_modified (+ #bns_attin_modified 1) lst (cdr lst) );setq (acet-alert (strcat "\nNo matching attribute tags were found on that " "block insert. \nTry another block insert." );strcat );acet-alert );if );progn then (setq lst (cdr lst));setq );if (acet-undo-end) );while );defun bns_attin_interactive ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun bns_print_att_list ( lst / sp n a b j k) (setq lst (cons (list "---" "-----") lst) lst (cons (list "TAG" "VALUE") lst) sp " " sp (strcat sp sp sp sp sp sp sp sp) sp (strcat sp sp sp sp sp sp sp sp) j 0 k 0 n 0 );setq (repeat (length lst) (setq a (nth n lst) b (cadr a) a (car a) j (max (strlen a) j) k (max (strlen b) k) );setq (setq n (+ n 1));setq );repeat (princ "\n") (setq j (+ j 2) k (+ k 2) );setq (setq n 0) (repeat (length lst) (setq a (nth n lst) b (cadr a) a (car a) );setq (if (not (equal b "<>")) (progn (setq a (strcat a (substr sp 1 (max 0 (- j (strlen a)))) );strcat b (strcat b (substr sp 1 (max 0 (- k (strlen b)))) );strcat a (strcat "\n " a b) );setq (princ a) );progn then );if (setq n (+ n 1));setq );repeat n );defun bns_print_att_list (acet-autoload2 '("Yes_no.lsp" (bns_get_yes_no lst size))) (princ)
bns_attin seems to be able to accept an "interact" flag - but I have no idea from looking at this code what form that should be in. 😞
Thanks again for your help. 🙂
Whew that's a long one, I see why you didn't post it immediately.
Okay, so, now with that routine, I just was hoping for a few more pieces of information.
If you could run the routine, and then capture the prompt that you're trying to bypass, as well as give what information you would like for the program to automatically use?
Without your corresponding text files and all of that I don't know if I can test this properly, but am still willing to help. I believe I could locate the prompt and supply an alternate with those additional pieces of information.
So...when and what does it ask you that you're trying to bypass, because you already know what you're wanting to process. What is that item/s?
(bns_attin padn nil );; no interactive placement
(bns_attin padn T);; allow interactive placement
HTH
Henrique