LISP to select a block, look at a attribute and run certain commands based on that attribute in the block.

LISP to select a block, look at a attribute and run certain commands based on that attribute in the block.

ryan.garland.1.ctr
Explorer Explorer
2,170 Views
7 Replies
Message 1 of 8

LISP to select a block, look at a attribute and run certain commands based on that attribute in the block.

ryan.garland.1.ctr
Explorer
Explorer

I am a little new to creating complex LISP routines but is there away to create a LISP that could auto select a certain block and then read a predefined attribute in that block and then based on that attribute setup the correct layers in a drawing file without the user having to click the button, then select the block, then allowing the command to auto finish, basically I would like the user to click the button and it runs the command.

 

Any help on this would be great.

 

Thanks in advance.

0 Likes
2,171 Views
7 Replies
Replies (7)
Message 2 of 8

CodeDing
Advisor
Advisor

@ryan.garland.1.ctr ,

 

How comfortable are you with AutoLISP?

Here is a very simple approach to what it sounds like you're asking.

(defun c:TEST ( / blk att lyrOpts ss attVal opt lType lyr)
  (setq blk "My Block Name")
  (setq att "ATTRIBUTE_NAME")
  (setq lyrOpts '(
    ("OPTION 1"
      ("LAYER 1" 7 "Continuous")
      ("LAYER 2" 6 "DASHED")
      ("LAYER 3" 5 "Continuous")
    )
    ("OPTION 2"
      ("LAYER 4" 4 "Continuous")
      ("LAYER 5" 3 "DASHED")
      ("LAYER 6" 2 "Continuous")
    )
  ));quote/setq
  (if (and (setq ss (ssget "_X" (list '(0 . "INSERT") (cons 2 blk))))
           (setq attVal (getpropertyvalue (ssname ss 0) att)
           (setq opt (member (strcase attVal) lyrOpts))
      );and
    (foreach l (cdr opt)
      (setq lType (cond ((tblobjname "LTYPE" (last l))) ((tblobjname "LTYPE" "Continuous"))))
      (if (setq lyr (tblobjname "LAYER" (car l)))
        (progn
          (setpropertyvalue lyr "Color" (cadr l))
          (setpropertyvalue lyr "LinetypeId" lType)
        );progn
      ;else
        (entmake
          (list (cons 0 "LAYER") (cons 100 "AcDbSymbolTableRecord") (cons 100 "AcDbLayerTableRecord")
                 (cons 2 (car l)) (cons 70 0) (cons 62 (cadr l)) (cons 6 (cdr (assoc 2 (entget lType))))
          );list
        );entmake
      );if
    );foreach
  ;else
    (prompt "\nError, Block or Attribute not found.")
  );if
  (princ)
);defun

Best,
~DD

0 Likes
Message 3 of 8

ryan.garland.1.ctr
Explorer
Explorer

Thanks for the response @CodeDing

 

I am still on the learning curve of AutoLISP but starting to figure it out.

 

It looks like that code is what I am looking for but having an error put when I load it into CAD

     "error: malformed list on input"

So I am looking through it to see if I can figure it out what is causing the error.

 

Is there away to have a IF statement on the attribute value itself?

 

We use DSIZE and FSIZE borders and the attribute tag is  "BORDER_TYPE", the default value of the attribute would be "MECH", "ARCH", "ELEC", etc... respectively on the border being used.

 

Here is a screenshot of the code that I have started (still in progress) but it is only usable with one border type but if used on a different border it wants to create new layers that don't need to be there which is to be expected since it doesn't know which border it is checking.

 

Capture.PNG

 

 

0 Likes
Message 4 of 8

Kent1Cooper
Consultant
Consultant

@ryan.garland.1.ctr wrote:

....

It looks like that code is what I am looking for but having an error put when I load it into CAD

     "error: malformed list on input"

So I am looking through it to see if I can figure it out what is causing the error.

....

 


It may not be the only issue, but I noticed this line is missing the closing right parenthesis:

           (setq attVal (getpropertyvalue (ssname ss 0) att))

 

Kent Cooper, AIA
0 Likes
Message 5 of 8

Kent1Cooper
Consultant
Consultant

@ryan.garland.1.ctr wrote:

.... Here is a screenshot of the code that I have started ....

 


I realize this isn't directly about your primary question, but I have a question and some suggestions about the code you have started.

 

It looks like if the LINE Layer exists, it will merge ARROW into it if that also exists, but will not do any of the other Layer settings.  Those will happen only if LINE does not exist.  Is that really what you want?

 

On the assumption that you want the other Layer stuff to happen in any case, there's a lot of consolidation possible.

 

There's no need to check whether a Layer exists to decide whether to create it.  You can just give the Layer name to the Make or New option, and it won't matter whether it already exists.  The only time it's advisable to check, in my opinion, is if it's possible that it exists with some different option(s) than your standard [such as a different color] and you want to let the User keep the non-standard setting(s).

 

I assume you're Making the MAJOR and MINOR Layers just to prevent a no-Layer(s)-found error in the LAYMRG if neither of them exists.  In that case, since they are only going to be merged into LINE anyway, there's no point in assigning colors to them.

 

Using the New option followed immediately by the Make option with the same Layer name [LINE] is redundant -- the New option is wasted.

 

It may be valid, but it looks funny to me to Make Layer 0.  It can't be Purged, so it will always exist, so all you need to do is Set it current.  And only once, if you adopt the remaining suggestions.

 

A single (command) function can contain any number of commands, and a single Layer command any number of options.  And in LAYER, most options can be applied to any number of Layer names with comma separators, including [for example] assigning the same Color to more than one Layer at once.

 

I think [without testing] that your entire screenshot content can be replaced with this:

(defun C:LayerCheck ()
  (command
    "_.layer"
      "_new" "LINE,ARROW,MAJOR,MINOR,BUBBLE,DEV_LIST,EXISTING,PIDSYM,REDUCER,REVISION,TEXT,ZONE"
        ; [first 4 to prevent no-Layer(s)-found error(s) in LAYMRG]
      "_color" 131 "LINE" "_color" 151 "BUBBLE" "_color" 91 "DEV_LIST,PIDSYM"
      "_color" 40 "EXISTING" "_color" 11 "REVISION" "_color" 171 "TEXT" "_color" 191 "ZONE"
      "_set" "0" ""
    "_.laymrg" "_name" "ARROW" "_name" "MAJOR" "_name" "MINOR" "" "_name" "LINE" "_yes"
  ); command
); defun

[It's possible that it could be a little shorter by using Make options for the individual Layers, so that the Color options can use "" Enter to accept the current Layer for the color assignments.  That would avoid the spelling out of Layer names twice, but would add back in all the individual Make option calls and "" Enters that are not needed in the above, and it would require two Color-91 assignments instead of one.]

Kent Cooper, AIA
0 Likes
Message 6 of 8

CodeDing
Advisor
Advisor

@ryan.garland.1.ctr ,

 

I believe Kent's closing parenthesis will fix the malformed list, my mistake.

 

Can you perhaps provide some pseudo-code of what your workflow will look like? The snip of code you provided seems to differ from what I interpreted your original request to be.

 

My code's pseudo-code:

- Get block (by name, as specified in code)

- Read block Attribute (always same attribute name, as specified in code)

- Based on value of attribute..

--- If value is "OPTION 1"

-----loop through layer properties defined inside Option 1, update layer properties if it exists, otherwise create layer with properties if doesn't exist yet

--- If value is "OPTION 2"

-----........same process

 

Your snip's pseudo-code:

- If "LINE" layer exists

--- If "ARROW" layer exists, merge the 2

--- Otherwise, nothing

- Otherwise, Create a bunch of layers & merge some

 

 

I know you're trying to expand your code to possibly include the block and attribute, but if you could just give me a good idea of your desired workflow, I think we can get you in the right direction.

 

Best,

~DD

0 Likes
Message 7 of 8

ryan.garland.1.ctr
Explorer
Explorer

Thanks for the response @CodeDing and @Kent1Cooper

 

I believe we are getting close to what I am trying to do.

 

I believe the pseudo-code would be something like this:

 

When a User clicks the button

 

-Figure out which block is being used - “Block could be named DBORDER or FBORDER

-Read predefined block Attribute - “Attribute Tag in any border size is named BORDER_TYPE”

-Based on Attribute Default Value - “DARCH, DELEC, DMECH, FARCH, FELEC, FMECH”

 

IF Block = DBORDER

     IF Attribute = DARCH

                (if (tblsearch “LAYER” “ARROW”)

                                (progn ;; If layer exist then get rid of it

                                                (command “-laymrg” “name” “ARROW” “” “name” “LINE” “y”)

                                )

                                (progn;; If layer doesn’t exist then move on to something else

                                )

                )

     ELSEIF Attribute = DELEC

                Similar To Above Code

     ELSEIF Attribute = DMECH

                Similar To Above Code

     Else

                (prompt "\nError, Block or Attribute not found.")

      ENDIF

ELSEIF Block = FBORDER

      IF Attribute = FARCH

                Similar To Above Code

      ELSEIF Attribute = FELEC

                Similar To Above Code

      ELSEIF Attribute = FMECH

                Similar To Above Code

      Else

                (prompt "\nError, Block or Attribute not found.")

      ENDIF

ELSE

                (prompt "\nError, Block or Attribute not found.")

ENDIF

Message 8 of 8

CodeDing
Advisor
Advisor

@ryan.garland.1.ctr ,

 

Here is the basic outline of the pseudo-code you provided.. But I can't help but wonder if things can be improved still.

 

Let's do this then, take this outline and fill the rest of it out as best you can, then if you can, post it back here and let's see where we can maybe remove some redundancies. Lisp is very good about handling things as Lists but our current layout is not exactly reflecting that, so let's review when you come back with the rest of your code.

(defun c:TEST ( / ss att)
  (if (setq ss (ssget "_X" '((0 . "INSERT") (2 . "DBORDER,FBORDER"))))
    (progn
      (setq att (getpropertyvalue (ssname ss 0) "BORDER_TYPE"))
      (cond
        ((eq att "DARCH")
          ;; Do Stuff
        )
        ((eq att "DELEC")
          ;; Do Stuff
        )
        ((eq att "DMECH")
          ;; Do Stuff
        )
        ((eq att "FARCH")
          ;; Do Stuff
        )
        ((eq att "FELEC")
          ;; Do Stuff
        )
        ((eq att "FMECH")
          ;; Do Stuff
        )
        (t (prompt "\nAttribute not found or unable to handle."))
      );cond
    );progn
  ;else
    (prompt "\nNeither DBORDER or FBORDER block found.")
  );if
  (princ)
);defun

Best,

~DD

0 Likes