Jump to content

Understanding AutoLISP - a progression


Recommended Posts

Posted

Hello AutoLISPers

 

I've created this thread in order to have a centralized location for seemingly random questions to do with AutoLISP and any of it's related languages. I use the term 'seemingly random' to distinguish between topics related to a specific lisp routine, and these, which I hope to produce more general knowledge over any given function, or portion of a routine, so that new users may gain an understanding of lisp that enables them to create or assist in the creation of autolisp routines themselves.

 

 

;;;;;;;;;;;;;;;;;;;;;;let the knowledge begin;;;;;;;;;;;;;;;;;;;;;;;;;

Posted

Reposting from a related thread, into this thread because the information requested would be useful in a variety of situations, rather than the specific topic listed that I originally posted this into.... (which is http://www.cadtutor.net/forum/showthread.php?81496-Select-blocks-with-certain-field-value-or-parameter-value) in case anyone would care to view it. I'll just jump right in, and respond once I've a response...

 

Lee Mac, can you go over the methods required to select or modify a specific attribute within an attributed block?, and which method of selection would typically be the best to use for routine attributed block modifications?

I've noticed that there are many ways to acquire dxf information on blocks, namely ssget and nentsel, and entsel, all returning different lists.

It would be fantastic to know which methodology would require which functions to get to a specific piece of information within the block.

Using autoLISP, (rather than vlisp) and the aforementioned methods on the same block in the same drawing, these are what I get returned to me.

 

Example #1:

(setq blk (car (entsel))      ; user input to select an item and assign blk to it's entity name, the first item returned in the list given by entsel.
[color=black][font=Calibri](if (assoc 66 (entget blk))         ; if statement requiring that there be attributes in previous entity, checking assoc 66 to return "1" and not "0", has attributes[/font][/color]

this next line (after the setq) does many functions in one wrapper, i'll attempt to break each step down to the best of my knowledge:

[color=black][font=Calibri][color=black][font=Calibri](setq att blk)                                                                                                                        ;sets att to blk, redefines as att[/font][/color][/font][/color]
[color=black][font=Calibri][color=black][font=Calibri](while (= (cdr (assoc 0 (entget (setq att(entnext att))))) "ATTRIB") [/font][/color][/font][/color]
[color=black][font=Calibri][color=black][font=Calibri];;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;[/font][/color][/font][/color]
[color=black][font=Calibri][color=black][font=Calibri](entnext att)[/font][/color][/font][/color]

<Entity name: 7ffff607380>

(setq att (entnext att))

<Entity name: 7ffff607380>

This return I am not sure of, it appears to be the entity name of the attributes? Would be nice to have this one explained, but I will continue

 

(entget att)

((-1 . <Entity name: 7ffff607380>) (0 . "ATTRIB") (330 . <Entity name: 
7ffff607370>) (5 . "228") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . 
"0") (100 . "AcDbText") (10 8.71875 9.1119 0.0) (40 . 0.125) (1 . "12\"") (50 . 
1.5708) (41 . 0. (51 . 0.0) (7 . "Standard") (71 . 0) (72 . 1) (11 8.65625 
9.25 0.0) (210 0.0 0.0 1.0) (100 . "AcDbAttribute") (280 . 0) (2 . "SIZE") (70 
. 0) (73 . 0) (74 . 2) (280 . 0))

(while (= (cdr (assoc 0 (entget att)))) "ATTRIB")

 

So this line wants to check that the assoc 0 of the list returned by (entget att) is the quoted text, i.e.

the "cdr" (2nd item) within this list:

((-1 . <Entity name: 7ffff607380>) (0 . "ATTRIB") (330 . <Entity name: 
7ffff607370>) (5 . "228") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . 
"0") (100 . "AcDbText") (10 8.71875 9.1119 0.0) (40 . 0.125) (1 . "12\"") (50 . 
1.5708) (41 . 0. (51 . 0.0) (7 . "Standard") (71 . 0) (72 . 1) (11 8.65625 
9.25 0.0) (210 0.0 0.0 1.0) (100 . "AcDbAttribute") (280 . 0) (2 . "SIZE") (70 
. 0) (73 . 0) (74 . 2) (280 . 0))

Should be "ATTRIB", which we can clearly see that it is, then to preform the following functions, again as long as the assoc 0 is "ATTRIB", however I am missing the connection between this list and the original block selection, it was given via the entget command but the block was selected using the entsel, why is this? Obviously one gets user input, but it would seem to me that there could be a quicker way to do this, as it's causing another disconnect for me at least, but I will continue..

 

[color=black][font=Calibri](setq val    (cdr (assoc 1(entget att)))                          ; value[/font][/color]
[color=black][font=Calibri]          vallst (cons val vallst)                          ; value list[/font][/color]

 

So from here it sets a var to the value of assoc 1 within (entget att), which in the previous list we can see has an attribute value of "12\"", if I'm understanding correctly.

The next line that uses the cons function to append val to the front of each of it's own definitions is confusing as the variable being defined is being used in the definition of the variable itself, but i'm sure there is a reasonable explanation for this. Perhaps following a more experienced coders logic in obtaining this information would be useful for myself and others like myself attempting to gain an understanding on manipulating blocks via autolisp.

Thanks and cheers. hopefully this posts correctly now!

Posted

Let me explain a bit why this is necessary for me to understand.

 

(DEFUN C:RS (/ Blks ENT1 IP oldatt)
;;; Erases all blocks named "revtext2"
;;; (ax:EraseBlock doc "revtext2")
(defun ax:EraseBlock (doc bn / layout i)
(vl-load-com)
(setq doc (vla-get-ActiveDocument (vlax-get-acad-object)))
 (vlax-for layout (vla-get-layouts doc)
   (vlax-for i (vla-get-block layout)
     (if (and
           (= (vla-get-objectname i) "AcDbBlockReference")
           (= (strcase (vla-get-name i)) (strcase bn))
         )
       (vla-Delete i) 
     )
   )
 )
)
(setq Blks (ssget "_x" '((0 . "INSERT")(2 . "IFC stamp"))))
(SETQ ENT1 (ENTGET (ENTLAST)))
(SETQ IP (CDR (ASSOC 10 ENT1)))
(ax:EraseBlock doc "IFC stamp")
(setq oldatt (getvar "ATTREQ"))
(setvar "ATTREQ" 0)
(COMMAND "INSERT" "Y:/Piping Department/Blocks/Stamps/BID_ONLY.DWG" IP "" "" "" "08/30/13")
(setvar "ATTREQ" oldatt)
)

 

Is what I've been working on, and it will delete the specified block, but when inserting the new block it puts it in the middle of the drawing and not at the insertionpoint of the old drawing. With more understanding, I would be able to find out why it is inserting it in a set location that is NOT the same location of the block that was deleted, even though that is how the routine is written, to my understanding at least!

 

Thanks in advance for any who take the time to view and reply to this thread.

Posted

And here is the issue that has caused me to post all of that yesterday, or another issue related to it that I would do well to understand these questions, so that I could have debugged this a bit quicker, anyhow...

(DEFUN C:RS (/ SS ENT1 IP)
(setq ss (ssget "_X" '((0 . "INSERT") ( 2 . "IFC stamp"))))
(SETQ ENT1 (ENTGET (ENTLAST)))
(SETQ IP (CDR (ASSOC 10 ENT1)))
(COMMAND "ERASE" "L" "")
(COMMAND "INSERT" "Y:/Piping Department/Blocks/Stamps/BID_ONLY.DWG" IP "" "" "" "08/6/13")
)

 

This routine, though once was working for us to delete a specified block and insert another in it's place, stopped working for us.

The (SETQ ENT1 (ENTGET (ENTLAST))) line was pulling the wrong block as the "entlast" and I am not sure why, because it was adding the correct block to the selection set and holding it there.

But was giving information about a wrong block even though the correct block was added to the selection set in the previous command.

Why?

...it was causing the insertionpoint of our new block to be any of random places on our drawing, depending on which block was selected in the (entlast) line, as mentioned just above.

I was able to do some scouring and was able to construct the following routine in it's place, devoid of any (entlast) calls. Bravo for that, self, problem solving, but if any guru would care to explain the reason behind my troubles I would be grateful.

The following works correctly and does as we expect,

(DEFUN C:RS (/ Blks ENT1 IP oldatt)
;;; Erases all blocks named "IFC stamp" and replaces with "BID_ONLY"
;;; (ax:EraseBlock doc "IFC stamp")
;;; routine works as expected
(defun ax:EraseBlock (doc bn / layout i)
(vl-load-com)
(setq doc (vla-get-ActiveDocument (vlax-get-acad-object)))
 (vlax-for layout (vla-get-layouts doc)
   (vlax-for i (vla-get-block layout)
     (if (and
           (= (vla-get-objectname i) "AcDbBlockReference")
           (= (strcase (vla-get-name i)) (strcase bn))
         )
       (vla-Delete i) 
     )
   )
 )
)

(setq IP (cdr (assoc 10 (entget (ssname (ssget "_X" '((2 . "IFC stamp"))) 0)))))
(ax:EraseBlock doc "IFC stamp")
(setq oldatt (getvar "ATTREQ"))
(setq oldattd (getvar "ATTDIA"))
(setvar "ATTREQ" 1)
(setvar "ATTDIA" 0)
(COMMAND "INSERT" "Y:/Piping Department/Blocks/Stamps/BID_ONLY.DWG" IP "" "" "" "8/30/13")
(setvar "ATTREQ" oldatt)
(setvar "ATTDIA" oldattd)
)

 

Differences are fairly striking, the new one uses a combination of lisp and vlisp, though I wish I could have had the correct way to do this in lisp explained to me as I would truly prefer to understand the code that we use on our drawings, and although I'm exactly sure that the vlisp portion of this code is meant to delete out the old block, I would still like to know which each line does specifically, but being vlisp I would need to have it explained in depth. That said, the rest is fairly simple, setting "ip" to be the assoc 10 of the selected block to be replaced. We wrapped the ssget in the setq for the insertion point to ensure that it would get the IP of the correct block this time, the other lines call the vlisp subfunction to erase the old block after the insertion point is set to a var, then some block attribute system vars are modified so that when the new block is inserted the attributes will be filled correctly.

 

Thanks to anyone in advance who will care to go over the entlast, or any of the (ent) questions I've laid out above this post.

Cheers

Posted

Hi bhull1985,

 

your code can't work as desired, because the auxiliary function you're using, the ax:EraseBlock function, will erase all blocks named "xxxxx", so, when you call the function with

(ax:EraseBlock doc "IFC stamp")

the ax:EraseBlock function, will erase all "IFC stamp" blocks in the dwg, let me try to explain what your code do

(DEFUN C:RS (/ Blks ENT1 IP oldatt)
;;; Erases all blocks named "revtext2"
;;; (ax:EraseBlock doc "revtext2")
(defun ax:EraseBlock (doc bn / layout i)
(vl-load-com)
(setq doc (vla-get-ActiveDocument (vlax-get-acad-object)))
 (vlax-for layout (vla-get-layouts doc)
   (vlax-for i (vla-get-block layout)
     (if (and
           (= (vla-get-objectname i) "AcDbBlockReference")
           (= (strcase (vla-get-name i)) (strcase bn))
         )
       (vla-Delete i) 
     )
   )
 )
)
(setq Blks (ssget "_x" '((0 . "INSERT")(2 . "IFC stamp"))));; select all blocks named "IFC stamp"
(SETQ ENT1 (ENTGET (ENTLAST)));; sets the ENT1 with the entity's definition data returned by entlast function, 
        ;; that returns the name of the last entity in the drawing, possibly not a "IFC stamp" block...
(SETQ IP (CDR (ASSOC 10 ENT1)));; sets the IP with whatever comes associated with dxf 10 in ENT1...
(ax:EraseBlock doc "IFC stamp");; will erase all blocks named "IFC stamp"
(setq oldatt (getvar "ATTREQ"));; sets the oldatt with the value from ATTREq SysVar
(setvar "ATTREQ" 0);; sets the ATTREQ SysVar to 0
(COMMAND "INSERT" "Y:/Piping Department/Blocks/Stamps/BID_ONLY.DWG" IP "" "" "" "08/30/13");; your insert comand to insert the new block in IP???
(setvar "ATTREQ" oldatt);; sets the ATTREQ SysVar to the original value...
)

Brandon, this code is only a demo (not tested, have no autocad for testing), created to demonstrate a way to achieve what you intend to, is not a quick code, because it uses AutoCAD commands to demonstrate easily all steps, and assuming layers are not locked...

(defun c:demo (/ *error* ENT HND IP ITM LAY LYT NUM OLD_ATT OLD_CLAY OLD_OSM SS)
(defun *error* (msg)
 (if old_att (setvar 'ATTREQ old_att))
 (if old_osm (setvar 'OSMODE old_osm))
 (if old_clay (setvar 'CLAYER old_clay))
 (if (not (member msg '("Function cancelled" "quit / exit abort")))
   (princ (strcat "\nError: " msg))
 )
 (princ)
);; *error*
 (if (setq ss (ssget "_X" '((0 . "INSERT")(2 . "IFC stamp"))))
   (progn
     (setq old_att (getvar 'ATTREQ))
     (setq old_osm (getvar 'OSMODE))
     (setq old_clay (getvar 'CLAYER))
     (setvar 'ATTREQ 0)
     (setvar 'OSMODE 0)
     (setq itm 0 num (sslength ss))
     (while (< itm num)
(setq hnd (ssname ss itm))
       (setq ent (entget hnd))
(setq ip (cdr (assoc 10 ent)))
(setq lyt (cdr (assoc 410 ent)))
(setq lay (cdr (assoc 8 ent)))
(vl-cmdf "_.layout" "_S" lyt)
(setvar 'CLAYER lay)
(vl-cmdf "_.insert" "Y:/Piping Department/Blocks/Stamps/BID_ONLY.DWG" ip "" "" "" "08/30/13")
(setq itm (1+ itm))
     );; while
     (setvar 'ATTREQ old_att)
     (setvar 'OSMODE old_osm)
     (setvar 'CLAYER old_clay)
     );; progn
   );; if
 (princ)
 );; demo

 

HTH

Henrique

Posted

Thank you Henrique, I will spend some time with this after my lunch break.

But yes, the insert portion of the routine is meant to get the insertionpoint of the block that is being erased, so that when the new block gets placed into the drawing it is at the same location of the last block.

Thanks for your time and explanation!

Posted

Okay! Onto today's lisp topic/problem :)

 

I am attempting to tackle a new project.

I'm writing a small routine to enable layer toggles based off of a few characters inputted by a user, such as:

 

Laymod "IFC" thaw

 

would be the user execution command, and within that I want this program to search for any matches within the list of returned layers in the drawing to what's in the double-quotes, in the example above, it would return any or all layers that have the string "IFC" within them.

 

I've been able to construct the layer list, and have set vars for the layers to be an argument of the subfunction required to toggle the layers.

 

What I need some assistance with is the string matching portion of this routine, that would search the table of layers and return any matches to the user input....if anyone would assist me in this i would be grateful.

 

Thanks and cheeeeeeers!

Posted

I would post each of your questions as a completely new post else others will not read them thinking still the same old question.

 

Using Vl you can do stuff like Vla-get-insertionpoint and avoid using dxf codes its a bit more plain English to understand within the code.

 

Same as there is a vl insert command rather than use vl-cmdf

 

Have a google

Posted

I use google regularly and have posted an original topic on another thread pointing to this forum, but was hoping to have a central location for a broad spectrum of lisp questions and answers, pertaining to the routines we use here in this location.

Cheers, and I wish I had more knowledge on vlisp, use it when I can, but without a guru to check me it'll be really though to actually complete things in a language that I nor no one I work with know. Lisp has enough people with knowledge that if I run into an issue I can usually get an answer within the hour. I wouldn't want to bother Lee Mac so much if I attempted vlisp :o

  • 2 weeks later...
Posted

I am looking for a match property program that will work with block text attributes

I am hoping that someone will offer me tips in how to accomplish this

Currently I think it would require

exploding the block

get dxf codes from source text

apply them and entmod the text-to-be-changed

wblock back together

 

But am not sure if this is the most efficient way to accomplish such a task.

So looking for some comments, suggestions.

I'll attach a picture to explain what I mean , often the attributes in our blocks are on separate layers and colors when they in fact should not be!

If there is an existing match property lisp routine that will work for blocks or nested blocks/xrefs, I would love to get a copy.

Thanks!!match.jpg

pencil.png

Posted

Oh once again Lee has made this, alright! :D

  • 1 month later...
Posted

In browsing the forums today, I came upon an issue that i'd like to post back into this thread. Revitalize it just a little bit...

I'm attempting to use the (member) function to check if an item (string) belongs to a list of strings.

All I'm getting is Nil....here are the conditions.

(setq lst (dynamicpropertyallowedvalues (car (entsel)) "*"))
Select object: (("Visibility1" "Check Valve" "Flanged Check Valve"))

Command: !lst
(("Visibility1" "Check Valve" "Flanged Check Valve"))

 

So that's what I've done to obtain my list, that's a subfunction that is defined elsewhere. Now, when I try to see if a string "Check Valve" is contained within lst, which it obviously is....

Command: (member '"Check Valve" lst)
nil
Command: (member "check valve" lst)
nil
Command: (member "Visibility1" lst)
nil
Command: (setq theitem "Flanged Check Valve")
"Flanged Check Valve"
Command: (member theitem lst)
nil

 

So that's where I'm at, hoping to gain some understanding as to why my member function isn't returning True or returning a list with the given member item and the following items...

If I'm going about this the wrong way, could someone please inform me as to the proper way to check if a string is contained within a list and how to manipulate it. The lst length should vary, under these conditions I do not want to use (nth) or similar functions because those would not work for what i'm trying to do.

Thanks in advance!

Posted

You have a list within a list:

 

_$ (setq lst '(("Visibility1" "Check Valve" "Flanged Check Valve")))
(("Visibility1" "Check Valve" "Flanged Check Valve"))

_$ (member '("Visibility1" "Check Valve" "Flanged Check Valve") lst)
(("Visibility1" "Check Valve" "Flanged Check Valve"))

_$ (member "Visibility1" (car lst))
("Visibility1" "Check Valve" "Flanged Check Valve")

_$ (member "Check Valve" (car lst))
("Check Valve" "Flanged Check Valve")

Posted

Okay, great, that should do....thanks, I'll test it too but I see from your returned info there what needs to happen.

Thanks Lee, once again :P

 

 

Ahhhhh, alright. I see.

(member "Visibility1" (car lst))
("Visibility1" "Check Valve" "Flanged Check Valve")

 

How I would have expected my tests earlier to return is obtained by this method, simply stating adding to look in the first item in the list "lst" , being as there is only the one actual list with data within lst, that's why the (car) function is required. And once it's looking at the lst containing the three strings, and finds "Visibility1" within that list, it returns the entire list.

 

It does not, however, return True...but I'm assuming that an (if) statement could be wrapped around the (member) statement and if a list is returned (i.e. the item is found in the list), then the if function would proceed to the "then" portion, as if it returned True. This would be easy to test in fact i'll do it now....

Posted
...and finds "Visibility1" within that list, it returns the entire list.

 

Note that this is only because "Visibility1" is the first item in the list supplied to member. ;)

Posted

Similar to the (cdr) function in that regard, if I had given it "Check Valve" it would return just the 2.......

as shown in your last example.

And similarly

Command: (setq lst '(("Visibility1" "Check Valve" "Flanged Check Valve")))
(("Visibility1" "Check Valve" "Flanged Check Valve"))


Command: (member "Flanged Check Valve" (car lst))
("Flanged Check Valve")

 

Okay, got it. Thanks again!

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