Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

The best method for Attribute Extraction and/or Block replacement?

31 REPLIES 31
Reply
Message 1 of 32
AlexFielder
2274 Views, 31 Replies

The best method for Attribute Extraction and/or Block replacement?

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://forums.autodesk.com/t5/NET/How-to-programmatically-extract-the-attributes-from-a-CAD-file/td-...

 

http://forums.autodesk.com/t5/AutoCAD-2013-2014-DWG-Format/Block-Replace-in-AutoCAD-2014-does-not-wo...

 

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.

31 REPLIES 31
Message 2 of 32
Anonymous
in reply to: AlexFielder

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...

Message 3 of 32
AlexFielder
in reply to: Anonymous

That's the kind of thing I was thinking of, but I don't want to do any programming unless I absolutely have to; I have programmed AutoCAD using c# .NET in the past, I just don't want to go down that particular road yet. It amazes me that Autodesk still don't have a built-in tool for this sort of thing- they have the ATTOUT/ATTIN commands, and there's the DATAEXTRACTION (EATEXT of old) command which generates a nice excel file. It's just editing the data in Excel and linking it back to the source .dwg files that is missing. As luck would have it, within the sample dataset the 30 files all have the same attribute names even though some of them have different drawing frame blocks (one is an older revision of the other I think) - this means there's less work to do and no mapping between old and new attribute names which would have been a massive chore to try and program. Is there any way of having the ATTOUT command append it's output to one file instead of either overwriting the file or creating one text file per .dwg?
Message 4 of 32
Anonymous
in reply to: AlexFielder

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:

http://lee-mac.com/batte.html

 

easy enough to include in a script if necessary.

Message 5 of 32
AlexFielder
in reply to: Anonymous

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!)

Message 6 of 32
Anonymous
in reply to: AlexFielder

I'll send Lee a pm link to this thread.

Message 7 of 32
AlexFielder
in reply to: Anonymous

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!?

Message 8 of 32
Anonymous
in reply to: AlexFielder

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

Message 9 of 32
AlexFielder
in reply to: Anonymous

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 🙂

Message 10 of 32
Anonymous
in reply to: AlexFielder

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

Message 11 of 32
AlexFielder
in reply to: Anonymous

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.

 

🙂

Message 12 of 32
Anonymous
in reply to: AlexFielder

Anytime. Well, mostly.

Message 13 of 32
AlexFielder
in reply to: Anonymous

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.

 

 

Message 14 of 32
AlexFielder
in reply to: AlexFielder

This has turned out to be a lot more complicated than I originally thought (what a surprise Smiley Indifferent ) 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

Message 15 of 32
Anonymous
in reply to: AlexFielder

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

Message 16 of 32
AlexFielder
in reply to: Anonymous

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?

Message 17 of 32
Anonymous
in reply to: AlexFielder

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.

Message 18 of 32
AlexFielder
in reply to: Anonymous

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. 🙂

Message 19 of 32
Anonymous
in reply to: AlexFielder

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?

Message 20 of 32
hmsilva
in reply to: AlexFielder

(bns_attin padn nil );; no interactive placement

(bns_attin padn  T);; allow interactive placement

 

HTH

Henrique

EESignature

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost