Author Topic: While Loop to Select a viewport  (Read 2610 times)

0 Members and 1 Guest are viewing this topic.

Daniel J. Ellis

  • Swamp Rat
  • Posts: 811
While Loop to Select a viewport
« on: July 22, 2013, 03:59:33 AM »
I'm trying to use a WHILE loop to ensure that a user selects a viewport.  The code I have is:
Code: [Select]
(WHILE
(NOT
(= objtype "VIEWPORT")
);NOT
(SETQ
vport (ENTSEL "\nSelect a single Viewport")
obj (CAR vport)
objdata (ENTGET obj)
objtype (GETASSOC objdata 0)
);SETQ
(ALERT "This was indeed a viewport, thankyou")
);WHILE

This doesn't seem to recognise that something isn't a viewport - Whenever ANYTHING is selected it pops up the "This was indeed a viewport" alert.

Any and all help would be greatly appreciated.

dJE
===
dJE

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: While Loop to Select a viewport
« Reply #1 on: July 22, 2013, 04:14:34 AM »
The trick is to check for negatives. The while loop should finish when there are only positives in the tests. E.g.
Code - Auto/Visual Lisp: [Select]
  1. (while (or (not (setq ent (entsel "Select a single Viewport: ")))
  2.            (/= (cdr (assoc 0 (setq edata (entget (car ent))))) "VIEWPORT"))
  3.   (alert "You've not selected a viewport,\nplease try again."))
  4. (alert "You've selected a viewport")
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Lee Mac

  • Seagull
  • Posts: 12912
  • London, England
Re: While Loop to Select a viewport
« Reply #2 on: July 22, 2013, 06:38:52 AM »
To understand why your code is producing undesired results, walk through the code expression by expression and analyse the result of each evaluation:



On first pass:

objtype = nil

Therefore:

(NOT (= objtype "VIEWPORT")) = T

validating the test expression for the while loop.

1. User has selected an object, but NOT a Viewport:

vport   = (<Entity name: abc123> (123.0 456.0 0.0))
obj     = <Entity name: abc123>
objdata = ((0 . "CIRCLE") (5 . "ABC") ... )
objtype = "CIRCLE"


Now the alert expression is evaluated, regardless of the object type:

(ALERT "This was indeed a viewport, thankyou")

All expressions within the while loop have now been evaluated, so the test expression is again evaluated:

(NOT (= objtype "VIEWPORT")) = (NOT (= "CIRCLE" "VIEWPORT")) = T

So the loop continues:

2. User does not select anything:

vport   = nil
obj     = nil
objdata = ; error: bad argument type: lentityp nil
objtype = "CIRCLE" (<-- still holds previous value)


The entget expression will return an error as the entity name argument is nil (since obj is nil).

3. User selects a Viewport:

vport   = (<Entity name: abc123> (123.0 456.0 0.0))
obj     = <Entity name: abc123>
objdata = ((0 . "VIEWPORT") (5 . "ABC") ... )
objtype = "VIEWPORT"


The alert expression is again evaluated (though again regardless of the object type):

(ALERT "This was indeed a viewport, thankyou")

The test expression is then evaluated:

(NOT (= objtype "VIEWPORT")) = (NOT (= "VIEWPORT" "VIEWPORT")) = (NOT T) = nil

The test expression returns nil, hence the while loop is terminated.



Whenever you are uncertain as to why a specific block of code is behaving in an undesired way, I would always recommend stepping through the code, expression by expression, as I have above, feeding the expressions each possible case (i.e. user selects invalid object / user fails to select anything / user selects viewport) and noting the returns of each evaluation; the reason for the unexpected behaviour should soon become apparent.

In fact, I would recommend this methodology when attempting to understand any portion of code: step through each expression as if you were playing the role of the AutoLISP interpreter: pass each expression the arguments stated in the code and analyse the return of each evaluation.



For the task at hand, I would personally recommend the following code:

Code - Auto/Visual Lisp: [Select]
  1. (defun c:test ( / sel )
  2.     (while
  3.         (progn (setvar 'errno 0)
  4.             (not
  5.                 (or (setq sel (ssget "_+.:E:S" '((0 . "VIEWPORT"))))
  6.                     (= 52 (getvar 'errno))
  7.                 )
  8.             )
  9.         )
  10.         (princ "\nMissed, try again.")
  11.     )
  12.     (if sel
  13.         (princ "\nThank you for selecting a Viewport.")
  14.         (princ "\nNothing selected, goodbye.")
  15.     )
  16.     (princ)
  17. )

I would opt for ssget over entsel where viewport selection is concerned, since the ssget filter list will permit the user to select polygonal viewports in addition to rectangular viewports - to achieve the same behaviour using entsel you would need to test the selected entity for the existence of a pointer to the associated VIEWPORT entity, (i.e. test for the (102 . "{ACAD_REACTORS") (102 . "}") pair surrounding the VIEWPORT entity).

When using ssget, you could also enable the NOMUTT system variable to facilitate the use of a custom selection prompt.

Crank

  • Water Moccasin
  • Posts: 1503
Re: While Loop to Select a viewport
« Reply #3 on: July 22, 2013, 12:51:54 PM »
@ Lee:
Shouldn't the variable SELECTIONCYCLING temporary be set to 0?
Vault Professional 2023     +     AEC Collection

Daniel J. Ellis

  • Swamp Rat
  • Posts: 811
Re: While Loop to Select a viewport
« Reply #4 on: July 23, 2013, 03:28:04 AM »
Thanks for that detailed response, Lee.  It looks like my original routine wasn't working because I was thinking of the While loop in the way I think about an IF statement (i.e. all eventualities must be included within the statement) when that was not the case.

I'm going to go with your SSGet version, for the reasons you laid out but I do have a couple of queries:
Code: [Select]
(SETVAR 'errno 0) Seems to be setting error reporting to "no error."  I assume this is just to clear out any error that was stored previously?
Code: [Select]
(SSGET "+.:E:S")  The :E tells it to select everything under the picker, and the :S tells it to only select a single entity, I couldn't find anything on the web saying +. does, though.

Thanks again,

dJE
===
dJE

Lee Mac

  • Seagull
  • Posts: 12912
  • London, England
Re: While Loop to Select a viewport
« Reply #5 on: July 23, 2013, 10:02:28 AM »
Code: [Select]
(SETVAR 'errno 0) Seems to be setting error reporting to "no error."  I assume this is just to clear out any error that was stored previously?

Correct, since in the documentation it states:
Quote from: ERRNO Documentation
The ERRNO system variable is not always cleared to zero. Unless it is inspected immediately after an AutoLISP function has reported an error, the error that its value indicates may be misleading.
Therefore, I err on the side of caution and reset it before a selection prompt.

Code: [Select]
(SSGET "+.:E:S")  The :E tells it to select everything under the picker, and the :S tells it to only select a single entity, I couldn't find anything on the web saying +. does, though.

See the reference attached to this post:

http://www.theswamp.org/index.php?topic=42316.msg474784#msg474784

Shouldn't the variable SELECTIONCYCLING temporary be set to 0?

I'm not sure.

Lee Mac

  • Seagull
  • Posts: 12912
  • London, England
Re: While Loop to Select a viewport
« Reply #6 on: July 23, 2013, 12:50:45 PM »
Try your code with:
Code: [Select]
(setvar "SELECTIONCYCLING" 2)
(setvar "SELECTIONPREVIEW" 3)

Good catch Crank, I now see what you are referring to - as you suggest, Daniel may want to consider temporarily disabling Selection Cycling before prompting for selection if polygonal viewports are present.

Crank

  • Water Moccasin
  • Posts: 1503
Re: While Loop to Select a viewport
« Reply #7 on: July 23, 2013, 12:53:07 PM »
Ha, I tried wanted to replace my post, but you're to fast. ;)
Vault Professional 2023     +     AEC Collection