November 15, 2019

ACA-AMEP: Edit Property Data Via AutoLISP, Part 3

First article in the series.
Previous article in the series.

In the previous article, we used a while loop to examine each Property Set in the collection of Property Sets attached to the object of interest, comparing each Property Set's Name to the name of the Property Set that holds the Property whose value is to be changed. At this point, there are two possible conditions:
  1. A matching Property Set was found. The value in the Property Set counter variable, iPSet, was set to one more than the number of Property Sets in the collection (variable iPSets).
  2. A matching Property Set was not found. The value in the Property Set counter variable, iPSet, will be equal to the number of Property Sets in the collection (variable iPSets).
(cond    ; Cond A.
  ((= iPSet iPSets)
   ;; If the Property Set is not found after processing all attached property sets, issue prompt.
   (prompt
     (strcat
       "\nProperty Set "
       sPSet
       " not found on object passed to argument obj. "
     ) ;_ End strcat.
   ) ;_ End prompt.
   nil    ; Return nil.
  ) ;_ End condition A1.
  (T    ; Else, continue.
   (setq oProps (vlax-get-property oPSet 'Properties)
          ; Get Properties object of the Property Set.
         iProps (vlax-get-property oProps 'Count)
    ; Get number of Properties in the Property Set.
         iProp  0  ; Initialize Property counter.
   ) ;_ End setq.

   (while (< iProp iProps) ; While unprocessed Properties remain...
     (setq oProp   (vlax-invoke-method oProps 'Item iProp)
    ; Get current Property object.
    sPropNm (vlax-get-property oProp 'Name)
    ; Get Property name.
     ) ;_ End setq.
     (if (= (strcase sPropNm) (strcase sProp))
        ; If target Property found...
       (setq iProp (1+ iProps)) ; ...exit while loop.
       (setq iProp (1+ iProp)) ; ...else, increment Property counter.
     ) ;_ End if.
   ) ;_ End while.

   (cond   ; Cond A2B.
     ((= iProp iProps)
      ;; If the Property is not found after processing all Properties in the Property Set, issue prompt.
      (prompt
        (strcat
          "\nProperty "
          sProp
          " not found in Property Set "
          sPSet
          " attached to the object passed to argument obj. "
        ) ;_ End strcat.
      ) ;_ End prompt.
      nil   ; Return nil.
     ) ;_ End condition A2B1.
     (T    ; Else, update Property value with processed value.
      (setq varPropValOld (vlax-get-property oProp 'Value))

      ***** ADD CODE HERE TO GENERATE THE DESIRED NEW VALUE   *****
      ***** SET VARIABLE sPropValNew to the NEW DESIRED VALUE *****

      (vlax-put-property oProp 'Value sPropValNew)
      sPropValNew   ; Return new string value.
     ) ;_ End condition A2B2.
   ) ;_ End cond A2B.

  ) ;_ End condition A2.
) ;_ End cond A.

Here is what is happening in that code:
  • A cond statement is used to determine which of the two conditions noted above is active. The first test checks to see if iPSet and iPSets are equal. If this is true, then the target Property Set was not found, and there is nothing to process. A prompt is written to the command line to inform the user. I have a separate subroutine to hold this code, so after issuing the prompt, this condition returns nil to the calling routine, to indicate that the requested Property Set was not found. The calling routine can then choose how to handle that situation.
  • If iPSet and iPSets are not equal, then the target Property Set was found and we can continue on. The test for this condition is T (True), because if the first test is false, no further test is required, and the code to process will be contained within this condition.
  • Variable oPSet holds the Property Set object whose name matched the target Property Set name. The Properties property of the Property Set object is used to obtain the Properties collection of that Property Set. The Properties collection contains all of the individual Property objects that are defined in that Property Set Definition.
  • The Count property of the Properties collection is used to determine the total number of Properties in the collection (variable iProps). A variable to hold the Property counter, iProp is initialized to 0, the index of the first Property in the collection.
  • Similar to the way we iterated over the Property Set collection, a while loop is used to iterate over the Properties collection, looking for a Property whose name matches the target property name, which is held in variable sProp. If a match is found, iProp is set to one more than the total number of Properties in the collection. If a match is not found, then iProp is incremented by 1, so that the next Property in the collection will be processed on the next pass of the loop, if at least one unprocessed Proeprty remains.
  • At this point, there are two possible conditions:
    1. A matching Property was found, and the value of iProp is one more than the value of iProps
    2. A matching Property was not found, and the values of iProp and iProps are equal.
  • Once again, a cond statement is used to run the appropriate code. If a matching Property was not found, the first condition writes a prompt to the command line to inform the user that the Property was not found, and returns nil to the calling routine, to indicate that the requested Property was not found in the requested Property Set. The calling routine can then choose how to handle that situation.
  • If the target Property was found, then the first condition will not be executed, and the second condition will be executed, as the test is set to T.
  • The Value property of the Property object is used to obtain the current value, storing it in variable varPropValOld.
  • At this point, you will need to add the code that generates the new value for the target Property, saving that value to variable varPropValNew
  • The vlax-put-property function is used to push the new value onto the Value property of the Property Object. This condition then returns the new value to the calling routine.
  • All of the currently open conditions and cond functions are then ended.

If you choose to have all your code in a single AutoLISP command function, then instead of returning nil or varPropValNew at the end of the A1, A2B1 and A2B2 conditions, you would add whatever additional code you may feel appropriate to run prior to the end of your command function.

November 05, 2019

ACA-AMEP: Edit Property Data Via AutoLISP, Part 2

First article in the series.

In the previous article, we obtained a collection of the Property Sets on the object for which we want to change the value of a Property. We also determined the total number of Property Sets in the collection, and set up a Property Set counter, initialized to 0, the index of the first Property Set. The following code assumes that you know not only the name of the Property that is to be changed, but also the name of the Property Set in which it resides and that the name is stored in a variable called sPSet.
(while (< iPSet iPSets)  ; While unprocessed Property Sets remain...
  (setq oPSet   (vlax-invoke-method oPSets 'Item iPSet) ; Get current Property Set object.
        sPSetNm (vlax-get-property oPSet 'Name)         ; Get Property Set name.
  ) ;_ End setq.
  (if (= (strcase sPSetNm) (strcase sPSet))             ; If target Property Set is found...
    (setq iPSet (1+ iPSets))                            ; ...exit while loop.
    (setq iPSet (1+ iPSet))                             ; ...else, increment Property Set counter.
  ) ;_ End if.
) ;_ End while.

Here is what is happening in that code:
  • A while loop is used to iterate over the Property Sets collection. The Item method is used to get the Property Set at the current index value in iPSet.
  • The Name property of the Property Set object is used to obtain the name of that Property Set.
  • An if statement is used to determine if that Property Set name matches the target Property Set name. The strcase function is used to change both strings to all capital letters, to avoid any issues with different capitalization of the names. If a match is found, the iPSet index counter is set to a number one greater than the total numnber of Property Sets in the collection, so that the loop will be exited. If a match is not found, the iPSet index counter is incremented by 1, so that the next Property Set in the collection will be processed on the next pass of the loop, if at least one unprocessed Property Set remains.

Next article in the series.

November 01, 2019

ACA-AMEP: Edit Property Data Via AutoLISP, Part 1

Sometimes you need to make changes to the values of a Property on all or many AEC Objects in a file. If that change can be done programatically (example, in a text-type manual Property, edit the text value, replacing all occurrences of "1ST" with "2ND"), and there are a large number of Objects to process, it may be easier to do so via AutoLISP, than to manually edit each value. This series of articles will show you how to do so.

NOTE: I have only used this technique on manual Properties. I am not certain what would happen if you tried to use this to change the value of an automatic property. I suspect the change may fail, at least in some cases, like formula Properies. Even if it works for some automatic Properties, such as the height or width of a Door, doing so may have unexpected results in the drawing. If you have a use case for trying to edit the values of automatic Properties, please test this thoroughly on copies of files, or files created strictly for testing purposes, not on actual project files, until you are certain there is no harm.

The VisualLISP commands will be used to access the object model. You will also need to operate on vla-objects. If your method of getting the selection set on which to operate results in AutoLISP entity names, these can be converted to vla-objects by using the
(setq obj (vlax-ename->vla-object ename))
function, where obj is the name of a variable holding the vla-object and ename is the name of a variable holding the entity name of the object on which you want to operate.

Getting to the Property value is not a matter of simply querying the object for it. You will need to drill down into the object model. Start with the following code. You can use different variable names if the ones used in the example code here do not conform to your naming standard. Just make certain you substitute your names consistently.
(vl-load-com)
(setq objAcad    (vlax-get-acad-object)  ; AutoCAD Application Object.
      objAecSchd (vla-GetInterfaceObject objAcad (strcat "AecX.AecScheduleApplication" (aecappver)))
                                         ; AecScheduleApplication object.
      oPSets     (vlax-invoke-method objAecSchd 'PropertySets obj)
                                         ; Get PropertySets collection object.
      iPSets     (vlax-get-property oPSets 'Count)
                                         ; Get number of Property Sets in the PropertySets collection object.
      iPSet  0                           ; Initialize Property Set counter.
) ;_ End setq.

Here is what is happening in that code:
  • The (vl-load-com) function loads the VisualLISP functions, if they are not already loaded. If they are loaded, it does nothing.
  • The first item in the setq function gets the AutoCAD object. This is then used to get the AEC Schedule Application object. This object is version-specific, so I use a subroutine I put together to return the version string, called AECAPPVER. You can find the code for that routine in this previous article. The article was updated to support versions through 2020.
  • The third item in the setq function uses the PropertySets method of the AEC Schedule Application object to get the PropertySets collection for the object on which we want to operate (which is held in the obj variable).
  • The final two items in the setq function determine the number of Property Sets that are in that collection, and initializes a Property Set counter to 0, which is the index of the first Property Set in the collection.

Next article in the series.