Jump to content
SteveK

Replacing blocks across multiple layouts

Recommended Posts

SteveK

Hello,

I'm also interested in knowing how to insert blocks across multiple layouts but this may hit two birds with one stone.

Anyways, I'm trying to replace blocks across several layouts. I was doing this simply by changing dxf code 2. The problem is if the new block does not already exist in the drawing it doesn't work. Which is ok, I just use the insert command to insert the block first. But then I've got an extra block in my drawing... There must be a better way, here's my code:

 

(defun C:brl (/ ss
         sslst
         block
         tempUnits)
 
 (setq block "BLOCK NAME")
 (If (NOT (tblsearch "BLOCK" block))
   (Progn
     (setq tempUnits (getvar "INSUNITS"))
     (setvar "INSUNITS" 0)
     (command "_-INSERT" (strcat "R:\\AutoCAD\\Blocks\\" block ".dwg") "0,0" "1" "" "0" "Attribute value")
     (setvar "INSUNITS" tempUnits)
     ))
 
 (foreach layout (layoutlist)
   (setq ss (ssget "_X" (list (cons 0 "INSERT")
                  (cons -4 "<or")
                  (cons 2 "IFA") ; Different blocks
                  (cons 2 "IAC")
                  (cons 2 "IFC")
                  (cons -4 "or>")
                  (cons 410 layout))))
   (If ss
     (Progn
   (setq sslst (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss))))
   (mapcar '(lambda (x)
            (entmod (subst (cons 2 block) (assoc 2 (entget x)) (entget x))))
            (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss))))

   ))
   )
 (princ)
 )

Thanks,

 

PS. if you're wondering what the point is of a foreach loop for all layouts this is only temporary as CAB's posted a layout select program (page 5 of Some Simple Autolisp Questions.) so afterwards I'll be changing (layoutlist) to a tab list.

 

Aside: How do you link to specific posts in threads? :)

Share this post


Link to post
Share on other sites
Patrick_35

Hi

 

An example

(defun c:blr(/ bl block doc file sel)
 (setq block "BLOCK NAME"
   doc (vla-get-activedocument (vlax-get-acad-object))
 )
 (vla-startundomark doc)
 (or (tblsearch "block" block)
     (not (setq file (findfile (strcat "R:\\AutoCAD\\Blocks\\" block ".dwg"))))
   (vla-delete (vla-insertblock (if (eq (getvar "cvport") 1)
                  (vla-get-paperspace doc)
                  (vla-get-modelspace doc)
                )
                (vlax-3d-point '(0.0 0.0 0.0))
                file
                1
                1
                1
                0
       )
   )
 )
 (and (tblsearch "block" block)
      (ssget "x" (list (cons 0 "insert") (cons 2 "IFA,IAC,IFC") (cons 67 1))) ; dxf 67=1 only paperspace
   (progn
     (vlax-for bl (setq sel (vla-get-activeselectionset doc))
   (vla-put-name bl block)
     )
     (vla-delete sel)
   )
 )
 (vla-endundomark doc)
 (princ)
)

@+

Share this post


Link to post
Share on other sites
SteveK

Thanks Patrick, though I'm only a beginner with vlisp that makes sense and works well.

A couple of learning questions I have: cvport 1 determines whether I'm currently in paperspace? I tried running the code in modelspace and it still changed all the blocks in paperspace, how does vla-get-paperspace & vla-get-modelspace impact the code?

 

 

On another note:

If I have a list of layouts I want to use (ie not all layouts in the drawing) I suppose I could put a foreach loop before the ssget function and add (cons 410 layout) but that would make lots of loops. I'm wondering what is the best way to get that list into something I could put directly into the ssget function?

eg. If I could get the list ("Sheet 1" "Sheet 3" "Sheet 5" "etc")

into a string and then put it into the ssget filter like

(cons 410 "Sheet 1, Sheet 3, Sheet 5, etc"), that'd be the best method I reckon.

 

EDIT: Nevermind unless there's a better way, I just thought of this: :)

(setq laylist (layoutlist)
     laystr (car laylist))
(repeat (- (length (layoutlist)) 1)
 (setq laylist (cdr laylist)
   laystr (strcat laystr "," (car laylist)))
 )
[color=Gray](setq laystr (strcat "\"" laystr "\""))[/color]
 (princ laystr)

Thanks

Share this post


Link to post
Share on other sites
Lee Mac

Or, just this should do:

 

(setq str (car (layoutlist)))
(foreach lay (cdr (layoutlist))
 (setq str (strcat str (chr 44) lay)))

Share this post


Link to post
Share on other sites
SteveK

Thanks Lee, yeah that works.

Which is handy actually because I was finding with my method for some reason the first and last layouts weren't included in the selection set...?

Share this post


Link to post
Share on other sites
Lee Mac

Haven't really looked at your repeat loop properly, but on first glance, there is no need to use:

 

(setq laystr (strcat "\"" laystr "\""))

 

The variable laystr would be interpretted as:

 

""layout1,layout2,layout3""

Share this post


Link to post
Share on other sites
SteveK

Ahh, that'd do it.

 

Okay, everything is sweet with my program now; there was another thing I wanted to add to it, thatis, there is a single attribute in the block I want to change. Now I can do this with autolisp (see Patrick's edited code below) but it looks terrible amoung all the vlisp coding. Is it easy to do in vlisp? Nothing too complex :)

 

(defun rplce_Issue_blks(block tablist rev_date / bl block doc file sel
           laylist laystr
           ss subenlst)
 (vl-load-com)
 (setq doc (vla-get-activedocument (vlax-get-acad-object))
 )
 (vla-startundomark doc)
 (or (tblsearch "block" block)
     (not (setq file (findfile (strcat "R:\\AutoCAD\\Blocks\\" block ".dwg"))))
   (vla-delete (vla-insertblock (if (eq (getvar "cvport") 1)
                  (vla-get-paperspace doc)
                  (vla-get-modelspace doc)
                )
                (vlax-3d-point '(0.0 0.0 0.0))
                file
                1
                1
                1
                0
       )
   )
 )

; Added layouts
(setq laystr (car tablist))
(foreach lay (cdr tablist)
 (setq laystr (strcat laystr (chr 44) lay)))
 ;(princ laystr)

 (and (tblsearch "block" block)
      (ssget "_x" (list (cons 0 "insert") (cons 2 "IFA,IAC,IFC") (cons 410 laystr) (cons 67 1))) ; dxf 67=1 only paperspace
   (progn
     (vlax-for bl (setq sel (vla-get-activeselectionset doc))
   (vla-put-name bl block)
   )
     (vla-delete sel)
   )
 )

[color=Red]  ; This is deffinately Bad Practice (using autolisp in a vlisp routine), but I don't know how to change an attribute any other way...[/color]
 [color=Blue][b] (If (setq ss (ssget "_x" (list (cons 0 "insert") (cons 2 "IFA,IAC,IFC") (cons 410 laystr) (cons 67 1))))
(Progn
 (foreach en (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss)))
   (If (= (cdr (assoc 0 (setq subenlst (entget (entnext en))))) "ATTRIB")
     (entmod (subst (cons 1 rev_date) (assoc 1 subenlst) subenlst))
     (Princ "Couldn't change the date in the new Issue Blocks\n"))
   ))(Princ "No Blocks found\n"))[/b][/color]
[color=Red]  ; End of Bad Practice[/color]
 
 
 (vla-endundomark doc)
 (princ)
)

Also, I would still like to know to impact of the vla-get-paperspace & vla-get-modelspace functions in Patricks code.

Share this post


Link to post
Share on other sites
Patrick_35

Hi

 

Try this

 

(defun rplce_Issue_blks(block tablist rev_date / bl block doc file sel
           laylist laystr
           ss subenlst)
 (vl-load-com)
 (setq doc (vla-get-activedocument (vlax-get-acad-object))
 )
 (vla-startundomark doc)
 (or (tblsearch "block" block)
     (not (setq file (findfile (strcat "R:\\AutoCAD\\Blocks\\" block ".dwg"))))
   (vla-delete (vla-insertblock (if (eq (getvar "cvport") 1)
                  (vla-get-paperspace doc)
                  (vla-get-modelspace doc)
                )
                (vlax-3d-point '(0.0 0.0 0.0))
                file
                1
                1
                1
                0
       )
   )
 )

; Added layouts
 (setq laystr (car tablist))
 (foreach lay (cdr tablist)
   (setq laystr (strcat laystr (chr 44) lay))
 )
 ;(princ laystr)
 (and (tblsearch "block" block)
      (ssget "_x" (list (cons 0 "insert") (cons 2 "IFA,IAC,IFC") (cons 410 laystr) (cons 67 1))) ; dxf 67=1 only paperspace
   (progn
     (vlax-for bl (setq sel (vla-get-activeselectionset doc))
       (vla-put-name bl block)
[color=Red][b]    (and (setq att (vlax-invoke bl 'getattributes))
     (vla-put-textstring (car att) rev_date)
   )
[/b][/color]      )
     (vla-delete sel)
   )
 )

 ; This is deffinately Bad Practice (using autolisp in a vlisp routine), but I don't know how to change an attribute any other way...
;   (If (setq ss (ssget "_x" (list (cons 0 "insert") (cons 2 "IFA,IAC,IFC") (cons 410 laystr) (cons 67 1))))
;(Progn
;  (foreach en (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss)))
;    (If (= (cdr (assoc 0 (setq subenlst (entget (entnext en))))) "ATTRIB")
;      (entmod (subst (cons 1 rev_date) (assoc 1 subenlst) subenlst))
;      (Princ "Couldn't change the date in the new Issue Blocks\n"))
;    ))(Princ "No Blocks found\n"))
 ; End of Bad Practice
 
 (vla-endundomark doc)
 (princ)
)

No tested

 

@+

Share this post


Link to post
Share on other sites
Lee Mac

Just as an aside Steve, to explain a little more about the method that Patrick has provided, which will help you if you are dealing with more than one attribute in a block.

 

There are two ways to get a list of attributes in the block, as shown below. Each will return a different data type, either a list or variant.

 

Lets say that your Block Object is set to a variable Obj.

 

(setq Obj (vlax-ename->vla-object (car (entsel "\nSelect Block: "))))

Now, there are two ways to retrieve the attributes, and both depend also upon whether you want to get at Constant Attributes also.

 

Using Patricks Method:

 

(vlax-invoke Obj 'GetAttributes)

Will return a list of attributes in the block, excluding Constant Attributes.

 

Something like this will be returned:

 

(#<VLA-OBJECT IAcadAttributeReference 0e13b9ec> 
#<VLA-OBJECT IAcadAttributeReference 0e13b76c>)

An alternative to this, and a method that you may be more accustomed to seeing is:

 

(vlax-safearray->list
 (vlax-variant-value
   (vla-getAttributes Obj)))

Which will again, provide you with a list of AttributeReference Objects to manipulate how you please.

 

To include constant attributes you will need to invoke another method, and append the results, for example:

 

(append
 (vlax-invoke Obj 'GetAttributes)
 (vlax-invoke Obj 'GetConstantAttributes)
) ; End Append

This will return a list of all attributes in the block, for example:

 

(#<VLA-OBJECT IAcadAttributeReference 16ecdd8c>
#<VLA-OBJECT IAcadAttributeReference 16eca92c> 
#<VLA-OBJECT IAcadAttribute 0e1e4ae4>)

The alternative to this, using the other method as described above, would be something like:

 

(apply 'append
 (mapcar
   (function
     (lambda (x)
       (vlax-safearray->list
         (vlax-variant-value x))))
   (list (vla-getAttributes Obj)
         (vla-getConstantAttributes Obj))))

Or something similar, depending whether you want to use mapcar or not.

 

To take this a step further, what if we want to get all attributes from a block, but we don't know whether it has constant attributes or not.

 

Using this method:

 

(append
 (vlax-invoke Obj 'GetAttributes)
 (vlax-invoke Obj 'GetConstantAttributes)
) 

Would not cause a problem, as the second list would just be an empty list [ '() or nil ], which can successfully be appended.

 

However, you cannot convert an empty safearray into a list, as you will receive an error regarding the index of the safearray.

 

This can be avoided with some error trapping:

 


  (append
    (if
      (not
        (vl-catch-all-error-p
          (setq atArr
            (vl-catch-all-apply
              'vlax-safearray->list
            (list
              (vlax-variant-value
                (vla-GetAttributes Obj)))))))
          atArr)
    (if
      (not
        (vl-catch-all-error-p
          (setq caArr
            (vl-catch-all-apply
              'vlax-safearray->list
            (list
              (vlax-variant-value
                (vla-GetConstantAttributes Obj)))))))
            caArr)
    ) ; End Append

Using, vl-catch-all-apply will prevent a program crashing when an error occurs in the operation of converting the safearray to a list. And vl-catch-all-error-p will return T if vl-catch-all-apply returns an error object (#), as opposed to the result that we need (a list).

 

Another way to check for an empty safearray is to check the upper bound of a dimension of the safearray.

 

A safearray returned by the vla-getConstantAttributes method has only one dimension, (a list of VLA-objects), and so we can check the upper bound of this dimension using:

 

 ([color=Blue]vlax-safearray-get-u-bound[/color]
   (vlax-variant-value
     (vla-getConstantAttributes Obj)) [color=Blue]1[/color]) ; First dimension

A result of -1 for the above indicates that the safearray is empty, and so, a simple conditional could be used to check for this, before proceeding to convert the safearray to a list.

 

Note: Remember that the upper bound of a safearray is one fewer than the number of elements that the dimension contains, as it is indexed from zero. Hence a block with 3 attributes, will return a safearray of one dimension with upper bound of 2.

 

I hope this helps you understand this process a little better.

 

If you have any further questions about anything I have posted, just ask.

 

Cheers,

 

Lee

Share this post


Link to post
Share on other sites
SteveK

Patrick thanks, it works - 2 functions, how simple is that! :D

 

Lee, that is a huge help, I will have more questions but I gotta chew on all that's here first. The obvious question though is, why would anyone use the vlax-safearray method (3 functions) when vlax-invoke (1 function) is so much simpler? I'm assuming it's because the longer method is the common path of learning vlisp. But still, I might just skip to invoke :wink:

 

Thanks!

Share this post


Link to post
Share on other sites
Lee Mac

Lee, that is a huge help, I will have more questions but I gotta chew on all that's here first. The obvious question though is, why would anyone use the vlax-safearray method (3 functions) when vlax-invoke (1 function) is so much simpler? I'm assuming it's because the longer method is the common path of learning vlisp. But still, I might just skip to invoke :wink:

 

I'm happy to help Steve,

 

As for your question - I suppose it depends on what data return you are looking for. Some functions use lists, whereas others use variants as input.

 

for example,

 

If you wanted to use the centre of a circle (set to variable obj) in a move operation, you could use something like this to get the centrepoint:

 

(setq pt (vlax-get Obj 'Center))

 

Which would return a list:

 

(12.4 43.4 0.0)

 

And then you would want to use it in the move operation:

 

(vla-move Obj 
 (vlax-3D-point [b]pt[/b]) ...)

 

But, as we know that the return of:

 

(setq pt (vla-get-Center Obj))

 

Is a variant, then we don't need to convert it to one in the move operation using (vlax-3D-point pt):

 

(vla-move Obj [b]pt[/b]...)

 

So, you see - sometimes we want to deal with lists, and sometimes we want to stick to variants.

 

Hopefully this makes things clearer.

 

Lee

Share this post


Link to post
Share on other sites
SteveK

Yeah good example, that makes sense Lee. I suppose while I'm new to vlisp I might stick with one.

 

 

This is a bit of tangent to the thread subject but I just have another general vlisp question, I've looked up the help file for these functions and while it makes sense it'd be nice if someone could tell me the necessity of them in dummy language.

I notice in a lot of vlisp programs there's these functions at the start:

(setq doc (vla-get-activedocument (vlax-get-acad-object))
                  (vla-get-paperspace doc)
                  (vla-get-modelspace doc)

How do these functions (particularly the later two) affect the output? For instance the program Patrick wrote at the start of this code seems to work for paperspace even if I run it from modelspace...

 

 

And the necessity of the function function is another function (:?) I can't get my head around, but that's only if someone's not too busy.

 

Thanks a lot for your help.

Share this post


Link to post
Share on other sites
Lee Mac

Ok, Steve... one at a time :)

 

First, the "doc" call.

 

OK, so a lot of functions in VLisp reference the document object, and retrieving this object through:

 

(vla-get-ActiveDocument
 (vlax-get-acad-object))

multiple times is not only very slow for the program, but also considered bad practice. Hence, for most programmers, if they know that they are going to be using the document object more than once, then they will just call it first and set it to a variable to save calling (vlax-get-acad-object) multiple times.

 

 

Ok... now onto the paperspace and modelspace objects.

 

When using such functions as:

 

(vla-addline
(vla-addcircle
(vla-insertblock
etc etc

These functions take, as their first argument, the block into which you are performing the operation. Now, do not get confused here with an AutoCAD "block".

 

A VL block can be either a block defintion (AutoCAD Block definition), or the Paperspace/ModelSpace objects.

 

Hence, a program needs to know what space the user is currently working in, so that the functions can insert objects into the correct space. So, the programmer will usually perform some kind of conditional check, either using the TILEMODE sys var, or maybe the vla-get-ActiveSpace function, which returns a similar result - and then call either the Paperspace or Modelspace block accordingly.

 

I think I will explain the function function in a different post, so as not to confuse matters.

 

Lee

Share this post


Link to post
Share on other sites
Lee Mac

Ok, so the function function.

 

This tells the interpreter/compiler (the thing that turns the code into machine talk), that the argument you are supplying is to be treated as a function.

 

You have probably seen this in many mapcar/lambda expressions:

 

(mapcar
 (function
   (lambda (x y)
     (...

 

This is equivalent to writing:

 

(mapcar '(lambda (x y) (...

 

[Except that the use of function is quicker for the program]

 

So, just as the apostrophe tells the interpreter not to evaluate the expression following it, but to take it as an argument. The function symbol, tell the intepreter to regard the expression as a function in itself, and further take it as an argument.

 

Some more info on the apostrophe can be found here:

http://www.cadtutor.net/forum/showpost.php?p=258390&postcount=20

 

Hope this helps,

 

Lee

Share this post


Link to post
Share on other sites
SteveK

Thanks Lee. I'm still a bit unsure about what functions need to reference the

(vla-get-ActiveDocument (vlax-get-acad-object))

but I'm sure it'll become more obvious as I study more vlisp programs.

 

 

 

Interesting, I knew about the quote but I did not know that function is quicker. In that case I might use it instead. There must be limits though, I see no one using it in:

(ssget "_X" (function ((0 . "LINE")(62 . 3))))

Thankyou again for your help, Lee. You're a household name these days :P

Share this post


Link to post
Share on other sites
Lee Mac
Thanks Lee. I'm still a bit unsure about what functions need to reference the
(vla-get-ActiveDocument (vlax-get-acad-object))

but I'm sure it'll become more obvious as I study more vlisp programs.

 

 

 

Interesting, I knew about the quote but I did not know that function is quicker. In that case I might use it instead. There must be limits though, I see no one using it in:

(ssget "_X" (function ((0 . "LINE")(62 . 3))))

Thankyou again for your help, Lee. You're a household name these days :P

 

Woah....

 

You can only use "function" when you are declaring a function....

 

The list that you are using in ssget is not a function, but a list.

 

The alternative to the apostrophe in that case is:

 

(ssget "_X" (quote ((0 . "LINE")(62 . 3))))

 

Sorry, for the confusion - I should've made that clearer.

 

Just to clarify, in the mapcar call for example, you could use:

 

(mapcar (function +) (quote (1 2 3)) (quote (4 5 6)))

 

[ Just as an aside, I don't think the quote function is any quicker than the apostrophe ]

Share this post


Link to post
Share on other sites
Lee Mac

As for functions that use the document object.

 

You provided two of them yourself:

 

(vla-get-paperspace doc)
(vla-get-modelspace doc)

 

Others can be:

 

(vla-get-layers doc)
(vla-get-layouts doc)
(vla-get-linetypes doc)
(vla-get-blocks doc)
etc etc

 

Lee

Share this post


Link to post
Share on other sites
SteveK

Thanks for clarifying. And sorry, what was I thinking... function on a list. :oops:

 

...I wish there was a way of collating all these theory lessons into one place; they always seem to come around off the back of a program thread.

Share this post


Link to post
Share on other sites
Lee Mac
Thanks for clarifying. And sorry, what was I thinking... function on a list. :oops:

 

...I wish there was a way of collating all these theory lessons into one place; they always seem to come around off the back of a program thread.

 

I know what you mean - I think there is going to soon be a section in the FAQ for LISP tutorials, but at the moment, I am just keeping a log of the links to each post for future reference. :)

Share this post


Link to post
Share on other sites
SteveK

Hello,

I don't really want to start a new thread with this question, I was just wondering how I find what layout an object is on? (vla-get-layout obj) doesn't seem to be recognized.

Share this post


Link to post
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...