Jump to content

Lisp to Return Name of Selected Block(s)


Beeftimer

Recommended Posts

Just as the title states. I am breaking a big problem into smaller ones, starting with this one. Can anyone tell me how to write a lisp that will return the name of a selected block, or return the names and counts of all blocks selected.

Link to comment
Share on other sites

  • Replies 22
  • Created
  • Last Reply

Top Posters In This Topic

  • Beeftimer

    11

  • Lee Mac

    7

  • Commandobill

    2

  • samifox

    1

Not sure what you mean the "count" but here's a quick one that will return a list of the names of selected blocks.

 

(defun c:blocknames ( / ss)
 (if (setq ss (ssget (list(cons 0 "INSERT"))))
   (mapcar '(lambda (x) (vla-get-name (vlax-ename->vla-object x))) (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss))))
 ))

Link to comment
Share on other sites

Beeftimer said:
Can anyone tell me how to write a lisp that will return the names and counts of all blocks selected.

 

Below is a very simple example demonstrating how to achieve this, complete with comments explaining each line of the program:

 

(defun c:simplecount ( / blk idx itm lst sel ) ;; Define function, declare local variables
   (if ;; If the following expression returns a non-nil value
       (setq sel ;; Assign the value returned by the following expression to the symbol 'sel'
           (ssget ;; Prompt the user to make a selection and return the selection set if successful
              '((0 . "INSERT")) ;; Filter the selection to block references only (INSERTs)
           ) ;; end ssget
       ) ;; end setq
       (repeat ;; Repeat the enclosed expressions the following number of times:
           (setq idx ;; Assign the value returned by the following expression to the symbol 'idx'
               (sslength sel) ;; Return the number of items in the selection set
           ) ;; end setq
           (setq blk ;; Assign the block name to the variable 'blk'
               (cdr ;; Retrieve the value associated with DXF group 2 (the block name)
                   (assoc 2 ;; Retrieve the DXF group 2 dotted pair from the following DXF data
                       (entget ;; Retrieve the list of DXF data for the following entity
                           (ssname sel ;; Retrieve the entity at the following index
                               (setq idx (1- idx)) ;; Decrement the index variable (since selection set indexes are zero-based)
                           ) ;; end ssname
                       ) ;; end entget
                   ) ;; end assoc
               ) ;; end cdr
           ) ;; end setq
           ;; If the block is already recorded in the list:
           (if ;; If the following expression returns a non-nil value
               (setq itm ;; Assign the value returned by the following expression to the symbol 'itm'
                   (assoc blk lst) ;; Attempt to retrieve a list item whose first element is equal to the block name
               ) ;; end setq
               ;; Update the existing list entry:
               (setq lst ;; Redefine the 'lst' variable with the updated list data
                   (subst ;; Substitute the following list item in the list
                       (cons blk (1+ (cdr itm))) ;; Increment the number of occurrences recorded for this item in the list
                       itm ;; The existing item to be substituted
                       lst ;; The list in which to perform the substitution
                   ) ;; end subst
               ) ;; end setq
               ;; Else add a new entry to the list:
               (setq lst ;; Redefine the 'lst' variable with the following updated list data
                   (cons ;; 'Push' a new item onto the front of the list
                       (cons blk 1) ;; Construct a dotted pair whose first key is the block name and value is 1
                       lst ;; The list to which the item should be added (may be nil)
                   ) ;; end cons
               ) ;; end setq
           ) ;; end if
       ) ;; end repeat
       ;; Else the user didn't make a selection
   ) ;; end if
   ;; Print the results (if they exist)
   (foreach itm lst ;; For every 'itm' in the list given by 'lst'
       (princ ;; Print the following to the command-line
           (strcat ;; Concatenate the following strings
               "\n" ;; (New-line character)
               (car itm) ;; The block name
               ": " ;; An arbitrary separator for the data
               (itoa (cdr itm)) ;; The number of occurrences of the block, converted to a string
           ) ;; end strcat
       ) ;; end princ
   ) ;; end foreach
   (princ) ;; Suppress the return of the last evaluated expression (if)
) ;; end defun
 
Edited by Lee Mac
Link to comment
Share on other sites

Not sure what you mean the "count" but here's a quick one that will return a list of the names of selected blocks.

 

(defun c:blocknames ( / ss)
 (if (setq ss (ssget (list(cons 0 "INSERT"))))
   (mapcar '(lambda (x) (vla-get-name (vlax-ename->vla-object x))) (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss))))
 ))

 

Yes, this does solve the first part of the problem. What I meant by "counts" was to give the name of the blocks and then list how many of each were selected. So instead of returning, "block1, block2, block1, block1," it sould return block1 - 3, block2 - 1.

Link to comment
Share on other sites

Below is a very simple example demonstrating how to achieve this, complete with comments explaining each line of the program:

 

Wow, Lee, I know it's been a while, but when did you start commenting your code so detailed?

Link to comment
Share on other sites

Below is a very simple example demonstrating how to achieve this, complete with comments explaining each line of the program:

 

([color=BLUE]defun[/color] c:simplecount ( [color=BLUE]/[/color] blk idx itm lst sel ) [color=GREEN];; Define function, declare local variables[/color]
   ([color=BLUE]if[/color] [color=GREEN];; If the following expression returns a non-nil value[/color]
       ([color=BLUE]setq[/color] sel [color=GREEN];; Assign the value returned by the following expression to the symbol 'sel'[/color]
           ([color=BLUE]ssget[/color] [color=GREEN];; Prompt the user to make a selection and return the selection set if successful[/color]
              '((0 . [color=MAROON]"INSERT"[/color])) [color=GREEN];; Filter the selection to block references only (INSERTs)[/color]
           ) [color=GREEN];; end ssget[/color]
       ) [color=GREEN];; end setq[/color]
       ([color=BLUE]repeat[/color] [color=GREEN];; Repeat the enclosed expressions the following number of times:[/color]
           ([color=BLUE]setq[/color] idx [color=GREEN];; Assign the value returned by the following expression to the symbol 'idx'[/color]
               ([color=BLUE]sslength[/color] sel) [color=GREEN];; Return the number of items in the selection set[/color]
           ) [color=GREEN];; end setq[/color]
           ([color=BLUE]setq[/color] blk [color=GREEN];; Assign the block name to the variable 'blk'[/color]
               ([color=BLUE]cdr[/color] [color=GREEN];; Retrieve the value associated with DXF group 2 (the block name)[/color]
                   ([color=BLUE]assoc[/color] 2 [color=GREEN];; Retrieve the DXF group 2 dotted pair from the following DXF data[/color]
                       ([color=BLUE]entget[/color] [color=GREEN];; Retrieve the list of DXF data for the following entity[/color]
                           ([color=BLUE]ssname[/color] sel [color=GREEN];; Retrieve the entity at the following index[/color]
                               ([color=BLUE]setq[/color] idx ([color=BLUE]1-[/color] idx)) [color=GREEN];; Decrement the index variable (since selection set indexes are zero-based)[/color]
                           ) [color=GREEN];; end ssname[/color]
                       ) [color=GREEN];; end entget[/color]
                   ) [color=GREEN];; end assoc[/color]
               ) [color=GREEN];; end cdr[/color]
           ) [color=GREEN];; end setq[/color]
           [color=GREEN];; If the block is already recorded in the list:[/color]
           ([color=BLUE]if[/color] [color=GREEN];; If the following expression returns a non-nil value[/color]
               ([color=BLUE]setq[/color] itm [color=GREEN];; Assign the value returned by the following expression to the symbol 'itm'[/color]
                   ([color=BLUE]assoc[/color] blk lst) [color=GREEN];; Attempt to retrieve a list item whose first element is equal to the block name[/color]
               ) [color=GREEN];; end setq[/color]
               [color=GREEN];; Update the existing list entry:[/color]
               ([color=BLUE]setq[/color] lst [color=GREEN];; Redefine the 'lst' variable with the updated list data[/color]
                   ([color=BLUE]subst[/color] [color=GREEN];; Substitute the following list item in the list[/color]
                       ([color=BLUE]cons[/color] blk ([color=BLUE]1+[/color] ([color=BLUE]cdr[/color] itm))) [color=GREEN];; Increment the number of occurrences recorded for this item in the list[/color]
                       itm [color=GREEN];; The existing item to be substituted[/color]
                       lst [color=GREEN];; The list in which to perform the substitution[/color]
                   ) [color=GREEN];; end subst[/color]
               ) [color=GREEN];; end setq[/color]
               [color=GREEN];; Else add a new entry to the list:[/color]
               ([color=BLUE]setq[/color] lst [color=GREEN];; Redefine the 'lst' variable with the following updated list data[/color]
                   ([color=BLUE]cons[/color] [color=GREEN];; 'Push' a new item onto the front of the list[/color]
                       ([color=BLUE]cons[/color] blk 1) [color=GREEN];; Construct a dotted pair whose first key is the block name and value is 1[/color]
                       lst [color=GREEN];; The list to which the item should be added (may be nil)[/color]
                   ) [color=GREEN];; end cons[/color]
               ) [color=GREEN];; end setq[/color]
           ) [color=GREEN];; end if[/color]
       ) [color=GREEN];; end repeat[/color]
       [color=GREEN];; Else the user didn't make a selection[/color]
   ) [color=GREEN];; end if[/color]
   [color=GREEN];; Print the results (if they exist)[/color]
   ([color=BLUE]foreach[/color] itm lst [color=GREEN];; For every 'itm' in the list given by 'lst'[/color]
       ([color=BLUE]princ[/color] [color=GREEN];; Print the following to the command-line[/color]
           ([color=BLUE]strcat[/color] [color=GREEN];; Concatenate the following strings[/color]
               [color=MAROON]"\n"[/color] [color=GREEN];; (New-line character)[/color]
               ([color=BLUE]car[/color] itm) [color=GREEN];; The block name[/color]
               [color=MAROON]": "[/color] [color=GREEN];; An arbitrary separator for the data[/color]
               ([color=BLUE]itoa[/color] ([color=BLUE]cdr[/color] itm)) [color=GREEN];; The number of occurrences of the block, converted to a string[/color]
           ) [color=GREEN];; end strcat[/color]
       ) [color=GREEN];; end princ[/color]
   ) [color=GREEN];; end foreach[/color]
   ([color=BLUE]princ[/color]) [color=GREEN];; Suppress the return of the last evaluated expression (if)[/color]
) [color=GREEN];; end defun[/color]

 

Yes, Lee, this was exactly what I was looking for. I will look it over to see how you did it and see how I can use it in my own code. Thank you so much.

Link to comment
Share on other sites

Wow, Lee, I know it's been a while, but when did you start commenting your code so detailed?

 

Haha! - Most of my code has no comments whatsoever (the comments take longer to write than the code itself!), I usually only add comments when it is apparent that the OP is a beginner looking to learn from the code.

Link to comment
Share on other sites

Yes, Lee, this was exactly what I was looking for. I will look it over to see how you did it and see how I can use it in my own code. Thank you so much.

 

You're most welcome Beeftimer - I hope the comments are clear.

Link to comment
Share on other sites

You're most welcome Beeftimer - I hope the comments are clear.

 

As clear as they can be without giving me an entire course on coding. Google is helping me with the rest, which also inadvertently lead me to a website that shares your name. Hmmm... very helpful, although I do have some research to do in order to put all of this info to use. I took only the minimum required programming courses in college and some of this is over my head, but you've given me a great place to start. The comments help tremendously.

Link to comment
Share on other sites

Excellent, I'm pleased to hear that you find my website helpful. Of course, if you find something that is unclear, or if you are struggling to understand why a particular function or expression has been used in the code, feel free to post a question and I or another member of this community will be happy to offer a clearer explanation.

 

Lee

Link to comment
Share on other sites

Excellent, I'm pleased to hear that you find my website helpful. Of course, if you find something that is unclear, or if you are struggling to understand why a particular function or expression has been used in the code, feel free to post a question and I or another member of this community will be happy to offer a clearer explanation.

 

Lee

 

Hello again, Lee. I was doing some dissecting of your code and I pretty much understood everything except this part (I took out the comments): (if (setq itm (assoc blk lst) )...

 

I'm confused because the way I understand an if function just doesn't seem to jive here. I'm used to seeing something like, (if (= 2 3) (do something) (princ "no)). If I've written the LISP code correctly here it would evaluate as "if 2=3 then do something, else print no." But in your code, it seems to say, "if set a variable, itm, to (assoc blk lst) then do this...."

 

It doesn't make sense to me for two reasons:

 

1. How can setting a variable be a condition of an if statement

2. lst is introduced here without yet being defined in the code, unless I'm mistaken.

 

I know the code works, so I'm not questioning your logic (nor am I in any position to), but I would really appreciate it if you might clear this up for me. Everything else is solid and I've learned a lot from looking at it. It's just this one part that I'm hung up on.

 

Thanks!

Link to comment
Share on other sites

Wait, would it be that the first time the if statement goes through the loop, it would return nil and then the else statement would set the variable lst? Then the second time (and every time thereafter) lst would be defined? Is that it?

Link to comment
Share on other sites

[ For those following the thread, the below post is an explanation of the following code which is an example of a simple block counter ]

(defun c:simplecount ( / blk idx itm lst sel ) ;; Define function, declare local variables
   (if ;; If the following expression returns a non-nil value
       (setq sel ;; Assign the value returned by the following expression to the symbol 'sel'
           (ssget ;; Prompt the user to make a selection and return the selection set if successful
              '((0 . "INSERT")) ;; Filter the selection to block references only (INSERTs)
           ) ;; end ssget
       ) ;; end setq
       (repeat ;; Repeat the enclosed expressions the following number of times:
           (setq idx ;; Assign the value returned by the following expression to the symbol 'idx'
               (sslength sel) ;; Return the number of items in the selection set
           ) ;; end setq
           (setq blk ;; Assign the block name to the variable 'blk'
               (cdr ;; Retrieve the value associated with DXF group 2 (the block name)
                   (assoc 2 ;; Retrieve the DXF group 2 dotted pair from the following DXF data
                       (entget ;; Retrieve the list of DXF data for the following entity
                           (ssname sel ;; Retrieve the entity at the following index
                               (setq idx (1- idx)) ;; Decrement the index variable (since selection set indexes are zero-based)
                           ) ;; end ssname
                       ) ;; end entget
                   ) ;; end assoc
               ) ;; end cdr
           ) ;; end setq
           ;; If the block is already recorded in the list:
           (if ;; If the following expression returns a non-nil value
               (setq itm ;; Assign the value returned by the following expression to the symbol 'itm'
                   (assoc blk lst) ;; Attempt to retrieve a list item whose first element is equal to the block name
               ) ;; end setq
               ;; Update the existing list entry:
               (setq lst ;; Redefine the 'lst' variable with the updated list data
                   (subst ;; Substitute the following list item in the list
                       (cons blk (1+ (cdr itm))) ;; Increment the number of occurrences recorded for this item in the list
                       itm ;; The existing item to be substituted
                       lst ;; The list in which to perform the substitution
                   ) ;; end subst
               ) ;; end setq
               ;; Else add a new entry to the list:
               (setq lst ;; Redefine the 'lst' variable with the following updated list data
                   (cons ;; 'Push' a new item onto the front of the list
                       (cons blk 1) ;; Construct a dotted pair whose first key is the block name and value is 1
                       lst ;; The list to which the item should be added (may be nil)
                   ) ;; end cons
               ) ;; end setq
           ) ;; end if
       ) ;; end repeat
       ;; Else the user didn't make a selection
   ) ;; end if
   ;; Print the results (if they exist)
   (foreach itm lst ;; For every 'itm' in the list given by 'lst'
       (princ ;; Print the following to the command-line
           (strcat ;; Concatenate the following strings
               "\n" ;; (New-line character)
               (car itm) ;; The block name
               ": " ;; An arbitrary separator for the data
               (itoa (cdr itm)) ;; The number of occurrences of the block, converted to a string
           ) ;; end strcat
       ) ;; end princ
   ) ;; end foreach
   (princ) ;; Suppress the return of the last evaluated expression (if)
) ;; end defun
 
Beeftimer said:
I pretty much understood everything except this part (I took out the comments): (if (setq itm (assoc blk lst) )...

 

It doesn't make sense to me for two reasons:

 

1. How can setting a variable be a condition of an if statement

2. lst is introduced here without yet being defined in the code, unless I'm mistaken.

 

Beeftimer said:
Wait, would it be that the first time the if statement goes through the loop, it would return nil and then the else statement would set the variable lst? Then the second time (and every time thereafter) lst would be defined? Is that it?

 

You've got it.

 

But for completeness, I'll go into more detail and answer your earlier points:

 

Quote
1. How can setting a variable be a condition of an if statement

Firstly, any expression, variable, or constant can form the 'test expression' argument of an if statement - this is not restricted to an expression involving the evaluation of a function, such as =.

 

And more importantly is the fact that, in AutoLISP, a conditional expression (such an if statement) will be validated if the test expression returns any non-nil value - that is, the test expression doesn't necessarily need to return T or True explicitly for the if statement to be validated, for example consider the following:

(setq var 1)
(if var
   (princ "Do this.")
   (princ "Else do this.")
)
 

Here, the test expression is simply the symbol 'var' - this symbol is evaluated by the if function to yield any value it may have been assigned. In the above example, the symbol 'var' is a variable which has been assigned a value of 1. Since 1 is not equal to nil, the if statement is validated and the 'then' argument will be evaluated.

 

Only if the test expression returns a value equivalent to nil will the 'else' expression (if supplied) be evaluated.

 

So, back to my code (comments removed):

(if (setq itm (assoc blk lst))
   (setq lst (subst (cons blk (1+ (cdr itm))) itm lst))
   (setq lst (cons  (cons blk 1) lst))
)
 

In the above, the test expression is:

(setq itm (assoc blk lst))
 

However, since the return of the setq function is equal to the last value assigned to the last symbol/value pair (since setq can assign multiple values to multiple symbols in a single expression), the setq expression will simply return the value returned by the assoc expression.

 

So the test expression is equal to the value returned by:

(assoc blk lst)
 

Here, the variable blk contains the name of the block at a given iteration of the repeat loop, and the variable lst will contain an association list which may be empty - consider that nil is equivalent to an empty list:

_$ (= '() nil)
T
 

Which brings us to your second question:

Quote
2. lst is introduced here without yet being defined in the code, unless I'm mistaken.

On the first iteration of the repeat loop, the variable lst is indeed undefined and hence the symbol lst holds no value and is null. However, since a nil value is equal to an empty list, the symbol lst is effectively a variable containing an empty list, which can be passed as an argument to the assoc function.

 

Since there are no list items to query, the assoc expression will obviously return nil, causing the parent setq expression to also return nil and so the test expression is not validated and the 'else' argument for the if statement is evaluated:

(setq lst (cons (cons blk 1) lst))
 

Here, the cons function is being used in two ways to return two different data types: when supplied with two atoms, the cons function will return a dotted pair, e.g.:

_$ (cons "a" 1)
("a" . 1)
 

Hence, the inner cons expression will return a dotted pair in which the first element is the block name, and the second element is the integer value 1, e.g.:

_$ (cons "myblock" 1)
("myblock" . 1)
 

The first element (or key) of a dotted pair can be obtained using the car function (standing for Contents of Address Register), with the associated value obtained using the cdr function (Contents of Decrement Register):

_$ (car (cons "myblock" 1))
"myblock"
_$ (cdr (cons "myblock" 1))
1
 

However, when the second argument passed to cons is a list, the cons function will 'push' the first argument onto the list adding it to the front of the list, observe:

_$ (cons 1 '(2 3 4 5))
(1 2 3 4 5)
 

And, since nil can be viewed as being an empty list, this is a valid list argument for the cons expression:

_$ (cons 1 nil)
(1)
 

In our case, on the first iteration of the repeat loop, the contents of the lst variable might look something like:

_$ (setq lst (cons (cons "myblock" 1) lst))
(("myblock" . 1))
 

If, on the second iteration of the repeat loop a new block name was encountered, it would simply be pushed onto the list held by the lst variable:

_$ (setq lst (cons (cons "myblock2" 1) lst))
(("myblock2" . 1) ("myblock" . 1))
 

In each case, the 'else' argument of the if statement has been evaluated, adding a new element to the list assigned to lst, and redefining the lst variable in the process to hold the new updated list.

 

But what if a block was encountered with a block name already contained in the associated list?

 

In this case, the assoc expression would return the dotted pair whose first element is the block name:

_$ (assoc "myblock" lst)
("myblock" . 1)
 

This is of course a non-nil value, and so the 'then' argument for the if statement will be evaluated:

(setq lst (subst (cons blk (1+ (cdr itm))) itm lst))
 

In short, this expression modifies the value associated with the dotted pair retrieved by assoc, and then substitutes the new dotted pair for the old dotted pair in the association list, which is then returned by the subst function and redefines the lst variable by virtue of the setq expression.

 

Evaluating each expression from the inside out reveals this behaviour:

_$ (setq blk "myblock")
"myblock"
_$ (setq itm (assoc blk lst))
("myblock" . 1)
_$ (cdr itm)
1
_$ (1+ (cdr itm))
2
_$ (cons blk (1+ (cdr itm)))
("myblock" . 2)
_$ (subst (cons blk (1+ (cdr itm))) itm lst)
(("myblock2" . 1) ("myblock" . 2))
_$ (setq lst (subst (cons blk (1+ (cdr itm))) itm lst))
(("myblock2" . 1) ("myblock" . 2))
 

I hope this clarifies things, but if you have further questions, feel free to ask.

 

Lee

Edited by Lee Mac
Link to comment
Share on other sites

I hope this clarifies things, but if you have further questions, feel free to ask.

 

Lee

 

This is all very, very helpful and informative. You speak my language, in the sense of explaining things; the way you explain it to me sounds a lot like the way i would explain things that i understand to others who don't, i.e. you make this very straight forward and easy for me to understand. I wish all of my college professors were this thorough and easily understandable (I had maybe a total of two who were, and I'll never forget them).

 

Just a couple (or three) more small questions, though:

 

1. What is the meaning of _$ ? I've also seen this on your website.

2. The repeat function is basically a for-loop, yes?

3. A dotted pair is simply a list with two atoms?

 

And again, thank you so much for taking so much time to help me out. I hope that one day I can pay it forward, but then again I guess that's the point of this forum, right?

Link to comment
Share on other sites

_$ Thats what you type at command line. Answer follows good when trying to test and do line by line or need a value.

Repeat is a for loop also look at foreach similar.

 

3 Lee ?

Link to comment
Share on other sites

_$ Thats what you type at command line. Answer follows good when trying to test and do line by line or need a value.

Repeat is a for loop also look at foreach similar.

 

Thanks for the response, Bigal. When I type _$ in the command line, it says, "Unknown command "$". Press F1 for help." I still don't understand this.

Link to comment
Share on other sites

Also (to anyone who might know the answer), when using a single quote (e.g. when inputting a list), basically what that's doing is telling the computer that the expression inside the parentheses is not a function, right? For example, when you write:

 

(foreach n '(a b c) print n))

 

the ' tells the function that (a b c) is a list instead of telling it that "a" is a function to be evaluated, correct? Do I understand this properly? Lee (if you're reading this), I know you went in depth explaining this on your website, but I just wanted to make sure that I've got a handle on this concept. Thanks, guys!

 

Beef

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