Jump to content

need help to convert Between Enames and VLA-object


Recommended Posts

Posted (edited)

Hi.

I have a lisp that I need to assign an object to a VLA-Object (see attached code below) - see in the foreach loop,I'm trainig to assign a VLA object to obj2 variable.

I've read this article and follow the steps there.

I'm getting the Entity name in to the "enam" variable, but I'm failing to assign it to the VLA object.

the error I'm getting is this: ; error : Automation Error. Property [HASATTRIBUTES] not available

I also attached a sample drawing (EXP1.dwg) with 2 inserted blocks that I'm testing this lisp on it.

what I'm doing wrong in the code?

thanks,

Ari.

(defun c:add_SYS_ATT1 ( / ss i blk blks def AttObj obj2 tagname hh count)
    (vl-load-com)
    (setq tagname "SYSTEM")
    (setq hh (atof "36"))
    (and
       (setq ss (ssget '((0 . "INSERT"))))
       (setq i (sslength ss))
       (while (> i 0)
          (setq blk (cdr (assoc 2 (entget (ssname ss (setq i (1- i)))))))
          (if (not (vl-position blk blks))(setq blks (cons blk blks)))
       )
    )
       (foreach blk blks
                (Vlax-for obj (setq def (vla-item (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) blk)))
                (setq enam (vlax-vla-object->ename def))
                (setq obj2 (vlax-ename->vla-object enam))
                (setq count 0)
                (princ obj2)
                (
                  if (= (vla-get-hasattributes obj2) :vlax-true)
                    (foreach att (vlax-invoke obj2 'getattributes) 
                    (princ att)
                       (if ( = tagname (strcase (vla-get-tagstring att)))
                         (setq count (1+ count))
                       );;close if
                    );;close foreach
                );;close if 
                (princ count)                      
                       (
                          if (= count 0)
                            (progn								
                                  (setq AttObj 
                                    (vla-addattribute def
                                      36
                                      acAttributeModeInvisible
                                      ""
                                      (vlax-3D-point 72 84)
                                       "SYSTEM"
                                       ""
                                    ) ;;close vla-addattribute
                                  ) ;;close setq 
        	                   (vlax-put AttObj 'Alignment acAlignmentmiddle)			
        	                   (command "_.attsync" "_N" blk)
                            ) ;;close progon
                       );;close if

       );; close foreach
    (princ)
)
(vl-load-com) (princ)

  

 

Edited by aridzv
Posted (edited)

This cleans it up and follows a more logical flow. If you have any questions or need me to explain anything let me know.

 

(defun c:add_SYS_ATT1 (/ ss blk blk-lst atts-lst def AttObj obj2)
  (vl-load-com)
  (if (setq ss (ssget '((0 . "INSERT")))) ;change if to while repeats command if you keep selecting things
    (foreach e (vl-remove-if 'listp (mapcar 'cadr (ssnamex SS)))  ;makes a list of all entitys by ename in selection set and steps thought them one at a time
      (setq blk (cdr (assoc 2 (entget e))))	
      (if (not (vl-position blk blk-lst)) 
        (progn
          (setq blk-lst (cons blk blk-lst))
          (setq obj2 (vlax-ename->vla-object e)) 
          (setq atts-lst nil)                    ;clear list from last use
          (if (= (vla-get-hasattributes obj2) :vlax-true)
            (foreach att (vlax-invoke obj2 'getattributes)
              (setq atts-lst (cons (strcase (vla-get-tagstring att)) atts-lst))   ;make a list of all Attributs tag names to check rather then checking them all individually
            )   ;;close foreach
          )     ;;close if
          (if (not (member "SYSTEM" atts-lst))                                    ;checks list for "SYSTEM" could also use vl-position
            (progn
              (setq def (vla-item (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) blk))
              (setq AttObj (vla-addattribute def 36 acAttributeModeInvisible "" (vlax-3D-point 72 84) "SYSTEM" ""))
              (vlax-put AttObj 'Alignment acAlignmentmiddle)
              (command "_.attsync" "_N" blk)
            )   ;;close progn
          )     ;;close if
        )       ;;close progn     
      )         ;;close if
    )           ;;close foreach
  )             ;;close if
  (princ)
)
Edited by mhupp
Posted (edited)

@mhupp

WOW!! - THANKS!

many thanks for the clear code and the Detailed explanations - It will surely help me a lot in the future!!

The original code is not mine (of course...) - and that leads me to another 2 questions, if possible,about this line:

(setq def (vla-item (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) blk))

1. why the need to create def (what is the difference between def and blk)?

2. Can you help me understand the sequence of this line creation?

 

and again - many thanks!!

Ari.

 

*EDIT:

thanks for solving the issue clearing variable content with this line:  (setq atts-lst nil) ;clear list from last use 

I was looking for this VBA equivalent to "set object = nothing"... 🙂

Edited by aridzv
Posted
3 hours ago, aridzv said:

@mhupp

(setq def (vla-item (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) blk))

 

1. why the need to create def (what is the difference between def and blk)?

2. Can you help me understand the sequence of this line creation?

 

This line is setting up for the next line of code. Think of it as a map location saying in AutoCAD > Active Drawing > list of blocks in this drawing > of those blocks select the one named blk.  def is a location, were blk is just the name of the block. The next line of code is then saying create this attribute at the "def" location.

 

Here is a little example. when ever your using any vla-add function you first have to define the location where to add that item.

(defun C:Foo (/ spm dia cen cir)
  (vl-load-com)
  (setq spm (vla-get-modelspace (vla-get-activedocument (vlax-get-acad-object)))) ;set the location/area for model space
  (setq dia (getstring "\nCircle Radius: ")) 
  (setq cen (getpoint "\nCircle Center: "))
  (setq cir (vla-addcircle spm cen dia)) ;create a circle defined as cir in spm location/area
  ;you could then manipulate the circle by calling "cir" if you wanted.
)
  • Thanks 1
Posted
On 1/28/2022 at 2:42 PM, mhupp said:

 

This line is setting up for the next line of code. Think of it as a map location saying in AutoCAD > Active Drawing > list of blocks in this drawing > of those blocks select the one named blk.  def is a location, were blk is just the name of the block. The next line of code is then saying create this attribute at the "def" location.

 

@mhupp

Hi.

I have another question about the logic of this line and lisp - 

the lisp already create a list of blocks (ss) in the first "if",so we know that that list contain only blocks.

is there a way from here to go through that list,ivoke the attribute list for each block,check if that block contain a tag name "SYSTEM" (in that case..), and if not then create it?

like I wrote before - I'm not fluent with lisp, but my logic led me to that direction.

is it better logic or worse?

I'm looking to use this lisp as an exercise to try differant approch and learn more codding.

Thanks,

Ari.

Posted
(defun c:add_SYS_ATT1 (/ ss blk blk-lst atts-lst def AttObj obj2)
  (vl-load-com)
  (if (setq ss (ssget '((0 . "INSERT")))) 
  ;allows user to build a selection set of blocks 
    (foreach e (vl-remove-if 'listp (mapcar 'cadr (ssnamex SS)))
    ;steps though selection set with each entity by name
      (setq blk (cdr (assoc 2 (entget e))))	
      ;gets entity name of block
      (if (not (vl-position blk blk-lst)) 
      ;checks to see if block name is in a list
        (progn
          (setq blk-lst (cons blk blk-lst))
          ;adds block name to list
          (setq obj2 (vlax-ename->vla-object e)) 
          ;converts entity name to vla-object name
          (setq atts-lst nil)
          ;clear list from last use 
          (if (= (vla-get-hasattributes obj2) :vlax-true)
          ;checks if vla-object has attributes
            (foreach att (vlax-invoke obj2 'getattributes)
            ;steps through each attribute
              (setq atts-lst (cons (strcase (vla-get-tagstring att)) atts-lst))   
              ;make a list of all Attributs tag names
            )   
          )     
          (if (not (member "SYSTEM" atts-lst))
          ;checks list for "SYSTEM" 
            (progn
              (setq def (vla-item (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) blk))
              ;sets locaiton inside block
              (setq AttObj (vla-addattribute def 36 acAttributeModeInvisible "" (vlax-3D-point 72 84) "SYSTEM" ""))
              ;add attribute to def locaiton
              (vlax-put AttObj 'Alignment acAlignmentmiddle)
              ;alin attribute to middle
              (command "_.attsync" "_N" blk)
            )   ;;close progn
          )     ;;close if
        )       ;;close progn     
      )         ;;close if
    )           ;;close foreach
  )             ;;close if
  (princ)
)

 

Yes you could do that. It seems like the above lisp has more steps and would take longer but would run about the same amount of time or less. lets say you have 1000 blocks but their is only 25 unique blocks. so if you run this lisp on them its only going to step all the way though the entire lisp 25 times. the other 975 times its only going to process two lines of code before checking the next block.

 

(setq blk (cdr (assoc 2 (entget e))))   
(if (not (vl-position blk blk-lst))

 

I only took what was wrote before an brought it under one if statement. when writing lisp you should always think about what could happen. for instance with my lisp since everything is wrapped in an if statement if no blocks are found/selected it exits with out any errors. in your code when it reaches "(foreach blk blks" it would error saying something like undefined.

 

I'm not saying my way is better. I'm sure someone could come in here *cough* @ronjonp @BIGAL and do something in like 4 lines of code.

 

Posted (edited)

@mhupp

Thanks for the answer!!

It will take me some time to learn this code,

but there is one fundemental issue that i'm trying to figure out for some time:

this type of code - (setq blk (cdr (assoc 2 (entget e))))

"e" is set to a block entity,and that block entity do have a list of arguments,that is clear.

cdr means  "get the second item on that arguments list (or just any list..)".

in the dxf-reference_enu, both in the BLOCKS Section (Chapter 5 BLOCKS Section) and the insert section (Chapter 6 ENTITIES Section) the block name is indeed assoc code 2 (I've attached a screenshot from the dxf manual),so that is also clear to me why the use of assoc 2.

so why the use of cdr?

1. what is the logic Behind this, and why it is wrong to write "(setq blk (assoc 2 (entget e)))?

2. why do we need the entget when e is already set to a block object,which means that it an entity from the beginning?

 

thanks for patience...😀

Ari.

 

 

Capture.PNG

Capture1.PNG

Edited by aridzv
  • Like 1
Posted (edited)

e is a temporary variable inside the foreach function. all e is before the entget is the entity name  <Entity name: 3d99d260> think of it as a book number in the Dewey Decimal System in a library. the libray is the drawing the book is the entity. you don't know whats in the book unless you go get it that's what entget does.

 

It takes the entity name and looks in the drawings your working with returning

((-1 . <Entity name: 3d99d260>) (0 . "INSERT") (5 . "1E7") (330 . <Entity name: 3d998de0>) (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "0") (370 . -1) (100 . "AcDbBlockReference") (66 . 1) (2 . "PVC TEE SW-THREADED 63x2In") (10 374.0 116.0 0.0) (41 . 1.0) (42 . 1.0) (43 . 1.0) (50 . 0.0) (70 . 0) (71 . 0) (44 . 0.0) (45 . 0.0) (210 0.0 0.0 1.0))

 

Asso 2 is saying tell me whats list that starts in 2 is and returns the following.

(2 . "PVC TEE SW-THREADED 63x2In")

 

cdr says return everything but the first item.

"PVC TEE SW-THREADED 63x2In"

If you used it on 10 the insertion point it would return a list with three items

(374.0 116.0 0.0)

 

To answer your questions #1 ill refer to the above entget

(setq blk (assoc 2 (entget e))) = (2 . "PVC TEE SW-THREADED 63x2In")
  This will error when using it in def becaues its only looking for a name in quotes
(setq def (vla-item (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) blk))
  This would work but why wait just use cdr when your setting the variable
(setq def (vla-item (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) (cdr blk)))

 

image.png.044cb988df0946714a048ddea84cff2f.png

 

Answer #2

Its not. all e is at that point is the entity name. In some cases the entity name in question could not be valid anymore. it could have been deleted or modified somehow giving it a different entity name. entget is saying return what is in your records for this name.

 

Use this to look at items in your drawing. this will show all the dxf codes

;;----------------------------------------------------------------------------;;
;; Dump all DXF Group Data             
(defun C:DumpIt (/ SS)
  (if (setq SS (ssget))
    (foreach ent (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss)))
      (mapcar 'print (entget ent '( "*")))
    )
  )
  (textscr)
  (princ)
)

 

This will show you the same and probably more in the visual lisp codes

;;----------------------------------------------------------------------------;;
;; Dump all methods and properties for selected objects               
(defun C:VDumpIt (/ SS)
  (if (setq SS (ssget))
    (foreach ent (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss)))
      (vlax-Dump-Object (vlax-Ename->Vla-Object ent) t)
    )
  )
  (textscr)
  (princ)
)

 

 

 

 

Edited by mhupp
Posted (edited)

@mhupp

thanks again!

last question about this issue ("...just use cdr.."):

why using cdr and not get the full list?

why ommiting the first item on the list,the entity name (<Entity name: 3d99d260> in this case)?

is it possible to use (setq blk (assoc 2 (entget e)))?

thanks,

Ari.

 

Edited by aridzv
Posted

Answered above. right above the screen shot.

Posted (edited)

@mhupp

you're right, I missed it..

thanks,

Ari.

Edited by aridzv

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
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

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