Jump to content

Some Simple Autolisp Questions.


SteveK

Recommended Posts

I have been going through some of jeffsanders tutorials as a fair few of you guys recommend it.

 

One question I have is; How do I make the relationship between selection sets and entities? For example (& also my problem):

First I make a selection of all objects with layer "STR":

(defun C:CTHeight()
 (if (setq mySet (ssget "X" (list (cons 8 "STR"))))

Then (& for the moment lets assume the selection is all text) I want to change the text height of all my selection (so using another copied clip from his tutorials):

[font=Courier](if(setq ent(entsel))[/font] ;Except I don't want to use entsel 
                                  ;because I already have a selection
   (progn                              
     (setq en(car ent))
     (setq enlist(entget en))
     (setq enlist (subst (cons 40 '50)(assoc 40 enlist)enlist))
     (entmod enlist)
     (entupd en)
   )
   (alert "Error")
 )
 (princ)

What can I use to make the jump from a selection set (mySet) to changing the entities (ent)?

 

No Doubt there's a better way to do it, but this is part problem solving and part understanding. Thanks

Link to comment
Share on other sites

  • Replies 59
  • Created
  • Last Reply

Top Posters In This Topic

  • Lee Mac

    22

  • SteveK

    20

  • CAB

    8

  • MSasu

    6

Top Posters In This Topic

Hi Steve,

 

There are many ways to convert a selection set into the various entities contained within.

 

Here are a few examples of varying methods, in all the examples, assume "ss" is a variable with a selection set bound to it.

 

(setq i -1)
(while (setq ent (ssname ss (setq i (1+ i))))
 (setq lst (cons ent lst)))
(reverse lst)

 

The above uses a WHILE loop with the SSNAME function. The SSNAME function will return the entity name of the entity that lies at a certain index within the selection set. the index starts at 0, hence i is set to -1 before the loop.

 

The above will return a list of entities contained within the selection set. You can then use this list to perform operations on the entities using such functions as FOREACH or MAPCAR.

 

Alternatively, you can perform the operation whilst iterating through the set:

 

(setq i -1)
(while (setq en (ssname ss (setq i (1+ i))))
 (setq enlist(entget en))
 (setq enlist (subst (cons 40 50) (assoc 40 enlist) enlist))
 (entmod enlist)
 (entupd en)
) ; end while

 

Alternatively, you can use the REPEAT function (a little slower, but hardly noticeable...):

 

(repeat (setq n (sslength ss))
 (setq en (ssname ss (setq n (1- n))))
 (setq enlist(entget en))
 (setq enlist (subst (cons 40 50) (assoc 40 enlist) enlist))
 (entmod enlist)
 (entupd en)
) ; end Repeat

 

The above steps backwards through the set, from the highest index number, down to zero, and operates on each entity in turn.

 

Other, more advanced methods include:

 

(setq lst (vl-remove-if 'listp
           (mapcar 'cadr (ssnamex ss))))

 

Which will again return a list of all the entities in the Selection Set, but needs a call to (vl-load-com) to load the Visual LISP functions. This method, although more elegant, is slower than the previous examples.

 

Hope this helps, if you don't understand something that I have posted, just ask..

 

Lee

Link to comment
Share on other sites

Lee, thanks for the incredibly fast response. There's plenty there to process, so I'll get back to you with questions. As for the last (advanced) method; so Visual Lisp is the way most Autolisp users go?

Link to comment
Share on other sites

Lee, thanks for the incredibly fast response. There's plenty there to process, so I'll get back to you with questions. As for the last (advanced) method; so Visual Lisp is the way most Autolisp users go?

 

Some just stick to AutoLISP, as VL was only incorporated completely into AutoCAD 2000 I believe.

 

VL gives you more access and flexibility within AutoCAD as it is a lower-level programming language than AutoLISP. But I would only migrate to VL after you are completely competent in AutoLISP, as VL has all the functionality of AutoLISP & a lot more...

 

Lee

Link to comment
Share on other sites

Ok so I've implemented the second suggestion into my program:

(defun C:CTS()
 (if (setq ss (ssget "X" (list (cons 8 "STR")))) ;Create selection set with all objects with specified layer
   (progn
     (setq i -1)
     (while (setq en (ssname ss (setq i (1+ i))))
     (setq enlist (entget en))
   ;(if (= "TEXT" (cdr(assoc 0 enlist)))(
         (setq     enlist (subst (cons 72 1) (assoc 72 enlist) enlist) ; vertical align - middle = 1
           enlist (subst (cons 73 2) (assoc 73 enlist) enlist) ; horizontal align - center = 2
           enlist (subst (cons 7 "ISOCP") (assoc 7 enlist) enlist) ; text style
           enlist (subst (cons 40 300) (assoc 40 enlist) enlist)) ; text height
         (entmod enlist)
         (entupd en)
   ) ; end if
     ) ; end while
   )
   (alert "Error: no layer exists")
 )
 (princ)
)

 

The code given works fine, it's the lines I've added to it that cause problems:

The commented-out if statement I thought would help to tell the program only which entities are text but (1) it doesn't work (it says: error: extra cdrs in dotted pair on input) and (2) it doesn't seem to need it anyway. Should an if statement like that be included? And if so what's wrong with mine?

The other problem I get is to do with the text alignment. For some reason all text that is initially set to justify "Left" the program moves to 0,0. What would make it do that?

 

Thanks

Link to comment
Share on other sites

A good start Steve,

 

I have made some comments for improvement:

 

(defun c:cts (/ [color=Red][b]ss i en enlist[/b][/color])
[b][color=Blue]  ;; Always remember to localise your variables[/color][/b]
 
 (if (setq ss (ssget "X" (list [b][color=Red](cons 0 "TEXT")[/color][/b] (cons 8 "0"))))
  [color=Blue][b] ;; I have also filtered for TEXT items only[/b][/color]
   (progn
     (setq i -1)
     (while (setq en (ssname ss (setq i (1+ i))))
       (setq enlist (entget en)
            [b][color=Red] enlist (subst (cons 11 (cdr (assoc 10 enlist))) (assoc 11 enlist) enlist)
[/color][/b] 
             ;|[b][color=Blue] When Changing the Alignment Setting of Text, unless it is
                the default: 72=0 73=0, then you need to specify an Alignment point
                in DXF 11. I have used the insertion point from DXF 10.[/color][/b]
             |;
             
             enlist (subst (cons 72 1) (assoc 72 enlist) enlist)     ; vertical align - middle = 1
             enlist (subst (cons 73 2) (assoc 73 enlist) enlist)     ; horizontal align - center = 2
             enlist (subst (cons 7 "ISOCP") (assoc 7 enlist) enlist) ; text style
             enlist (subst (cons 40 300) (assoc 40 enlist) enlist))  ; text height
       (entmod enlist)
       (entupd en)
       ) ; end while
     )
   (alert "Nothing Found")
   )
 (princ)
 )

Link to comment
Share on other sites

Thankyou again, that's a big help. That alignment setting makes sense.

 

Just to show how much I don't know about Lisp I have to ask, if you can use ssget so easily to search throughout a drawing for objects that are text or a certain layer, wouldn't it be possible to set objects in the same motion with like an ssset function?

Link to comment
Share on other sites

@Steve1:

The commented "if" line from your code don't work because you tried to group the statements using a simple parenthesis – must need to use the “progn” statement in order to get the if and respectively else actions grouped:

 

[color=black](if (= "TEXT" (cdr(assoc 0 enlist)))([color=red]progn[/color][/color]

Regards

Link to comment
Share on other sites

Just to show how much I don't know about Lisp I have to ask, if you can use ssget so easily to search throughout a drawing for objects that are text or a certain layer, wouldn't it be possible to set objects in the same motion with like an ssset function?

 

Nice idea, but I'm afraid not - (ssget "_X") is the easiest way to do this operation.

 

Coincidentally, there is no function ssset, closest is, sssetfirst which controls the grips and selection of a selection set.

 

Btw, nice spot Msasu - I didn't dwell on the IF statement too much, as I wanted to go about it another way, using the ss filter.

 

But, for more on IF statements and the like, see here Steve.

 

Lee

Link to comment
Share on other sites

One more thing Steve.

 

What are you using to create your LISP files? Most inexperienced users I find use Notepad to write the files, as that is the format they are used to. But i would recommend instead using the Visual LISP Editor that comes free with ACAD (if you are not already using it of course). Just type VLIDE at the command prompt, then go to File > New File, and off you go.

 

The help file in the Visual LISP Editor is invaluable - you can learn a great deal from it.

 

Lee

Link to comment
Share on other sites

Thanks for the response.

 

msasu: I assumed a progn statement is already running so I wouldn't need to call another one. So what you're saying is for every function that expects only one expression a progn statement is required?

 

Lee: Yep I use VLISP Editor, it makes it 10 times easier.

 

Are block layers picked up in selection sets? Eg if I was to call:

(setq ss (ssget "X" (list (cons 8 "STR"))))

and there was a block that had some layers in "STR", would it only include the block in the selection set if the actual block layer was "STR"?

Link to comment
Share on other sites

Hi Steve,

 

I believe I attached a link in post #9 which explains progn statements quite well. They are used to group expressions to be handled by a function as a single expression.

 

As for the ssget question, you could experiment yourself, using:

 

(sssetfirst nil (ssget "_X" '((8 . "STR"))))

 

But I can tell you that no, it will not be picked up by the ss filter.

Link to comment
Share on other sites

Steve1:

Yes, your trouble-maker piece of code is “wrapped” in a progn, but I was talking about the if statement inside it, the commented one; this contains 3 different statements (have colored it differently), so it will absolutely require for a prong - will raise an error if not.

 

 
   (if (= "TEXT" (cdr(assoc 0 enlist)))
([color=red][b]progn[/b][/color]
[color=blue]          (setq     enlist (subst (cons 72 1) (assoc 72 enlist) enlist) ; vertical align - middle = 1
           enlist (subst (cons 73 2) (assoc 73 enlist) enlist) ; horizontal align - center = 2
           enlist (subst (cons 7 "ISOCP") (assoc 7 enlist) enlist) ; text style
           enlist (subst (cons 40 300) (assoc 40 enlist) enlist)) ; text height[/color]
[color=darkorange]          (entmod enlist)[/color]
[color=green]          (entupd en)[/color]
   )
) ; end if

 

 

If your if contains only two statements, this will not raise an error, but, if not “wrapped” with progn, the second one will execute only when else condition is meet; see above examples:

 

1.

(if a
(setq b 1)   ; <-- b will have value only if a is true
(setq c 2)   ; <-- c will have value only if a is nil
)

 

2.

(if a
(progn
 (setq b 1)   ; <-- both b and c will have value if a is true
 (setq c 2) 
)
)

 

 

Hope that this is clarified the issue.

 

Regards

Link to comment
Share on other sites

for every function that expects only one expression a progn statement is required?

 

 

Yes, you are right - see the following examples:

 

(itoa (progn (setq a (+ 5 3)) (- a 4)))

 

(itoa (- (+ 5 3) 4))

 

evaluation will return "4" in both cases, but will raise an error if progn not involved - that it, 1 argument expected, 2 given:

 

(itoa (setq a (+ 5 3)) (- a 4))

 

Regards

Link to comment
Share on other sites

Use the TBLSEARCH statement - this will return T is the argument style exists in the text styles table of current drawing. The same, with this function you can search for linetypes, blocks, layers or other...

 

(tblsearch "STYLE" "MyTextStyle")

 

 

Regards

Link to comment
Share on other sites

You welcome!

There is also possible to list all the entries in a specified definitions table – see below how to list all text styles defined in current drawing (may use MEMBER statement later to search if a particular style is in list):

 

(setq TStyleEntry (cdr (assoc 2 (tblnext "STYLE" 1)))
     TStylesList '())
(while TStyleEntry
(setq TStylesList (append TStylesList (list TStyleEntry))
      TStyleEntry (cdr (assoc 2 (tblnext "STYLE"))))
)

 

Regards

Link to comment
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
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...