Seek a lisp to change the title block for each layout

Seek a lisp to change the title block for each layout

skchui6159
Advocate Advocate
630 Views
25 Replies
Message 1 of 26

Seek a lisp to change the title block for each layout

skchui6159
Advocate
Advocate

Referring to the previous post "https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/can-i-insert-attribute-in-all-curren..."

 

There are the new question that I can not solve. Can I replace the attribute value from the existing title block to the blank title block for each layout referring other dwg? Then insert the new block using the old title block boundary to the same place of each layout. Then erase the old title block in the layout. There are two title block size need to import, there are A1 and A4. The drawing size is indicated on each drawing. Thank you very much!

 

 

 

 

 

 

 

 

0 Likes
Accepted solutions (2)
631 Views
25 Replies
Replies (25)
Message 2 of 26

Moshe-A
Mentor
Mentor

@skchui6159 hi,

 

Check ATTR2ATTR command. it will work only if tag name is unique in block as i can see, you duplicate tags without giving them unique name.

the source block (the block to take attribute values) must be laid beside the target.

after you picked source + target, the program catches all target references in drawing and that include in the Model space and all layouts (careful here 😀)

 

enjoy

Moshe

 

(defun c:attr2attr (/ _getAttrData ; local function
		      pick0 ename0 elist0 pick1 ename1 elist1 AcDbBlkRef0 AcDbBlkRef1 blkName1 ss1 attributes0 attributes1)

 (defun _getAttrData (AcDbBlkRef)
  (mapcar
    (function
      (lambda (AcDbAttrib)
       (cons (strcase (vla-get-tagString AcDbAttrib)) (vla-get-textString AcDbAttrib))
      )
    ); function
   (vlax-invoke AcDbBlkRef 'GetAttributes)
  ); mapcar
 ); _sorAttrData

  
 ; here start c:attr2attr
 (cond
  ((not
     (and
       (setq pick0 (entsel "\nPick source block: "))
       (setq ename0 (car pick0))
       (setq elist0 (entget ename0))
       (= (cdr (assoc '0 elist0)) "INSERT")
       (= (cdr (assoc '66 elist0)) 1)
     )
   )
   (prompt "\ninvalid object from source block.")
  ); case
  ((not
     (and
       (setq pick1 (entsel "\nPick target block: "))
       (setq ename1 (car pick1))
       (setq elist1 (entget ename1))
       (= (cdr (assoc '0 elist1)) "INSERT")
       (= (cdr (assoc '66 elist1)) 1)
     )
   )
   (prompt "\ninvalid object from target block.")
  ); case
  ( t   
   (setq AcDbBlkRef0 (vlax-ename->vla-object ename0))
   (setq attributes0 (_getAttrData AcDbBlkRef0)) ; get source attributes
   
   (setq AcDbBlkRef1 (vlax-ename->vla-object ename1))
   (setq blkName1 (vla-get-effectiveName AcDbBlkRef1))
   (vlax-release-object AcDbBlkRef1)

   (if (setq ss1 (ssget "_x" (list '(0 . "insert") (cons '2 (strcat blkName1 ",`*U*")))))
    (foreach ename1 (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss1)))
     (setq AcDbBlkRef1 (vlax-ename->vla-object ename1))

     (mapcar
      (function
       (lambda (AcDbAttrib1 / tag1 dat)
        (setq tag1 (strcase (vla-get-tagString AcDbAttrib1)))

 	(if (setq dat (assoc tag1 attributes0))
         (vla-put-textString AcDbAttrib1 (cdr dat))
        )
       ); lambda
      ); function
      (setq attributes1 (vlax-invoke AcDbBlkRef1 'GetAttributes))
     ); vl-every
       
     (foreach o attributes1
      (vlax-release-object o)
     )
      
     (vlax-release-object AcDbBlkRef1)       
    ); foreach
     
   ); if
   
   (vlax-release-object AcDbBlkRef0)
  ); case
 ); cond

 (princ)
); c:attr2attr

 

 

0 Likes
Message 3 of 26

Moshe-A
Mentor
Mentor

@skchui6159 

 

fix a bug

 

 

Message 4 of 26

skchui6159
Advocate
Advocate

@Moshe-A 

Thank you very much for your helping😁. The unquie name 👍!

But there is a problem, there are the batch of dwgs about 4000dwgs need to work out, i need to use WScript (from Lee mac) and without selection any blocks to run lisp.

1. Could lisp use "Steal from Drawing" (from lee mac) to get the source dwg's blocks in specific file location in lisp?

2. Could lisp use "boundary box method" to find area which match the size of drawing in layout?

Thank for your help!😉

0 Likes
Message 5 of 26

Moshe-A
Mentor
Mentor

@skchui6159 ,

 


@skchui6159 wrote:

@Moshe-A 

Thank you very much for your helping😁. The unquie name 👍!

But there is a problem, there are the batch of dwgs about 4000dwgs need to work out, i need to use WScript (from Lee mac) and without selection any blocks to run lisp.

1. Could lisp use "Steal from Drawing" (from lee mac) to get the source dwg's blocks in specific file location in lisp?

2. Could lisp use "boundary box method" to find area which match the size of drawing in layout?

Thank for your help!😉


Ha! 4000 dwg's? i do not want to depress you so much but you have more problems then you are thinking of right now 🤣

AutoLISP can select blocks by their name but can you guarantee the title blocks name? (especially the old)

if picking by boundary, can you guarantee the existence of the old block in place maybe it is exploded?!

can you guarantee the existence of all the attributes?

add to that, files may fail to open cause it has errors that need to be Audit\Recover or some other problem with dependent files (xrefs, fonts...)

 

cheers,

Moshe

 

 

 

 

0 Likes
Message 6 of 26

skchui6159
Advocate
Advocate

@Moshe-A 

 

Yes. guarantee the title blocks name by below😄:

I think use ssget "_x" and "ctab".

(setq blknam '("TITLE BLOCK" "TITLE BLOCK1" "1a32" "TITLE BLOCK 3" "TITLE BLOCK 4" "a1titleblock" "-TITLE_A1_DETAIL")) ; Define multiple block names in A1 block
(setq blknam_A4 '("TITLE BLOCK-A4")) ; Define multiple block names in A4 block
(setq blkstr (apply 'strcat (mapcar '(lambda (x) (strcat x ",")) blknam)))
(setq blkstr (vl-string-right-trim "," blkstr))
(setq blkstr (strcat blkstr ",`*U*"))

 

 

(if (setq ss1 (ssget "_X" (list '(0 . "INSERT")
(cons 2 blkstr)
'(66 . 1)
(cons 410 (getvar "CTAB"))))); to select the old block

 

 

and the specific name is a poosible method to select the block by specific name. If there are exploded block, just ignore it. (I think the old block can get the boundary pt for importing the new title block)

Sure, all the attributes are existence.😁

 

 

 files may fail to open cause it has errors that need to be Audit\Recover or some other problem with dependent files (xrefs, fonts...), oh no, can be solve?😅

0 Likes
Message 7 of 26

Moshe-A
Mentor
Mentor

@skchui6159 ,

 

WOW that's very good, i see you can write code by your own so continue the good work and I (and so other experts invited) to help.

you need to incorporate my code into yours to make it all work.

 

so for the new block you can put it on standalone block file (as wblock) and insert it at 0,0,0 in model space - yes?

build a dotted pair list with all it attrib tag name + attrib value - delete the new title block.

 

fetch the existing blocks with this code:

(setq ss1 (ssget "_X" (list '(0 . "insert")  (cons '2 blkstr)))) ; where blkstr is your combined string for all existing title blocks

you can ignore CTAB if you want to replace every one in drawing (all layouts)

 

at each iteration through selection set (for dynamic block make sure it name) insert the new title block on top of the existing,

update all attributes value and delete the existing title block + qsave

 

Vwalla 😀 - job done!

 

the rest i done from mr Lee Mac 'WScript' (or ScriptPro 2.0 from AutoDESK)

 

Moshe

 

 

0 Likes
Message 8 of 26

skchui6159
Advocate
Advocate

@Moshe-A 

I think to get the minpt and maxpt, it can be done as follow code:

;(Because they are not sure the point of insertion point of the block is 0,0), but the new title block are confirmed (the insertion point)😅

(if (setq ss1 (ssget "_X" (list '(0 . "INSERT")
(cons 2 blkstr)
'(66 . 1)
(cons 410 (getvar "CTAB")))))
(progn
(setq i 0)
(repeat (sslength ss1)
(setq ent (ssname ss1 i)
obj (vlax-ename->vla-object ent))
;; Get bounding box for each title block
(vla-getboundingbox obj 'minPt 'maxPt)
(setq minPt (vlax-safearray->list minPt))
(setq maxPt (vlax-safearray->list maxPt))
(setq leftCorner (list (car minPt) (cadr maxPt) 0)) ; Upper-left corner
(setq rightCorner (list (car maxPt) (cadr minPt) 0)) ; Lower-right corner

 

I try to add into your code..., then import the block use their insert point...(Lower-right corner)

 

Reply to: so for the new block you can put it on standalone block file (as wblock) and insert it at 0,0,0 in model space - yes?

 

No, I think it needs to direct import to the layout. And model space is ignored. It need to construct a layoutlist for iteration. Finally, erase the old block by selection of each layout. Using for loop.

 

I am the beginner of Autolisp. Some code I can not write, just can write some sample code. Just learn something from this forum!😅 🤣🤣

 

In excel VBA, I think I am good for programming in excel, because it is easily to write the code. But in AutoCAD (VBA and lisp), to be honest, I think it is very hard to learn for programming (No record Marco for the code, but excel have).😰 Also, I cant install VBA for autoCAD in my company for their security issue😨. So I can only autolisp to solve the problem.

0 Likes
Message 9 of 26

Moshe-A
Mentor
Mentor

@skchui6159 wrote:

@Moshe-A 

I think to get the minpt and maxpt, it can be done as follow code:

;(Because they are not sure the point of insertion point of the block is 0,0), but the new title block are confirmed (the insertion point)😅

(if (setq ss1 (ssget "_X" (list '(0 . "INSERT")
(cons 2 blkstr)
'(66 . 1)
(cons 410 (getvar "CTAB")))))
(progn
(setq i 0)
(repeat (sslength ss1)
(setq ent (ssname ss1 i)
obj (vlax-ename->vla-object ent))
;; Get bounding box for each title block
(vla-getboundingbox obj 'minPt 'maxPt)
(setq minPt (vlax-safearray->list minPt))
(setq maxPt (vlax-safearray->list maxPt))
(setq leftCorner (list (car minPt) (cadr maxPt) 0)) ; Upper-left corner
(setq rightCorner (list (car maxPt) (cadr minPt) 0)) ; Lower-right corner

 

I try to add into your code..., then import the block use their insert point...(Lower-right corner)

 

Reply to: so for the new block you can put it on standalone block file (as wblock) and insert it at 0,0,0 in model space - yes?

 

No, I think it needs to direct import to the layout. And model space is ignored. It need to construct a layoutlist for iteration. Finally, erase the old block by selection of each layout. Using for loop.

what i meant here is to insert the new title block in model space but it could be any where (layout) you want in order to

extract its data (tag name + text value) and put that in a dotted pair list and i did that in my program. at the top you see

the function (_getAttrData AcDbBlkRef) it accept a VLA-OBJECT which is a block reference in our case the NEW title block and return a dotted pair of tag names + text value so this work is done for you - just use it.

after you hold all the data attribute needed, all left is to loop through the existing title blocks (hold by ss1) and modify

each one (look at my code i did that) 

 

I am the beginner of Autolisp. Some code I can not write, just can write some sample code. Just learn something from this forum!😅 🤣🤣

 

In excel VBA, I think I am good for programming in excel, because it is easily to write the code. But in AutoCAD (VBA and lisp), to be honest, I think it is very hard to learn for programming (No record Marco for the code, but excel have).😰 Also, I cant install VBA for autoCAD in my company for their security issue😨. So I can only autolisp to solve the problem.


 

0 Likes
Message 10 of 26

Moshe-A
Mentor
Mentor

@skchui6159 ,

 

Check this fix, it is full process (exclude the WScript this on you 😀)

it will work if you will be accurate in naming the "new title block"  as well as the existing block name.

 

; define 3 constants

(setq NEW_TITLE_BLOCK "new title block-a4")
(setq BLOCK_CONTAINER "new_title_block_container")
(setq OLD_TITLE_BLOCK (strcase "old title block-a4,title block,title block1,1a32,title block 3,title block 4,a1titleblock,-title_a1_detail"))

 

BLOCK_CONTAINER is actually "new_tiltle_block_container.dwg" (which you have to create) and inside you insert the NEW_TITLE_BLOCK "new title block-A4" and save it in a folder that is defined in support file search path.

 

study the above very carefully.

 

Program Process:

Search for existing NEW_TITLE_BLOCK and delete it if exist? this to make way for the real new title block to insert.

temporary insert BLOCK_CONTAINER in model space releasing NEW_TITLE_BLOCK to be available for insert in all layouts.

scan drawing for all existing title blocks (exclude model space) set to ss1

iterate each block in ss1 selection, get bounding box of existing title blocks

insert NEW_TITLE_BLOCK in layout, insertion point is lower left corner of the existing (you can change it to middle point if you like)

read existing block attributes (build dotted pair list)

apply the attribute value to NEW_TITLE_BLOCK

erase existing title block.

erase new title block from model space was release from BLOCK_CONTAINER

clean up and exit 😀

 

enjoy

Moshe

 

;; Unique  -  Lee Mac
;; Returns a list with duplicate elements removed.

(defun LM:Unique ( l )
    (if l (cons (car l) (LM:Unique (vl-remove (car l) (cdr l)))))
)


(defun c:attr2attr (/ _getAttrData remove_new_title_block insert_new_title_block_container insert_new_title_block 	; local functions
		      savAttdia savAttreq savCTab NEW_TITLE_BLOCK BLOCK_CONTAINER OLD_TITLE_BLOCK ss1 ename1		; local variables
		      modelEnt AcDbBlkRef1 AcDbBlkRef2 p0 p1 attributes1 attributes2 tag2 AcDbAttrib2)			; local variables

 (defun _getAttrData (AcDbBlkRef)
  (mapcar
    (function
      (lambda (AcDbAttrib)
       (cons (strcase (vla-get-tagString AcDbAttrib)) (vla-get-textString AcDbAttrib))
      )
    ); function
   (vlax-invoke AcDbBlkRef 'GetAttributes)
  ); mapcar
 ); _getAttrData


 (defun remove_new_title_block (/ ss ename)
  (if (setq ss (ssget "_x" (list '(0 . "insert") (cons '2 NEW_TITLE_BLOCK))))
   (foreach ename (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss)))
    (entdel ename)  
   ); foreach
  ); if
 ); remove_new_title_block


 (defun insert_new_title_block_container (/ fname)
  (if (setq fname (findfile (strcat BLOCK_CONTAINER ".dwg")))
   (progn
    (command "._insert" (strcat "*" fname) "0,0,0" 1 0)
    (entlast)
   ); progn
   (prompt (strcat "\nContainer block " BLOCK_CONTAINER " is not exist."))
  ); if
 ); insert_new_title_block_container

  
 (defun insert_new_title_block (base / fname)
  (if (tblsearch "block" NEW_TITLE_BLOCK)
   (progn
    (command "._insert" NEW_TITLE_BLOCK base 1 1 0)
    T
   ); progn
   (prompt (strcat "\nBlock " NEW_TITLE_BLOCK " is not exist."))
  ); if
 ); insert_new_title_block
  
  
 ; here start c:attr2attr
 (setvar "cmdecho" 0)
 (command "._undo" "_begin")

 (setq savAttdia (getvar "attdia"))
 (setq savAttreq (getvar "attreq"))

 (setvar "attdia" 0)
 (setvar "attreq" 0)

 (setq savCTab (getvar "ctab"))

 (setq NEW_TITLE_BLOCK "new title block-a4")
 (setq BLOCK_CONTAINER "new_title_block_container")
 (setq OLD_TITLE_BLOCK (strcase "old title block-a4,title block,title block1,1a32,title block 3,title block 4,a1titleblock,-title_a1_detail"))

 ; delete new title block if exist?
 (remove_new_title_block)
 (setvar "ctab" "Model")

 ; select existing title blocks, reject from model space
 (if (and
      (setq modelEnt (insert_new_title_block_container))  ; insert new title block container
      (setq ss1 (ssget "_x" (list '(0 . "insert") (cons '2 (strcat OLD_TITLE_BLOCK ",`*U*")) (cons '410 "~Model"))))
     )
  (progn
   (foreach ename1 (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss1)))
    (setq AcDbBlkRef1 (vlax-ename->vla-object ename1))

    (if (wcmatch (strcase (vla-get-effectiveName AcDbBlkRef1)) OLD_TITLE_BLOCK)
     (progn
      (vla-getBoundingBox AcDbBlkRef1 'minpt 'maxpt)
      (setq p0 (vlax-safearray->list minpt))
      (setq p1 (vlax-safearray->list maxpt))

      (setvar "ctab" (cdr (assoc '410 (entget ename1))))
      (command "._pspace")

      (if (insert_new_title_block p0) 	; insert new title block
       (progn
        (setq AcDbBlkRef2 (vlax-ename->vla-object (entlast)))
        (setq attributes1 (LM:Unique (_getAttrData AcDbBlkRef1)))    

        (foreach AcDbAttrib2 (setq attributes2 (vlax-invoke AcDbBlkRef2 'GetAttributes))
         (setq tag2 (strcase (vla-get-tagString AcDbAttrib2)))
         (if (setq dat (assoc tag2 attributes1))
          (vla-put-textString AcDbAttrib2 (cdr dat))
         ); if
        ); foreach

        (foreach o attributes2
         (vlax-release-object o)
        )

        (vlax-release-object AcDbBlkRef2)
       ); progn
      ); if
      
     ); progn
    ); if
     
    (vla-delete AcDbBlkRef1) ; delete old title block
    (vlax-release-object AcDbBlkRef1) 
   ); foreach
  ); progn
 ); if

 ; delete origin block
 (if modelEnt
  (entdel modelEnt)
 )
  
 (setvar "ctab" savCTab)
  
 (setvar "attreq" savAttreq)
 (setvar "attdia" savAttdia)

 (command "._undo" "_end")
 (setvar "cmdecho" 1)
  
 (princ "\nDone.")
 (princ)
); c:attr2attr

 

0 Likes
Message 11 of 26

Sea-Haven
Mentor
Mentor

Just a comment we had a old title block and a new one was made, it had the same name as the old one and matched attributes etc all you had to was insert the new one and CAD asked "do you want to redefine". Then was all done. May be a simpler way for 4000 dwg's maybe test.

0 Likes
Message 12 of 26

skchui6159
Advocate
Advocate

@Moshe-A Seem not work in my computer. But thank you very much!💪 I have also write two lisp for this issue.

First, output to csv, then insert the block by insert.lsp (inprogress). But, Need to load "steal" and "delete_block" from Lee Mac.

0 Likes
Message 13 of 26

skchui6159
Advocate
Advocate

@Sea-Haven How can you do that?😅

0 Likes
Message 14 of 26

Moshe-A
Mentor
Mentor

@skchui6159  hi,

 

Good morning, i take back the BLOCK_CONTAINER thing (maybe i thought on some other options last night and over sleep night  i realized it is not needed 😀) so attached is an update. the change from your side is save the "new title block-a4" as standalone dwg file (block) in a folder that is on support files search path.

 

Moshe

 

0 Likes
Message 15 of 26

skchui6159
Advocate
Advocate

@Moshe-A Sorry, I can not run..

skchui6159_0-1752475646046.png

skchui6159_1-1752475685873.png, what is the problem?

 

0 Likes
Message 16 of 26

Moshe-A
Mentor
Mentor
Accepted solution

@skchui6159,

 

Attached attr2attr.zip contain 3 files:

 

attr2attr.lsp

"NEW BLANK BLOCK_A.dwg" - the file to run the lisp inside 3 "old TITLE BLOCK-A4" inserted with attribute with numbers as value - yes?!

1 in model space

1 in layout1

1 in layout2

"new TITLE BLOCK-A4.dwg"   - the new title block to replace.

 

What i want you to do is to release this zip file in your current working folder.

open  "NEW BLANK BLOCK_A.dwg".

load attr2atttr.lsp.

run the command.

 

The result:

The blocks  "old TITLE BLOCK-A4" from layout1 + layout2 were replaced with "new TITLE BLOCK-A4.dwg"  attributes value copied.

the block in model space is left intact 😀

 

please do only that and reply if it also working for you?

we need to do it in steps, please do not go to your next steps WScript or blending it in your code cause i have not control on that.

 

agree?

Moshe

 

 

0 Likes
Message 17 of 26

skchui6159
Advocate
Advocate

@Moshe-A 

The lisp works ok to transfer data!😁 It is ok for A4 title block. 

we need to do it in steps, please do not go to your next steps WScript or blending it in your code cause i have not control on that.

Sure😆

0 Likes
Message 18 of 26

Moshe-A
Mentor
Mentor

@skchui6159 ,

 

To give support for other blocks like "new title block-A1" you could change the NEW_TITLE_BLOCK value and change OLD_TITLE_BLOCK value to include the old A1 title blocks names.

if you want this to handle A4 + A1 on one run, could be done but i need to make some changes to code.

 

Moshe

 

0 Likes
Message 19 of 26

Moshe-A
Mentor
Mentor
Accepted solution

@skchui6159 ,

 

ok this one support A4 + A1 in one run

 

and the change is:

(setq TITLE_BLOCK_A4 (cons "new title block-a4" (strcase "old title block-a4,title block,title block1,title block 3,title block 4")))
(setq TITLE_BLOCK_A1 (cons "new title block-a1" (strcase "old title block-a1,1a32,a1titleblock,-title_a1_detail")))

 

two consts one for A4 and one for A1

each is a dotted pair list where the first item is the new title block name the second item is old title blocks name

assuming a layout has one title block, the program will look first for "old title block-a4" and if found, modify it than

continue to next layout (e.g does not look for "old title block-a1" in current layout) but if a4 is not found and a1 does, it will modify it.

 

enjoy

Moshe

 

 

0 Likes
Message 20 of 26

skchui6159
Advocate
Advocate

@Moshe-A 

Thanks for you help, also I want to ask, what should I put in the zip for A1 and A4 running the lisp?

0 Likes