AfraLisp Blog

The AutoLisp/Visual Lisp/VBA Resource Website

AfraLisp Blog

Home Newsletter Utter Rubbish Coding Tips AutoCAD Tips Contact Downloads WAUN

Selection Objects

Selecting Objects and creating Selection Sets is much the same in Visual
Lisp as it is for standard AutoLisp except for two main differences. All Entities
contained in an AutoLisp selection set, must be converted to VLA Objects
before VLA functions can be applied to them, and you cannot use AutoCAD interactive functions such as (entsel) or (ssget) within a reactor callback function.

In this tutorial, we we look at Selecting Objects and creating Selection sets using the (entsel) function, the (ssget) function and then using only VLA functions.

Let's look at selecting a single entity first using (entsel).
Consider this coding :

(defun selectionlisp1 ( / sset check)

;load the visual lisp extensions
(vl-load-com)

;check for selection
(while
  
	;get the entity and entity name
	(setq sset (car (entsel)))

	;convert to vl object
	(setq sset (vlax-ename->vla-object sset))

	;check if the entity has a color property
	;and it can be updated
	(setq check (vlax-property-available-p sset "Color" T))

	;if it can
	(if check

		;change it's color
		(vlax-put-property sset 'Color 4)

	);if

);while

  (princ)

);defun

(princ)

We select the entity using the standard (entsel) function. We then have to
convert the entity to a VLA Object by using the (vlax-ename->vla-object)
function.
Next we check to see if the object first has a color property, and secondly
is updateable using the (vlax-property-available-p) function with it's "T"
argument set.
Finally we change it's color property using the (vlax-put-property) function.
Dead easy, hey?

Create Selection sets utilising (ssget) is also quite straightforward.
Have a look at this :

(defun selectionlisp2 ( / sset item ctr check)

;load the visual lisp extensions
(vl-load-com)

;check for selection
(while

	;get the selection set
	(setq sset (ssget))

	;set up the counter
	(setq ctr 0)

	;count the number of entities and loop
	(repeat (sslength sset)

		;extract the entity name
		(setq item (ssname sset ctr))

		;convert to vl object
		(setq item (vlax-ename->vla-object item))

	  	;check if the entity has a color property
		;and it can be updated
		(setq check (vlax-property-available-p item "Color" T))

		;if it can
		(if check

			;change it's color
			(vlax-put-property item 'Color 6)

		);if

		;increment the counter
		(setq ctr (1+ ctr))

	);repeat

);while

  (princ)

);defun

(princ)

The only difference here, is that we need to loop through each individual
item in the selection set, converting it to a VLA Object, and then checking
and changing it's properties.
Again this is standard AutoLisp with a little bit of VLA thrown in to confuse
you.

To create a selection set using only VLA is a slightly different matter.
Here we need to access the Object Model to reference, create and
store our selection set. Have a look at this :

(defun selectionvl ( / ssets acadDocument newsset ctr item)

;load the visual lisp extensions
(vl-load-com)

;retrieve a reference to the documents object
(setq acadDocument (vla-get-activedocument
 			   (vlax-get-acad-object)))

;retrieve a reference to the selection sets object
(setq ssets (vla-get-selectionsets acadDocument))

;add a new selection set
(setq newsset (vla-add ssets "SS1"))

;select your new selection set objects
(vla-selectOnScreen newsset)

;set the counter to zero
(setq ctr 0)

 ;count the number of objects and loop
 (repeat (vla-get-count newsset)

   	;retrieve each object
   	(setq item (vla-item newsset ctr))

   	;check if the entity has a color property
   	;and it can be updated
  	(setq check (vlax-property-available-p item "Color" T))

   	;if it can
   	(if check

   		;change it's color
   		(vlax-put-property item 'Color 5)

   	);if

   	;increment the counter
   	(setq ctr (1+ ctr))

 );repeat

  ;delete the selection set
  (vla-delete (vla-item ssets "SS1"))

  (princ)

);defun

(princ)

First we need to reference the selection set collection. Looking at the
Object Model, we find this collection to be part of the "Active Document" Object.
After grabbing and storing our reference, using the (vla-get-selectionsets) function, we then need to create a new selection set. (SS1) using the
(vla-add) function. 
O.K. that's done, now what next?
Well, we've got a selection set but there's nothing in it. Using the (vla-selectOnScreen) method, we populate our selection set with the
Objects that we would like to process. Again, we use the (repeat) function
to loop through all of our objects, checking if each object has a color
property and is updateable before changing it's color. Of course, we
don't need to convert these Objects as they are already VLA Objects.
The final step in our application is to delete the selection set to prevent
"Selection Set Already Exists" errors when we run the routine again.


Selecting with Filters

Now things get a wee bit tricky. I suggest you read my Tutorial on "Arrays" before you carry on with this section, as a good understanding of them will help you a lot.

First of all, let's have a look at the VBA syntax for Selecting with Filters :

object.SelectOnScreen [FilterType][, FilterData]

Object : SelectionSet
The object or objects this method applies to.

FilterType : Integer; input-only; optional
A DXF group code specifying the type of filter to use.

FilterData : Variant; input-only; optional
The value to filter on.

ActiveX requires a Filter Type to be an Array of Integers, and the Filter Data to be an Array of Variants. To Filter for all Objects on Layer "7" we would write it like this in Visual Lisp:

;create a single element array for the DXF Code
(setq filter_code (vlax-make-safearray vlax-vbinteger '(0 . 0)))

;create a single element array for the value
(setq filter_value (vlax-make-safearray vlax-vbvariant '(0 . 0)))

;DXF Code for layers
(vlax-safearray-fill filter_code '(8))

;the filter value
(vlax-safearray-fill filter_value '("7"))

;Use Select on Screen to select objects on Layer 7
(vla-selectOnScreen newsset filter_code filter_value)

In AutoLisp you would write this :

(setq newsset (ssget '((8 . "7"))))


This is how the revised program would look :

(defun selectionvl ( / ssets acadDocument newsset ctr item filter_code filter_value)

;load the visual lisp extensions
(vl-load-com)

;retrieve a reference to the documents object
(setq acadDocument (vla-get-activedocument
     (vlax-get-acad-object)))

;retrieve a reference to the selection sets object
(setq ssets (vla-get-selectionsets acadDocument))

;add a new selection set
(setq newsset (vla-add ssets "SS1"))

;create a single element array for the DXF Code
(setq filter_code (vlax-make-safearray vlax-vbinteger '(0 . 0)))

;create a single element array for the value
(setq filter_value (vlax-make-safearray vlax-vbvariant '(0 . 0)))

;DXF Code for layers
(vlax-safearray-fill filter_code '(8))

;the filter value
(vlax-safearray-fill filter_value '("7"))

;Use Select on Screen to select objects on Layer 7
(vla-selectOnScreen newsset filter_code filter_value)

;set the counter to zero
(setq ctr 0)

 ;count the number of objects and loop
 (repeat (vla-get-count newsset)

   ;retrieve each object
   (setq item (vla-item newsset ctr))

   ;check if the entity has a color property
   ;and it can be updated

  (setq check (vlax-property-available-p item "Color" T))

   ;if it can
   (if check
       
   ;change it's color
   (vlax-put-property item 'Color 5)

   );if

   ;increment the counter
   (setq ctr (1+ ctr))

 );repeat

  ;delete the selection set
  (vla-delete (vla-item ssets "SS1"))

  (princ)

);defun

(princ)

     


To select ALL the Circles on Layer "2", We would write this :

 ;create a 2 element array for the DXF Code
(setq filter_code (vlax-make-safearray vlax-vbinteger '(0 . 1)))

;create a 2 element array for the values
(setq filter_value (vlax-make-safearray vlax-vbvariant '(0 . 1)))

;DXF Code for Objects and Layer
(vlax-safearray-fill filter_code '(0 8))

;the filter values
(vlax-safearray-fill filter_value '("CIRCLE" "2"))

;select ALL Circles on Layer 2
(vla-select newsset acSelectionSetAll nil nil filter_code filter_value)


This is the equivalent in AutoLisp :

(setq newsset (ssget "x" '((0 . "CIRCLE") (8 . "2"))))


To select ALL Circles OR Text, we would write this :

;create a 4 element array for the DXF codes
(setq filter_code (vlax-make-safearray vlax-vbinteger '(0 . 3)))

;create a 4 element array for the names
(setq filter_value (vlax-make-safearray vlax-vbvariant '(0 . 3)))

;DXF Codes for OR and Objects
(vlax-safearray-fill filter_code '(-4 0 0 -4))

;the filter values
(vlax-safearray-fill filter_value '("<or" "CIRCLE" "TEXT" "or>"))

;select ALL Circles OR Text
(vla-select newsset acSelectionSetAll nil nil filter_code filter_value)

In AutoLisp, this is equivalent to :

(setq newsset  (ssget "x" '((-4 . "<or") (0 . "CIRCLE") (0 . "TEXT") (-4 . "or>"))))

Can you see what we are doing?

We are basically "splitting" the AutoLisp dotted pairs into two arrays, one containing the DXF codes, and the other containing the filter values.


There is another way of filtering the Selection Set itself. Consider this code:

(if (= (vlax-get-property item 'ObjectName) "AcDbCircle")

      (vlax-put-property item 'Color 3)

);if


Makes you think doesn't it?


Selection Set Already Exists

Did you notice how, at the end of our routines, we added the line :

 (vla-delete (vla-item ssets "SS1"))

If we didn't delete the selection set, next time we ran the routine we would get a "Selection Set Already Exists" error. Now adding this line is fine if everything runs correctly in our routine, but what happens if the user Cancels or there is another error that causes our program not to reach this line.
Have a look at this :

(defun selset_test ()

(vl-load-com)

(setq acadDocument (vla-get-activedocument
     (vlax-get-acad-object)))

(setq ssets (vla-get-selectionsets acadDocument))

(setq flag nil)

(vlax-for item ssets

(if (= (vla-get-name item) "newsset")
  	(setq flag T)
);if

);

(if flag

  	(vla-delete (vla-item ssets "newsset"))
  	
);if

  (setq newsset (vla-add ssets "newsset"))

);defun
      

This routine loops through each selection set in the selection set collection.
If it finds the selection set (newset), it deletes it and then creates it. If it does not find it, it simple creates it.

A better way of achieving the same result, is to make use of the (vl-Catch-All-Apply) function :

(defun selset_test1 ()

(vl-load-com)

(setq acadDocument (vla-get-activedocument
     (vlax-get-acad-object)))

(setq ssets (vla-get-selectionsets acadDocument))

(if (vl-catch-all-error-p (vl-catch-all-apply 'vla-item (list ssets "$Set")))

  (setq newSet (vla-add ssets "$Set"))
  
     (progn
       
    	(vla-delete (vla-item ssets "$Set"))
       
  	(setq newSet (vla-add ssets "$Set"))
       
     );progn

);if
      

Have a look at "Visual Lisp and Errors" for a more detailed explanation of the
(vl-Catch-All-Apply) function.


 Well, that's about it with "Selecting Objects." Not too bad, hey?
(I can hear you groaning from here in Africa, Hee, hee, hee.)

 
The AutoLisp/Visual Lisp/VBA Resource Website

Copyright 1999-Perpetuity by AfraLisp

All rights reserved.
Information in this document is subject to change without notice.
Site created and maintained by Kenny Ramage

The AutoLisp/Visual Lisp/VBA Resource Website