Simple Automation, edit block attribute with LISP.

Simple Automation, edit block attribute with LISP.

Anonymous
Not applicable
7,540 Views
20 Replies
Message 1 of 21

Simple Automation, edit block attribute with LISP.

Anonymous
Not applicable

Hello, I'm pulling my hair out over what I expected to be a simple function.

 

I'm completely new to using LISP so go easy on me.

 

Basically I need a function or something that will simple edit a block called "TitleBlock" and set the attribute "STATUS" to the text "Preliminary".

 

Also, what do i need to save the lsp file as? and what would I need to type to run it?

 

Any help would be great just to get me started, bonus points if you comment on code to help me understand it 🙂

0 Likes
Accepted solutions (1)
7,541 Views
20 Replies
Replies (20)
Message 2 of 21

Moshe-A
Mentor
Mentor

@Anonymous  hi,

 

check this

 

; Set Status Attribute
(defun c:SSA (/ ss i ename) ; declar local variables
 ; pause for select objects 
 (if (setq ss (ssget
		'((0 . "insert")     ; filter only blocks
		  (2 . "TitleBlock") ; by the name titleblock
		  (66 . 1)           ; must have attributes
                 )
              )
     )
  (progn
   (setq i -1) ; index
   (repeat (sslength ss) ; loop
    (setq ename (ssname ss i)) ; retrieve entity name
    (setpropertyvalue ename "status" "Preliminary") ; set attribute
    (setq i (1+ i)) ; inc index
   ); repeat
  ); progn
 ); if
)
Message 3 of 21

JamesMaeding
Advisor
Advisor

@Moshe-A 

that (setpropertyvalue ...) function is not built-in, right?

You need to supply that I think.

My function for this is not good for posting, as it accounts for fields and thing that would just confuse.


internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes
Message 4 of 21

dlanorh
Advisor
Advisor

@JamesMaeding wrote:

@Moshe-A 

that (setpropertyvalue ...) function is not built-in, right?

You need to supply that I think.

My function for this is not good for posting, as it accounts for fields and thing that would just confuse.


Built-in since 2012 IIRC

I am not one of the robots you're looking for

Message 5 of 21

JamesMaeding
Advisor
Advisor

@Moshe-A 

You are correct, it does show blue in the VLIDE..... never knew.

For the new lisper, IMO, that function is voodoo. Normally things in lisp that change properties are related to "com" interface, which is what VBA classically uses, but lisp and .net can use it too.

There is code behind the setpropertyvalue that loops through the attributes, finds the one by right name, and changes the value.

for an example of this, read this, which is super handy for modifying other props too besides value:

;(UPDATE-ATT-PROPS (ename (TAG VALUE INSPT HT STYLE WIDTH OBLIQUE)(...)))
;(UPDATE-ATT-PROPS (CAR (ENTSEL)) (LIST (LIST "1stTxtAbove" "TEST" (GETPOINT))))
(defun UPDATE-ATT-PROPS (EN ATTS / EN1 ATT EL DIMZIN INDEX VAL HT INSPT LST OB STY WD)
  (setq EN1 EN)
  (IF (assoc 66 (ENTGET EN)) ;HAS ATT'S
    (PROGN
      ;UPDATE BY TAG NAMES
      (SETQ DIMZIN (GETVAR "DIMZIN"))
      (SETVAR "DIMZIN" 8)
      (while (= "ATTRIB" (cdr (assoc 0 (setq EL (entget (setq EN (entnext EN)))))))
        (if (SETQ LST (assoc (cdr (assoc 2 EL)) ATTS))
           (PROGN
            ;STRING VALUE 1
             (IF (SETQ VAL (NTH 1 LST)) ;EXTRACT FROM ATTS VARIABLE
              (PROGN
                 (IF (NUMBERP VAL)(SETQ VAL (RTOS-MAX 2 VAL))) ;CONVERT TO STRING IF NEEDED
                (SETQ EL (subst (cons 1 VAL)(assoc 1 EL) EL))
              )
            )
            ;INSPOINT 10 OR 11, IF EITHER 72 OR 73 ARE NON-ZERO, USE 11
            (IF (SETQ INSPT (NTH 2 LST))
              (IF (OR (/= (CDR (assoc 72 EL)) 0.0)(/= (CDR (assoc 74 EL)) 0.0))
                (SETQ EL (subst (cons 11 INSPT)(assoc 11 EL) EL))
                (SETQ EL (subst (cons 10 INSPT)(assoc 10 EL) EL))
              )
            )
            ;HT 40
            (IF (SETQ HT (NTH 3 LST))
              (SETQ EL (subst (cons 40 HT)(assoc 40 EL) EL))
            )
            ;STYLE 7
            (IF (AND (SETQ STY (NTH 4 LST))
                     (TBLSEARCH "STYLE" STY)
                )
              (SETQ EL (subst (cons 7 STY)(assoc 7 EL) EL))
            )
            ;WIDTH 41
            (IF (SETQ WD (NTH 5 LST))
              (SETQ EL (subst (cons 41 WD)(assoc 41 EL) EL))
            )
            ;OBLIQUE 51
            (IF (SETQ OB (NTH 6 LST))
              (SETQ EL (subst (cons 51 OB)(assoc 51 EL) EL))
            )
            ;UPDATE ATTRIBUTE
            (entmod EL)
          )
        )
       )
       (SETVAR "DIMZIN" DIMZIN)
      (entupd EN1) ;UPDATE BLOCK
    )
  )
)

(DEFUN RTOS-MAX (FORMAT NUM / )
(RTOS NUM FORMAT (- 16 (STRLEN (ITOA (FIX NUM)))))
)

internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes
Message 6 of 21

Kent1Cooper
Consultant
Consultant

@Moshe-A wrote:
....
....
(setq i -1) ; index (repeat (sslength ss) ; loop (setq ename (ssname ss i)) ; retrieve entity name (setpropertyvalue ename "status" "Preliminary") ; set attribute (setq i (1+ i)) ; inc index ); repeat ....

 

I think you need to tweak that....

 

If you start off with the 'i' variable at -1, and the first (ssname) function tried to use it at that value, you'll get an error.  You need to either:

A)  set 'i' to 0 at the beginning, instead of to -1, or:

B)  step 'i' up one within  the (ssname) function, so that at first look it's raised to 0, like this:

 (setq i -1) ; index 
(repeat (sslength ss) ; loop
(setq ename (ssname ss (setq i (1+ i)))) ; retrieve entity name <-- raised here
(setpropertyvalue ename "status" "Preliminary") ; set attribute
;; (setq i (1+ i)) ; inc index <-- not here
); repeat

or [my preference]:

C)  set the counter to the length of the selection set right in the (repeat) function' quantity argument, instead  of pre-setting it to -1, and count downward from the end instead of upward from the beginning:

(repeat (setq i (sslength ss)) ; loop
  (setq ename (ssname ss (setq i (1- i)))) ; retrieve entity name
  (setpropertyvalue ename "status" "Preliminary") ; set attribute
); repeat

 

Kent Cooper, AIA
0 Likes
Message 7 of 21

Moshe-A
Mentor
Mentor

yes kent, as always you are right, my mistake [forgot to change it to (setq i 0)]

thanks for the correction

 

moshe

 

0 Likes
Message 8 of 21

Anonymous
Not applicable

Hey and thanks for replying.

 

So, i copied the code into Visual LSIP, saved it as SSA.lsp and clicked "Load Active edit window", then i typed "(c:SSA)" into the Visual LISP Console, Is that the correct way of running the code?

 

However, AutoCAD asks for me to select a block, when I do, the console returns:

 

error: ADS request error.

 

Any further advice? thanks in advance.

0 Likes
Message 9 of 21

ВeekeeCZ
Consultant
Consultant

@Anonymous wrote:

....

Any further advice? thanks in advance.


You already got the one. How about reading the whole thread?!

0 Likes
Message 10 of 21

Anonymous
Not applicable

I've had another look at the other replies and tried to change the i values bit again with some success, I'm guessing i pasted over bracket or something first time.

 

So now the code does changes the text which is great, however it pauses and waits for me to select the block rather than just doing it. How do i force the code to select the block "TitleBlock" at that point?

 

This is the code I'm using, I've added a save n close to the end to help with the planned 200 drawings change >.<

 

; Set Status Attribute
(defun c:SSA (/ ss i ename) ; declar local variables
 ; pause for select objects 
 (if (setq ss (ssget
		'((0 . "insert")     ; filter only blocks
		  (2 . "TitleBlock") ; by the name titleblock
		  (66 . 1)           ; must have attributes
                 )
              )
     )
  (progn
	(setq i -1) ; index 
		(repeat (sslength ss) ; loop
		(setq ename (ssname ss (setq i (1+ i)))) ; retrieve entity name <-- raised here
		(setpropertyvalue ename "status" "Preliminary") ; set attribute
		;;   (setq i (1+ i)) ; inc index  <-- not here
	); repeat
  ); progn
 ); if
)

  (command "_.save" "" "N")
  (command "_.close" "")
0 Likes
Message 11 of 21

ВeekeeCZ
Consultant
Consultant

Add "_x" right behind word ssget.

You should look HERE for more details, these are very useful basics.

0 Likes
Message 12 of 21

ВeekeeCZ
Consultant
Consultant
Accepted solution

@Anonymous wrote:

...This is the code I'm using...

 

...
  (progn
	(setq i -1) ; index 
		(repeat (sslength ss) ; loop
		(setq ename (ssname ss (setq i (1+ i)))) ; retrieve entity name <-- raised here
		(setpropertyvalue ename "status" "Preliminary") ; set attribute
		;;   (setq i (1+ i)) ; inc index  <-- not here
	); repeat
  ); progn
...

 

Don't. Since you're new, recommend you to follow the C advice given by Kent. It's the simplest and most straightforward method to use. Once you get used to it you'll won't have to think about it... whether it should start with 0 or -1 or whatever... 

 

(defun c:SSA (/ ss i ename)
  (if (setq ss (ssget "_X" '((0 . "insert") (2 . "TitleBlock") (66 . 1))))
    (repeat (setq i (sslength ss))
      (setq en (ssname ss (setq i (1- i))))
      (setpropertyvalue en "status" "Preliminary")))
  (princ)
  )
0 Likes
Message 13 of 21

Anonymous
Not applicable

That's great and works a treat. I've even managed to get it to save and close, thanks to everyone for the help so far.

Here is the code used for other people:

; Set Status Attribute
(defun c:SSA (/ ss i ename)
  (if (setq ss (ssget "_X" '((0 . "insert") (2 . "TitleBlock") (66 . 1))))
    (repeat (setq i (sslength ss))
      (setq en (ssname ss (setq i (1- i))))
      (setpropertyvalue en "status" "Preliminary")))
  (princ)
  (command "_.save" "" "N")
  (command "_.close" "")
  )

So, is there a way of adding a button to AutoCAD 2018 to run the code without having to open the editor and typing (c:SSA)? Once I had this off to someone else, I want to make their life as easy as possible as they know nothing of coding.

0 Likes
Message 14 of 21

ВeekeeCZ
Consultant
Consultant

@Anonymous wrote:
...
  (command "_.save" "" "N")
  (command "_.close" "")

 

Not sure about that one... what "N" answers to?

 

0 Likes
Message 15 of 21

ВeekeeCZ
Consultant
Consultant

@Anonymous wrote:

...

So, is there a way of adding a button to AutoCAD 2018 to run the code without having to open the editor and typing (c:SSA)? Once I had this off to someone else, I want to make their life as easy as possible as they know nothing of coding.


 

You could somehow automate the loading - see HERE

Then go to the CUI, create a new command where the macro field fill up with ^c^cssa; 

Message 16 of 21

Anonymous
Not applicable

That's awesome and working smoothly, you guys are great, thanks very much.

 

So, is there a way of taking the automation a step further, I would love it if the code could open a drawing then run, then open another drawing and work its way through a folder of say 200 drawings. (kind of like how batchplot works)

 

Where would I start? are there commands that could support this? would I have to put a list of the drawings into the code?

 

Here is some pseudo code:

-Run code

-Open first drawing

-make changes to text

-save

-close

-open next drawing unless no drawings left.

 

0 Likes
Message 17 of 21

dlanorh
Advisor
Advisor
Type "obdx" into the search box at the top of the page.

I am not one of the robots you're looking for

0 Likes
Message 18 of 21

JamesMaeding
Advisor
Advisor

@ВeekeeCZ 

for beginners I keep it simple with:

(setq index 0)

(repeat (sslength ss)

  (setq ename (ssname ss index))

  ...do something

  (setq index (+ 1 index))

)

experienced people can do anything, but the OP might like this and it goes through the ss in order instead of backwards.


internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes
Message 19 of 21

JamesMaeding
Advisor
Advisor

@Anonymous 

you would use a script to do that, as lisp cannot "continue" once the drawing that ran it closes.

Scripts are just text files with .scr extension that have acad commands typed in them.

Note that a space is an enter, but for commands that ask for a name, they accept spaces so you do a carriage return (new line).

You can load a script and lisp by dragging from explorer to the black drawing area of acad.

Scripts run instantly, not like lisps where you have to type the function name.

I even make lisps that make scripts and run them. That allows you to say, save current drawing, close, and open the next one alphabetically.

It takes a lot of testing though, and you should notice that in the VLIDE, you can double click before a paren, it will select to the end of the statement. Then right click and pick Inspect. It will show the results of the statement.

So classic debugging goes like this in the vlide:

1) put cursor at beginning of a statement you want to "stop" on. Hit F9 to see a red box there.

2) pick Tools->Load text in editor

3) run the function SSA or whatever on command line. It will run the lisp and stop at the break point.

4) select any variable and right click, Inspect to see the value at that moment

5) hit F8 to step forward, or shift F8 to do a whole statement at a time. Try it to see how it works.

6) at any time, select a statement and Inspect to see what it would give. You can actually edit your code and reinspect, but that also glitches the rest of the debug, it will kick you out.

7) keep hitting shift F8 to run through rest of code, or the little green arrow on debug toolbar.

8) red arrow on debug toolbar stops the debug at any time.

Learning to use that will make you 100x more powerful as then you can try stuff at runtime to get your code working.

Also, you can see where a routine glitches if it does. The debug will leave the cursor where it failed.

Read up on the use of the VLIDE. You can set up projects that compile to vlx, and use those projects to find things when you do larger programs.


internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes
Message 20 of 21

serag.hassouna
Advocate
Advocate

@JamesMaeding wrote:

@Anonymous 

you would use a script to do that, as lisp cannot "continue" once the drawing that ran it closes.

Scripts are just text files with .scr extension that have acad commands typed in them.

...

I even make lisps that make scripts and run them. That allows you to say, save current drawing, close, and open the next one alphabetically.

It takes a lot of testing though ...


Also, if you like to continue execution from outside you can interface with AutoCAD using "ActiveX COM" or ".NET".
I think this is more compact than scripts, as the logic still constant and consistent with the code.

0 Likes