Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Need to check if layer is in one of multiple lists

8 REPLIES 8
SOLVED
Reply
Message 1 of 9
adjg13
696 Views, 8 Replies

Need to check if layer is in one of multiple lists

I am trying to write a piece of a program that gets the layers from a drawing, and checks each one against a list of layers. If the layer is not a member of any of the lists it asks the user which layer list to add it to. This is what I have so far, but every time I try to run it I get a bad argument error.

(setq layerspresent (vla-get-layers (vla-get-activedocument (vlax-get-acad-object))))
	(setq i 0)
      (repeat (vla-get-count layerspresent)
		(setq thislayer (vla-get-name (vla-item layerspresent i)))
		(cond
			(
				((progn
					(or
						(= nil (member thislayer 0list))
						(= nil (member thislayer detaillist))
						(= nil (member thislayer clearancelist))
						(= nil (member thislayer clearanceelist))
						(= nil (member thislayer doorsdrawerslist))
						(= nil (member thislayer hiddenlist))
						(= nil (member thislayer hiddenelist))
						(= nil (member thislayer deletelist))
					)
				));ifs
				((progn
					(initget "0 DEtail CLearance CLEarance-e DOOrs-and-Drawers HIdden HIddEn-E DELete")
					(setq userchoice (getkword "\nChange layer to which AQSL layer/delete layer? (0 DEtail CLearance CLEarance-e DOOrs-and-Drawers HIdden HIDdn-e DELete)"))
					(cond
						(
							(= userchoice "0")
							(progn (
							(setq f (open "C:\\filterlists\\0list.txt" "a"))
							(write-line (cadr thislayer) f)
							(close f)
							(cons 0list thislayer)
							))
						)
						(
							(= userchoice "DEtail")
							(progn (
							(setq f (open "C:\\filterlists\\detaillist.txt" "a"))
							(write-line (cadr thislayer) f)
							(close f)
							(cons detaillist thislayer)
							))
						)
						(
							(= userchoice "CLearance")
							(progn (
							(setq f (open "C:\\filterlists\\clearancelist.txt" "a"))
							(write-line (cadr thislayer) f)
							(close f)
							(cons clearancelist thislayer)
							))
						)
						(
							(= userchoice "CLEarance-e")
							(progn (
							(setq f (open "C:\\filterlists\\clearanceelist.txt" "a"))
							(write-line (cadr thislayer) f)
							(close f)
							(cons clearanceelist thislayer)
							))
						)
						(
							(= userchoice "DOOrs-and-Drawers")
							(progn (
							(setq f (open "C:\\filterlists\\doorsdrawerslist.txt" "a"))
							(write-line (cadr thislayer) f)
							(close f)
							(cons doorsdrawerslist thislayer)
							))
						)
						(
							(= userchoice "HIdden")
							(progn (
							(setq f (open "C:\\filterlists\\hiddenlist.txt" "a"))
							(write-line (cadr thislayer) f)
							(close f)
							(cons hiddenlist thislayer)
							))
						)
						(
							(= userchoice "HIDden-e")
							(progn (
							(setq f (open "C:\\filterlists\\hiddenelist.txt" "a"))
							(write-line (cadr thislayer) f)
							(close f)
							(cons hiddenelist thislayer)
							))
						)
						(
							(= userchoice "DELete")
							(progn (
							(setq f (open "C:\\filterlists\\deletelist.txt" "a"))
							(write-line (cadr thislayer) f)
							(close f)
							(cons deletelist thislayer)
							))
						)
					)
				));thens
			);1st if then
			(
				(princ)
			);alt if then
		);cond
		(setq i (+ i 1))
      );end loop

 Depending on the user's choice the layer gets written to an external file where the layer lists get their layers from in the beginning and then adds the layer to the relevant list to use later in the program.

 

If anyone has any insight that would be great.

 

Thank you.

8 REPLIES 8
Message 2 of 9
Kent1Cooper
in reply to: adjg13


@adjg13 wrote:

I am trying to write a piece of a program that gets the layers from a drawing, and checks each one against a list of layers. If the layer is not a member of any of the lists it asks the user which layer list to add it to. This is what I have so far, but every time I try to run it I get a bad argument error.

 Depending on the user's choice the layer gets written to an external file where the layer lists get their layers from in the beginning and then adds the layer to the relevant list to use later in the program.

....


I expect the bad argument is from this line in each user-choice condition [here from the first one for "0"]:

 

  (cons 0list thislayer)

 

You have the arguments reversed.  The first argument in (cons) needs to be an item, and in this situation the list needs to be the second argument.  So 0list is a bad argument in that position.

 

Additionally, the result of that (cons) is not used in any way.  I think you need this instead:

 

  (setq 0list (cons thislayer 0list))

 

There are other things that could be simplified [e.g. you have many (progn) functions that may "work" but are not needed], and I may reply again with some suggestions later.  But try making the above change, and see whether it works.

Kent Cooper, AIA
Message 3 of 9
adjg13
in reply to: Kent1Cooper

I reversed the arguments in my cons statement, but I get the same error. After doing a little more debugging I found that the error is coming from this part in the code.

 

((progn
(or (= nil (member thislayer 0list)) (= nil (member thislayer detaillist)) (= nil (member thislayer clearancelist)) (= nil (member thislayer clearanceelist)) (= nil (member thislayer doorsdrawerslist)) (= nil (member thislayer hiddenlist)) (= nil (member thislayer hiddenelist)) (= nil (member thislayer deletelist)) )
))

; error: bad function: T

I don't see why it is giving me a bad function error because it comes out to true. The first statements should be true and then it should continue with the rest of the cond statement.

 

Also I am new to LISP,. I still haven't figured out when you have to use (progn and when you can have multiple conditions/expressions without using (progn.

 

Thank you.

Message 4 of 9
adjg13
in reply to: adjg13

Ok so I changed the code a little bit and now at least I'm getting a different error.

 

(setq layerspresent (vla-get-layers (vla-get-activedocument (vlax-get-acad-object))))
	(setq i 0)
      (repeat (vla-get-count layerspresent)
		(setq thislayer (vla-get-name (vla-item layerspresent i)))
		(cond
			(
				(or
					(= nil (member thislayer 0list))
					(= nil (member thislayer detaillist))
					(= nil (member thislayer clearancelist))
					(= nil (member thislayer clearanceelist))
					(= nil (member thislayer doorsdrawerslist))
					(= nil (member thislayer hiddenlist))
					(= nil (member thislayer hiddenelist))
					(= nil (member thislayer deletelist))
				)
				(
					(initget "0 DEtail CLearance CLEarance-e DOOrs-and-Drawers HIdden HIddEn-E DELete")
					(setq userchoice (getkword (strcat "\nChange " (cadr thislayer) " to which AQSL layer/delete layer? (0 DEtail CLearance CLEarance-e DOOrs-and-Drawers HIdden HIDdn-e DELete)")))
					(cond
						(
							(= userchoice "0")
							(progn (
							(setq f (open "C:\\filterlists\\0list.txt" "a"))
							(write-line thislayer f)
							(close f)
							(cons thislayer 0list)
							))
						)
						(
							(= userchoice "DEtail")
							(progn (
							(setq f (open "C:\\filterlists\\detaillist.txt" "a"))
							(write-line thislayer f)
							(close f)
							(cons thislayer detaillist)
							))
						)
						(
							(= userchoice "CLearance")
							(progn (
							(setq f (open "C:\\filterlists\\clearancelist.txt" "a"))
							(write-line thislayer f)
							(close f)
							(cons thislayer clearancelist)
							))
						)
						(
							(= userchoice "CLEarance-e")
							(progn (
							(setq f (open "C:\\filterlists\\clearanceelist.txt" "a"))
							(write-line thislayer f)
							(close f)
							(cons thislayer clearanceelist)
							))
						)
						(
							(= userchoice "DOOrs-and-Drawers")
							(progn (
							(setq f (open "C:\\filterlists\\doorsdrawerslist.txt" "a"))
							(write-line thislayer f)
							(close f)
							(cons thislayer doorsdrawerslist)
							))
						)
						(
							(= userchoice "HIdden")
							(progn (
							(setq f (open "C:\\filterlists\\hiddenlist.txt" "a"))
							(write-line thislayer f)
							(close f)
							(cons thislayer hiddenlist)
							))
						)
						(
							(= userchoice "HIDden-e")
							(progn (
							(setq f (open "C:\\filterlists\\hiddenelist.txt" "a"))
							(write-line thislayer f)
							(close f)
							(cons thislayer hiddenelist)
							))
						)
						(
							(= userchoice "DELete")
							(progn (
							(setq f (open "C:\\filterlists\\deletelist.txt" "a"))
							(write-line thislayer f)
							(close f)
							(cons thislayer deletelist)
							))
						)
					)
				)
			);1st if then
			(
				(princ)
			);alt if then
		);cond
		(setq i (+ i 1))
      );end loop

 Now I get an error here

 

(setq userchoice (getkword (strcat "\nChange " (cadr thislayer) " to which AQSL layer/delete layer? (0 DEtail CLearance CLEarance-e DOOrs-and-Drawers HIdden HIDdn-e DELete)")))

 Specifcally at (cadr thislayer) the error is error: bad argument type: consp "0". I just want to take the layer name part of thislayer and write it out in that string.

Message 5 of 9
Kent1Cooper
in reply to: adjg13


@adjg13 wrote:

I reversed the arguments in my cons statement, but I get the same error. After doing a little more debugging I found that the error is coming from this part in the code.

 

((progn
(or (= nil (member thislayer 0list)) ....
(= nil (member thislayer deletelist)) )
))
; error: bad function: T

I don't see why it is giving me a bad function error because it comes out to true. The first statements should be true and then it should continue with the rest of the cond statement.

 

Also I am new to LISP,. I still haven't figured out when you have to use (progn and when you can have multiple conditions/expressions without using (progn.

 

Thank you.


The problem is that the first of your double left parentheses before progn is an open-a-function starter, and what it expects afterwards is a function name.  The (progn (or ..... )) part returns T, which isn't a function name.  It may work if you eliminate the doubling of parentheses, but in fact, you don't need the (progn) at all there.

 

The (progn) function is for when you need to perform multiple functions but they need to be collectively treated as a single function.  Most often, people use it when they need to do multiple things together as the 'thenexpr' or the 'elseexpr' in an (if) function, since it only accepts a single function for each of those arguments.

 

In this case, the (or) function constitutes a single 'testexpr' function if you do it in an (if), or a 'test' if you do it in a (cond) function.  So you can just do this:

 

(cond

  ( ; open first condition

    (or ; 'test' expression

      (not (member thislayer 0list))

      ....

      (not (member thislayer deletelistlist))

    ); end or

    (.... go on to do stuff if ....); 'result'

    (.... the test was satisfied ....); 'result'

  ); close first condition

  ( ; open second condition

  ....etc.

 

But since there's only one condition to test for at this level of the operation, it may as well be (if) instead, which adds a (progn) to "wrap" multiple operations for the 'thenexpr', but eliminates the open-and-close-each-condition parentheses:

 

(if

  (or ; 'testexpr'

    (not (member thislayer 0list))

    ....

    (not (member thislayer deletelistlist))

  ); end or

  (progn ; 'thenexpr'

    (.... go on to do stuff if ....)

    (.... the test was satisfied ....)

  ); end progn

); end if

 

I'll post some consolidation suggestions a little later....

Kent Cooper, AIA
Message 6 of 9
Kent1Cooper
in reply to: adjg13


@adjg13 wrote:

.... 

 Now I get an error here

 

(setq userchoice (getkword (strcat "\nChange " (cadr thislayer) " to which AQSL layer/delete layer? (0 DEtail CLearance CLEarance-e DOOrs-and-Drawers HIdden HIDdn-e DELete)")))

 Specifcally at (cadr thislayer) the error is error: bad argument type: consp "0". I just want to take the layer name part of thislayer and write it out in that string.


The 'thislayer' variable is going to contain a text string, not a list.  (cadr) is to get the second element out of a list.  Use just:

 

... (strcat "\nChange " thislayer " to which AQSL ...

 

You'll need to make the same change lower down in the (write-line) functions [but I have a suggestion for a way to make it one (write-line) function -- coming soon].

Kent Cooper, AIA
Message 7 of 9
Kent1Cooper
in reply to: Kent1Cooper


@Kent1Cooper wrote:
.... [but I have a suggestion for a way to make it one (write-line) function -- coming soon].

Any time I see a lot of the same code repeated with minor differences, I figure there ought to be a way to consolidate it and do the repetitive things once, usually using variables to stand in for the minor differences.  Here's a way to do that with your routine, also incorporating corrections in earlier posts and a few other minor tweaks.  It greatly reduces the amount of code by spelling out one (member) function to find whether the layer name is in any of the lists, one opening of a file and writing to it and closing it, etc.  It does use some AutoLISP functions that will seem "higher-level" to a beginner, as they did to me when I first started doing these things, but I have found them very useful, and they're worth knowing about.  [Not having your lists and layers and all, I didn't test it, so I hope it works....]

 

(setq layerspresent (vla-get-layers (vla-get-activedocument (vlax-get-acad-object))))
(setq i 0)
(repeat (vla-get-count layerspresent)
  (setq thislayer (vla-get-name (vla-item layerspresent i)))
  (if ; not in any of the layer lists?
    (= 'textexpr'
      (length
        (vl-remove
          nil ; remove any nil results from list returned by (mapcar)
          (mapcar ; apply member check of layer name across all lists, return list of results
            '(lambda (x) (member thislayer x)); applied to all in following list
            (list 0list detaillist clearancelist clearanceelist doorsdrawerslist hiddenlist hiddenelist deletelist)
          ); mapcar
        ); vl-remove
      ); length
      0 ; all nils removed from list of results, so it's empty [returned nil for all lists]
    ); =
    (progn ; 'thenexpr'
      (initget "0 DEtail CLearance CLEarance-e DOOrs-and-Drawers HIdden HIDden-e DELete")
      (setq
        userchoice
          (getkword
            (strcat
              "\nChange "
              thislayer
              " to which AQSL layer/delete layer? [0 DEtail CLearance CLEarance-e DOOrs-and-Drawers HIdden HIDdn-e DELete]"
            ); strcat
          ); getkword & 'userchoice'
        listname (strcase (vl-string-subst "" "and-" (vl-string-subst "" "-" userchoice)) T)
          ; makes list name, also beginning of file name, from 'userchoice' with
          ; hyphen(s) and "and" removed, forced to all lower-case, as text string
        f (open (strcat "C:\\filterlists\\" listname "list.txt") "a"))
      ); setq
      (write-line thislayer f)
      (close f)
      (set (read listname) (cons thislayer (eval (read listname)); put layer name in local list
    ); progn -- 'thenexpr'
  ); if [no 'elseexpr' -- do nothing if it is already in one of the lists]
  (setq i (1+ i))
); repeat

Kent Cooper, AIA
Message 8 of 9
dbroad
in reply to: Kent1Cooper

In addition to the changes Kent proposed, the structure:

...

(setq i 0)
(repeat (vla-get-count layerspresent)
  (setq thislayer (vla-get-name (vla-item layerspresent i)))...

 

is somewhat simpler as

...

(vlax-for layer layerspresent 

   (setq thislayer (vla-get-name layer))...

 

 

Architect, Registered NC, VA, SC, & GA.
Message 9 of 9
adjg13
in reply to: dbroad

Thank you. These are very helpful concepts. I will tweak it a little bit and see if I can get it to work. I will end up doing something like this to the first part of the program where it reads in the layers from the files and saves them to a series of local lists. Right now I repeat the following code about seven times

(setq f (open "C:\\filterlists\\hiddenlist.txt" "r"))
	(setq count 0)
	(while (/= (setq text (read-line f)) nil)
		(setq hiddenlist
			(cond
				(
					(= 0 count)
					(cons (cons 8 text) hiddenlist)
				)
				(
					(append hiddenlist (list (cons 8 text)))
				)
			)
		)
		(setq count (+ count 1))
	)
	(close f)

 But I need to rewrite it to something similar to your code Kent. I just couldn't figure out a way to pass the list to a variable outside of the loop. Autolisp is so odd in that you don't really define variables before manipulating them, but I think I am starting to understand how it works.

 

EDIT: I got it to work with only a few minor tweaks. Thank you very elegant. I am definitely going to rewrite the first part in a manner similar to this.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost