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

Variable Scope

20 REPLIES 20
Reply
Message 1 of 21
dbrblg
1321 Views, 20 Replies

Variable Scope

I've had a quick look around the forums but cannot find anything on variable scope......

 

How do I define a global variable where I can access from two different lisp functions?

 

For example, something along the lines of:

(vl-load-com)

[var x]

(defun func2 ()
  (setq x "ABC")
  (princ)
)

(defun func1 ()
  (setq x "DEF")
  (princ)
)

 

Thanks

 

 

20 REPLIES 20
Message 2 of 21
Kent1Cooper
in reply to: dbrblg


@dbrblg wrote:

.....

How do I define a global variable where I can access from two different lisp functions?

 

For example, something along the lines of:

....
(defun func2 ()
  (setq x "ABC")
  (princ)
)

(defun func1 ()
  (setq x "DEF")
  (princ)
)

....


If you want to be able to set or change the variable x from within more than one function, and have either function replace any value set into that variable by the other, then what you have will work, not listing x as a localized variable within either one.  If you want each of the functions to have its own value in x without affecting its value for the other function, then it can't be global -- you would need to either localize it within each function:
 
(defun func1 (/ x)

....

 

[which would mean each function would need to make any use of x before it finishes], or it would need to have a different name in each function, such as x1 and x2.

 

Or am I misunderstanding what you're trying to do?

Kent Cooper, AIA
Message 3 of 21
M_Hensley
in reply to: dbrblg

You could save data in the registry and access it later from anther lisp by using SETENV and GETENV functions.

Message 4 of 21
dbrblg
in reply to: dbrblg

Thanks for your suggestions. 

 

I'm giving up with lisp for the time being...may eventually get back to it when I have found some patience.

 

I cannot believe lisp still exists in AutocadSmiley Frustrated

Message 5 of 21
Kent1Cooper
in reply to: dbrblg


@dbrblg wrote:

.... 

I'm giving up with lisp for the time being....


Don't give up yet.  I admit it's not clear to me what you mean by the phrase "variable scope" [for example, is "variable" there a noun or an adjective?].  If you can describe in more detail what you want to accomplish, or how you want to use the variable(s), there may well be something here already, or someone may come up with an easy solution, or it may be appropriate to something like [in the case of your sample settings] the USERS1-through-USERS5 System Variables, or something.

Kent Cooper, AIA
Message 6 of 21
dbrblg
in reply to: dbrblg

Hi Kent,

 

I'm trying to do something really quite simple....well, or so I thought anyway Smiley Surprised

 

With reference to my variable scope, I suspect I used a generic name to describe what I want.  It is a little ambiguous but I also suspect the terminology in lisp may be different from that of other languages.  

 

Basically what I am trying to achieve is to switch on certain points when a certain command is activated and then return them to what they were before when the command ends.

 

For example I have this:

(vl-load-com)

(defun c:StartReactor ()
  (or *Reactor_Command*
      (setq *Reactor_Command*
	     (vlr-lisp-reactor
	       "My command reactor "
	       '((:vlr-lispWillStart . Callback:LispWillStart)
		 (:vlr-lispEnded . Callback:LispEnded)
		)
	     )
      )
  )
  (prompt "\n  >>  Command reactor loaded ")
  (princ)
)

(defun Callback:LispWillStart (rea cmd)
;;;  (alert (strcat "Lisp started: " (car cmd)))
  (if (eq (car cmd) "(C:AEFOOTPRINT)")
    (progn
      (prompt (car cmd))
      (setq pointMode (getvar 'PDMODE))
      (prompt (strcat "\n pointMode Variable: " pointMode "\n PDMODE Variable: " (itoa(getvar 'PDMODE))))
      (setvar "PDMODE" 99)
      (prompt (strcat "\n PDMODE Variable: " (itoa(getvar 'PDMODE))))
    )
  )
  (princ)
)

(defun Callback:LispEnded (rea cmd)
;;;  (alert "Lisp ended")
  (if (/= pointMode nil)
    (progn
      (prompt (strcat "\n PDMODE Variable: " (pointMode)))
      (setvar 'PDMODE pointMode)
    )
(prompt (strcat "\n pointMode Variable is nil"))
  )
  (princ)
)

  (defun c:StopReactor ()
    (if	*Reactor_Command*
      (progn
	(vlr-remove *Reactor_Command*)
	(setq *Reactor_Command* nil)
      )
    )
    (prompt "\n  >>  Command reactor stopped ")
    (princ)
  )


(defun *error* (x) (vl-bt))

 As you can see there are a lot of prompts and alerts in there.  The debugger in Autocad is primitive to say the least (or it's me not grasping it fully) and i'm finding it a nightmare to debug properly.

 

The idea with the variable is there is one (called pointMode) which is shared between the LispWillStart and LispEnded functions which holds the state of PDMODE before it is changed and then allows it to be restored.

 

Thats the idea anyway.  I'm getting so many errors it's quite hard to describe where to start, hence I thought a break would be in order. 

 

Make of this what you will Smiley Happy it's not particuarly 'tidy' or efficient shall we say....but then again, lisp is not my first language, but you may have guessed that!!

 

 

Message 7 of 21
Kent1Cooper
in reply to: dbrblg


@dbrblg wrote:

.... what I am trying to achieve is to switch on certain points when a certain command is activated and then return them to what they were before when the command ends.

 

For example I have this:

....
      (setq pointMode (getvar 'PDMODE))
      (prompt (strcat "\n pointMode Variable: " pointMode "\n PDMODE Variable: " (itoa(getvar 'PDMODE))))
      (setvar "PDMODE" 99)
      (prompt (strcat "\n PDMODE Variable: " (itoa(getvar 'PDMODE))))
....
 
....
  (if (/= pointMode nil)
    (progn
      (prompt (strcat "\n PDMODE Variable: " (pointMode)))
      (setvar 'PDMODE pointMode)
    )
(prompt (strcat "\n pointMode Variable is nil"))
....

....The idea with the variable is there is one (called pointMode) which is shared between the LispWillStart and LispEnded functions which holds the state of PDMODE before it is changed and then allows it to be restored.

....


I haven't worked with reactors, but I notice a couple of things....

 

The pointMode variable will hold an integer, so you're going to need to convert that to a string to include it in a prompt.  After setting it to match PDMODE, you have a prompt that is telling the values of both, which will be the same.  And after setting PDMODE to 99, you're digging the value out of that, when you already know that's what it is.  Maybe something like this in place of the parts quoted above.

 

....
      (setq pointMode (getvar 'PDMODE))
      (prompt

        (strcat

          "\nCurrent PDMODE System Variable, saved in pointMode Variable: "

          (itoa pointMode)

          ".\nSetting PDMODE to 99."

        ); strcat

      ); prompt
      (setvar "PDMODE" 99)
....
 
....
  (if pointMode ; [easier than checking whether it's not equal to nil]
    (progn ; then
      (prompt (strcat "\nResetting PDMODE Variable to " (itoa pointMode)))
      (setvar 'PDMODE pointMode)
    ); progn
    (prompt "\n pointMode Variable is nil"); else [without unneeded (strcat)]

  ); if
....

Kent Cooper, AIA
Message 8 of 21
Moshe-A
in reply to: dbrblg

Hi,

 

from my experience (of many years) there is no problem with global (or local variables) in AutoLISP

and i know some other languages that i can compare to. the rules are the same.

 

here is a fix i made for you especially look at (lprompt) functon which let to prompt any message

without the need to convert int/real to string.

 

Moshe

 

 

(defun lprompt (args / a)
 (foreach a args (princ a))
)


(vl-load-com)
(setq LISP-REACTOR-DATA "My command reactor")

(defun c:StartReactor ()
  (or *Reactor_Command*
      (setq *Reactor_Command*
	     (vlr-lisp-reactor
	       LISP-REACTOR-DATA
	       '((:vlr-lispWillStart . Callback:LispWillStart)
		 (:vlr-lispEnded     . Callback:LispEnded    )
		)
	     )
      )
  )
  
  (prompt "\n>>Command Reactor is enabled now.")
  (princ)
)

(defun Callback:LispWillStart (rea cmd)
;;;  (alert (strcat "Lisp started: " (car cmd)))
  (if (and (eq (vlr-data rea) LISP-REACTOR-DATA)
           (eq (car cmd) "(C:AEFOOTPRINT)")
      )
    (progn
      (lprompt (list "\n" (car cmd) "\n"))
      (setq pointMode (getvar 'PDMODE))
      (lprompt (list "start: pointMode Variable: " pointMode "\nstart: PDMODE Variable: " (getvar 'PDMODE) "\n"))
      (setvar 'PDMODE 99)
      (lprompt (list "start: PDMODE Variable: " (getvar 'PDMODE) "\n"))
    )
  )

  (princ)
)

(defun Callback:LispEnded (rea cmd)
;;;  (alert "Lisp ended")
  (if (and (eq (vlr-data rea) LISP-REACTOR-DATA)
	   (/= pointMode nil)
      )
   (progn
    (lprompt (list "ended: PDMODE Variable: " pointMode "\n"))
    (setvar 'PDMODE pointMode)
   )
   (prompt "\nended: pointMode Variable is nil.\n")
  )

  (princ)
)

(defun c:StopReactor ()
 (if *Reactor_Command*
  (progn
   (vlr-remove *Reactor_Command*)
   (setq *Reactor_Command* nil)
  )
 )
  
 (prompt "\n>>Command Reactor is Disabled now")
  
 (princ)
)


(defun C:AEFOOTPRINT ()
 ; only invokes (Callback:LispWillStart)
 (princ)
)

 

Message 9 of 21
scot-65
in reply to: dbrblg


@dbrblg wrote:

How do I define a global variable where I can access from two different lisp functions? 


Global variables, for current session (until AutoCAD is closed), I refer to as "Gremlins".

I have a few of these written in several of my programs.

The structure I use to name this variable is "USER_xxx", where "xxx" is the routine name.

 

To create this Gremlin, do not declare the variable in the defun section, as you showed.

(defun c:MyABC ( / a b c)

 (setq a 1)

 (setq USER_MyABC "True")

 (princ)

)

 

USER_MyABC can now be seen by other programs, however "a" cannot.

 

It would be wise to declare this global variable outside the realm of the programs that access this.

Suggest declaring this inside any interface file (LSP, MNL, etc.), or in S::STARTUP itself.

Suggest having long names with underscore(s), in case other third-party programs happen

to employ the same variable name as his gremlin. Yes, it has happened to me early on when

I inherited our menu system with these known bugs.

Suggest inside the program that will reference this variable to do a check first, then apply

a default value if not found: (if (not USER_MyABC) (setq USER_MyABC "True")).

 

Where SDI=0, and you switch drawings, can the next drawing see USER_MyABC?

I do not know this answer. But to be safe, you can use the SETCFG/GETCFG, and

VL-REGISTRY-READ/VL-REGISTRY-WRITE methods to access this across drawings.

 

Hope this helps.

 

???

 


Scot-65
A gift of extraordinary Common Sense does not require an Acronym Suffix to be added to my given name.


Message 10 of 21
Moshe-A
in reply to: scot-65

Hi Scot-65,

 

(vl-bb-set) & (vl-bb-ref)

 

never used them my self but seems they can provide some solution?

 

moshe

 

 

 

Message 11 of 21
Kent1Cooper
in reply to: Moshe-A


@Moshe-A wrote:

....

(vl-bb-set) & (vl-bb-ref)

 

never used them my self but seems they can provide some solution?

.....


Yes, they can, within the same AutoCAD session [the blackboard namespace dies when you get out of AutoCAD].  When I want something that's going to be available from any drawing being edited on the same computer, and that will still be around after getting out of AutoCAD and even turning off the computer, I use (setenv) and (getenv).

Kent Cooper, AIA
Message 12 of 21
Moshe-A
in reply to: Kent1Cooper

Kent hi,

 

i didn't realize that the OP said 'global variables' he meant to variables that stay after AutoCAD is closed

cause he did indicate that AutoLISP is a poor comparing other languages. are you familiar with other language

that defines a global variable and automaticlly writes it to registry?!

 

about (getenv) & (setenv)?

i thought these functions deals with the environment variables of the OS (DOS SET command)

from what i understand now they are written to registry? by who AutoCAD or OS?

where are they stored (how can i see them from REGEDIT)

 

(getenv "MaxHatch")

(getenv "MaxArray")

 

i always wonder where these variables are stored and if there are more?

 

Moshe

 

Message 13 of 21
Kent1Cooper
in reply to: Moshe-A


@Moshe-A wrote:

.... 

i didn't realize that the OP said 'global variables' he meant to variables that stay after AutoCAD is closed.... 

about (getenv) & (setenv)?

.... 

i always wonder where these variables are stored and if there are more?

.... 


I'm not sure whether the OP is looking for something that stays after AutoCAD closes, but I made the suggestion in case they are.
 

I haven't looked into where they're stored, or how to find them other than by (getenv), but as to the question "if there are more," there are as many as you want, because you can make your own.  For example, here's a routine of mine that makes/stores/uses up to 20 of them:

 

http://cadtips.cadalyst.com/2d-editing/save-and-reuse-multiple-offset-distance-values

Kent Cooper, AIA
Message 14 of 21
dbroad
in reply to: dbrblg

@dbrblg

 

In your first post, X was a global variable because it was not listed in each argument as a local variable.  Such variable names are poor global choices because they are too easily reused.  They make OK local var names but a better approach would be to make the name self-commenting.

 

Your third post included code which was helpful.  Others have indicated the prompt formatting errors and have fixed them but the main problems are putting messages and prompting into the reactors.  In general, avoid all prompting in reactors.  

 

Another error IMO is to define an *error* function instead of using vlide's internal debugging features.  While debugging, hit CTRL+ F9 to find out where the problem occurs (in Vlide).  Use the "break on error" function to enable examining the state of variables.

 

 I propose that your callbacks should have looked like this:

 

((defun Callback:LispWillStart (rea cmd)
(if (eq (car cmd) "(C:AEFOOTPRINT)")
  (progn
(vlr-data-set rea (getvar "pdmode"))
(setvar "pdmode" 99)))
)

(defun Callback:LispEnded (rea cmd)
  (if (vlr-data rea)
    (progn
      (setvar "pdmode" (vlr-data rea))
      (vlr-data-set rea nil))
)

 

Data shared between callbacks to the same reactor are best shared in the reactor's own data.

 

A much better approach to using reactors, however, would be to rewrite the C:AEFOOTPRINT command to change the point style and to restore the point style itself.  It would reduce the reactor overhead of constantly monitoring every lisp command.

Architect, Registered NC, VA, SC, & GA.
Message 15 of 21
Moshe-A
in reply to: dbroad

dboard hi,

 

i thing the OP is only trying to learn reactors, so the prompts only to make sure he is in right place.

the use of (vlr-data-set) as you suggested is not the best way to use it if there are more (vlr-lisp-reactor) around.

 

moshe

 

Message 16 of 21
dbroad
in reply to: Moshe-A


i thing the OP is only trying to learn reactors, so the prompts only to make sure he is in right place.

the use of (vlr-data-set) as you suggested is not the best way to use it if there are more (vlr-lisp-reactor) around.

 

moshe

 


During setup of a reactor, using vlr-trace-reaction is better for tesing than setting up dummy prompts, especially if the prompts will not be used in the final reactor.  In this case, they were the source of the errors, which tend to stay hidden in reactors unless you use vlide break on error or vl-bt.

 

Although globals can be used, vlr-data-set is exactly what to do and is preferred. Each vlr-lisp-reactor works independently and has its own data storage.  The one he designed is specific to C:AEFOOTPRINT.  It isolates the data in such a way that only the callback functions and the setup functions have access to the data. In other words vlr-data is global to the LISP reactor callbacks and local wrt other functions ( a protected resource).

 

The problem is that his callback functions are too generically named IMO.

Architect, Registered NC, VA, SC, & GA.
Message 17 of 21
Moshe-A
in reply to: dbroad


Although globals can be used, vlr-data-set is exactly what to do and is preferred. Each vlr-lisp-reactor works independently and has its own data storage.  The one he designed is specific to C:AEFOOTPRINT.  It isolates the data in such a way that only the callback functions and the setup functions have access to the data. In other words vlr-data is global to the LISP reactor callbacks and local wrt other functions ( a protected resource).


 

i agree that each (vlr-lisp-reactor) works independently and (vlr-data-set) assign a specific data to reactor but if there is a second (vlr-lisp-reactor) defined? (that do some other settings) any lisp command that will be invoked

will fire the two lisp reactors? am i right?

 

how would you control in the callback that the lisp command that was firing your callback is the lisp command

you were expecting (and not other) as you do not want to set pdmode each lisp command is fired.

 

moshe

 

 

 

Message 18 of 21
dbroad
in reply to: Moshe-A


i agree that each (vlr-lisp-reactor) works independently and (vlr-data-set) assign a specific data to reactor but if there is a second (vlr-lisp-reactor) defined? (that do some other settings) any lisp command that will be invoked will fire the two lisp reactors? am i right?

 

how would you control in the callback that the lisp command that was firing your callback is the lisp command you were expecting (and not other) as you do not want to set pdmode each lisp command is fired.


Yes, each lisp reactor you create fires separately and there is no way to predict order of which reactor will fire first. If more than one lisp command needed to do similar things, they could share the same reactor and the check about which command was firing could be added in common. The start callback would just check for the commands that it applies to and ignore the rest.

 

You would not use 2 lisp reactors to do essentially the same thing IMO, such as changing point styles.

Architect, Registered NC, VA, SC, & GA.
Message 19 of 21
Moshe-A
in reply to: dbroad

if more than one lisp command needed to do similar things, they could share the same reactor and the check about which command was firing could be added in common.

 


i don't know what you are doing with your applications (if it's for your own use only)?

but mine evently will reach others and i always bring in consideration that they are

running other applications simultaneously that defines some other reactors?

that's why i always 'protects' my applications from been interfere to other.

 

 

 

Message 20 of 21
dbrblg
in reply to: Kent1Cooper

Wow 🙂

 

 

I'm back at work after the weekend and seen all the replies...I've looked trough them and appreciate all your help. 

 

I think I should clarify; when I meant global I was not necessarily thinking of retaining data when the computer is switched off but as in variables being globally available to all of the functions within a lisp file and not just the function the variable was defined in. 

 

Having said that, the replies which have detailed a permanent storage method will still come in handy with other projects I have planned so is still useful to me.

 

This is quite a response....thanks for all your advice and comments.  There is certainly a lot to read and take in.

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

Post to forums  

Autodesk Design & Make Report

”Boost