Global Variable List

Global Variable List

jcpLPAKD
Participant Participant
1,165 Views
14 Replies
Message 1 of 15

Global Variable List

jcpLPAKD
Participant
Participant

I am writing a dialog box that is going to have 15 to variables that will need to be recalled when the dialog box is opened again.  I would like to save all the variables to a single list and recall them later rather than having 15 global variables.  I stumbled on how to do this the other day and now I can't find the post again.

 

Any help would be greatly appreciated.

0 Likes
1,166 Views
14 Replies
Replies (14)
Message 2 of 15

pendean
Community Legend
Community Legend
Which 15 variables are you wishing to track exactly?
0 Likes
Message 3 of 15

john.kaulB9QW2
Advocate
Advocate

That's a very vague description/request. Are you talking about an association list or just a list? In the most general terms you'll need a basic list "push" and "pop" function(s) to add/remove items to this list. Please be more specific in your description/needs.

 

(defun list-push (sym lst) 
  ;; list-push
  ;; put an item first into existing list.
  ;; 
  ;; Example: 
  ;;    (list-push 'one 'masterlist)
  ;;    > (ONE)
  ;;    > !masterlist
  ;;    > (ONE)
  ;; OR
  ;;    (setq mylist '(2 3 4))
  ;;    (list-push 1 'mylist)
  ;;    > (1 2 3 4)
  ;;    > !mylist
  ;;    > (1 2 3 4)
  ;;
  ;; By: John K
  ;;    (inspired by Vladimir Nesterovsky)
  ;; 10.24.05
  (set lst (cons sym (eval lst))) )

(defun list-pop (lst / ~tval )
  ;; list-pop
  ;;
  ;; remove the first item from a list and redefne 
  ;; the var containing that list. 
  ;; 
  ;; Example:
  ;; Given a list called "masterlist" with the 
  ;; value of: (TWO ONE)
  ;;
  ;; !masterlist
  ;; (TWO ONE)
  ;;
  ;; (list-pop 'masterlist)
  ;; TWO
  ;;
  ;; (list-pop 'masterlist)
  ;; ONE
  ;;
  ;; !masterlist
  ;; nil
  ;;
  ;; By: John K
  ;;    (inspired by Vladimir Nesterovsky)
  ;;
  ;; 10.24.05
  (setq ~tval (eval lst))
  (set lst (cdr ~tval))
  (car ~tval) )

 

another swamper
0 Likes
Message 4 of 15

Moshe-A
Mentor
Mentor

@jcpLPAKD hi,

 

LISP is exactly the right language to hold a list of values and have wonderful functions to retrieve. cause you did not post your variables here is a general example:

 

(setq iA 5 iB 9 iC -3) ; intergers

(setq rA 2.50 rB 8.38 rC -7.98) ; reals

you can do the same with strings, lists, points

 

Now lets wrap them in list:

(setq lst '()) ; reset lst

(foreach var (list iA iB iC rA rB rC)

 (setq lst (cons var lst))

)

 

(princ (reverse lst))

 

The (cons) function construct the list. As opposed to (append) function, it construct the list in reverse (each item is appended in list head, that's why at the end of the construction we use (reverse) function. the question is why we do not use (append) instead? because (cons) is very fast and in construction of big lists it's the preferable

 

(car lst) ; return 5

(cadr lst) ; return 9

 

also (nth) function can retrieve values:

(nth 0 lst ; return 5

(nth 1 lst) ; return 9

(nth 2 lst) ; return -3

(nth 3 lst) ; return 2.50

(nth 4 lst) ; return 8.38

(nth 5 lst) ; return -7.98

 

as you can see the first item in lst is index 0, second is 1, third 2 and so on...

if you want to know the type of a value, use (type) function

 

(type (nth 2 lst)) ; returns 'INT

(type (nth 5 lst)) ; return 'REAL

and if it is a string, it will return 'STR

if it's a list, it will return 'LIST

 

Moshe

 

 

 

 

 

 

 

 

0 Likes
Message 5 of 15

john.kaulB9QW2
Advocate
Advocate

@Moshe-A wrote:

@jcpLPAKD hi,

<SNIP>

(setq iA 5 iB 9 iC -3) ; intergers

(setq rA 2.50 rB 8.38 rC -7.98) ; reals

 

Now lets put them in list:

(foreach var (list iA iB iC rA rB rC)

 (setq lst (cons var lst))

)

 

(princ (reverse lst))

 

The (cons) function construct the list. As opposed to (append) function, it construct the list in reverse (each item is appended in list head, that's why at the end of the construction we use (reverse) function. the question is why we do not use (append) instead? because (cons) is very fast and in construction of big lists it's the preferable

<SNIP>


@Moshe-A 

Question: why don't you just use `mapcar` instead of going through all that trouble of using a loop-construct and `cons`?

(setq lst (mapcar 'eval '(iA iB iC rA rB rC)))

 

 

EDIT (John):

1. Before I get flack; I know `mapcar` is a looping-construct.

2. I just checked and the `mapcar` method I just posted is two times faster that the `foreach`/`cons` method.

another swamper
0 Likes
Message 6 of 15

Moshe-A
Mentor
Mentor

@john.kaulB9QW2 ,

 



@Moshe-A 

Question: why don't you just use `mapcar` instead of going through all that trouble of using a loop-construct and `cons`?

(setq lst (mapcar 'eval '(iA iB iC rA rB rC)))

 

 

because i think @jcpLPAKD is in a beginner and (forgive me if i'm wrong) using (cons) or (append) is much simple to understand comparing to (mapcar)

 

 

0 Likes
Message 7 of 15

john.kaulB9QW2
Advocate
Advocate

@Moshe-A wrote:

because i think @jcpLPAKD is in a beginner and (forgive me if i'm wrong) using (cons) or (append) is much simple to understand comparing to (mapcar)


*blink-blink* say what?


`cons` means: "construct list of two EXPRESSIONS"
`mapcar` means: "map function to successive CAR's in a list"

 

`cons` is a lot more complicated than I think you give it credit for; it will make two different types of lists -i.e regular and dotted depending on the arguments you give.

 

`mapcar` is the basic form of doing something to each element in a list and it cannot get more basic than that.


Let's try to write out in words what each code block does:

 

Foreach variable in a list assign the item a temporary name of `VAR`. Then create/update a list with the value of the variable; because cons will evaluate each value of its arguments, the temporary name of `VAR` will point to the list variable which points to a value.

 

(foreach var (list iA iB iC rA rB rC)
   (setq lst (cons var lst))
)


Evaluate each variable in a list and return a list (of their values).

(setq lst (mapcar 'eval '(iA iB iC rA rB rC)))

 

Which is more complicated?

 

However, we are way off track here; the OP needs to explain what (s)he wants to do with variables/a var (or value) list/dialog.

another swamper
0 Likes
Message 8 of 15

jcpLPAKD
Participant
Participant

Sorry I wasn't clearer on what I am trying to do. 

Currently when closing the dialog box I am using get_tile to save the edit box and toggle values and individual to global variables. 

 

(action_tile "accept" "(opt_o)(done_dialog 1)")

(defun
   OPT_O ( )
  (setq DT_VAL1 (get_tile "eb1"))
  (setq DT_VAL1a (get_tile "eb1a"))
  (setq DT_VAL1b (get_tile "eb1b"))
  (setq DT_VAL1c (get_tile "eb1c"))
  (setq DT_CK1 (get_tile "tg1"))
  (setq DT_CK1a (get_tile "tg1a"))
;;;additonal variables to be determined
) ; end of OPT_O defun
 

It makes more sense to save these as local variables, add them to a list and save the list to a single global variable.  This is the first time I have had to deal with more than a couple of global variables in a routine and it makes sense to finally take the plunge into creating and breaking apart lists.

0 Likes
Message 9 of 15

john.kaulB9QW2
Advocate
Advocate

I would do something like this:

(defun *save-vars* ( lst / push->lst)
 ;; This procedure will generate a list of
 ;; variables and their value.
 ;;
 ;; By: John Kaul
 ;;
 ;; Ex: (*save-vars*
 ;;        '(("eb1" 0) ("eb2" 1)
 ;;          ("eb1a" 3) ("eb1b" 4)))
 ;;
 ;; The syntax of the list members is as follows:
 ;;  (<Variable name> <value>)
   (defun push->lst (sym lst)
     (set lst (cons sym (eval lst))) )
 (mapcar
  '(lambda (x)
       (push->lst (list 'setq (read (car x)) (cadr x)) '#Dialog-Var_Lst#)
     )
    lst
   )
 )

(defun *restore-vars* () 
  ;; This procedure will restore a list of variables
  ;;
  ;; By: John Kaul
  ;;
  ;; EX: (*restore-vars*)
  (mapcar 'eval #Dialog-Var_Lst#) 
  (setq #Dialog-Var_Lst# nil) 
 )

 

This will create a variable called "#Dialog-Var_Lst#" with the variable names and values.

 

I'm not entirely sure what it is you mean by 'taking the plunge into creating and breaking apart lists' though. 

Good luck. 

 

another swamper
0 Likes
Message 10 of 15

jcpLPAKD
Participant
Participant
I've avoided learning how to really understand working with lists. Most of the code I write rarely needs to access list information.
0 Likes
Message 11 of 15

Kent1Cooper
Consultant
Consultant

Are you talking about global variables only within the same editing session of the same drawing?  That seems to be what the suggestions so far are about.  Do you want to be able to draw on those same variables in a later session, and/or in another drawing?  I'm wondering whether to pursue the idea of Environment Variables, or maybe (vl-propagate) to make them available in other drawings even if not in later editing sessions.

Kent Cooper, AIA
0 Likes
Message 12 of 15

jcpLPAKD
Participant
Participant

Yes, they only need to be available in the current drawing session.

0 Likes
Message 13 of 15

Moshe-A
Mentor
Mentor

@jcpLPAKD ,

 

You think you need these values only in the current session?

 

when you start your program (command) these variables are getting default value - right?!

during the dialog session, user can change these values - right?

at dialog close you are saving these values or maybe not?

 

i assume you are familiar how some standard AutoCAD command dialog works? lets take OPTIONS or UNITS  commands, as you make change to controls (tiles in dcl) they are saved for the next session - agree?

how this done? it's more simple than you think. they are save to registry.

 

before AutoLISP/VisulaLISP gave use functions to read\write to registry we had 2 nice functions to do a similar work

(setcfg) and (getcfg) i suggest you explore these functions and use them for your first dcl program (after you understand their magic you move to vl-read-registry, vl-write-registry)

 

so what you have to do is simple. at dialog close (setcfg) each global variable. at dialog open (getcfg) each value back to set your edit_box

 

if you understand this concept, will will continue 😀

 

Moshe

 

 

 

 

 

 

 

 

 

 

 

 

 

 

0 Likes
Message 14 of 15

jcpLPAKD
Participant
Participant

I am building a dialog box for creating a piece drawing label which lists part name, number, quantity, material, drawing scale and optional additional information. Most of the information is going to be unique to the drawing session but some parts will share some information, ie... the material will usually not change but the quantities will.

So I am not seeing a need to save this information for more than the session.

 

Answering these questions has been very helpful in thinking about other ways I may want the data to be available to the user, like possible adding a reset button to the dialog box.

 

I will certainly look into (setcfg) and (getcfg) for possible use in the future. Thanks!

 

0 Likes
Message 15 of 15

Moshe-A
Mentor
Mentor

@jcpLPAKD ,

 

ok i understand.

 

Anyway, in case you change your mind (or for a future project) here a nice sample how this is done.

 

Note that all functions is declared as local plus local variables. with this structure you protect your program from been interfere by other program\functions using the same names.

 

enjoy

Moshe

 

 

// test.dcl
test_dialog : dialog {
  label = "Test Dialog";
 
  : column {
   : edit_box {
     label = "Text &Value";
     key = "text_value";
     edit_width = 10;
   }
  : edit_box {
    label = "&Increment";
    key = "increment";
    edit_width = 10;
  }
  : edit_box {
    label = "Text &Height";
    key = "text_height";
    edit_width = 10;
  }
 
 }// column

 spacer_1;

 ok_cancel;
}// dialog

 

(defun c:test (/ restore_acadcfg_string save_acadcfg_string read_acadcfg ctrl_accept set_tiles ; local functions
	         APPDATAKEY dcl_id what_next txtval inc txthgt)

 (defun restore_acadcfg_string (KEY def / value)
  (setq value (getcfg (strcat APPDATAKEY KEY)))

  (if (or (not value) (eq value ""))
   (setcfg (strcat APPDATAKEY KEY) def)
   value
  )
 ); restore_acadcfg_string

  
 (defun save_acadcfg_string (KEY value)
  (setcfg (strcat APPDATAKEY KEY) value)
 ); save_acadcfg_string

  
 ; read from acadcfg file
 (defun read_acadcfg ()
  (setq txtval (restore_acadcfg_string "text_value"  "abc"))
  (setq inc    (restore_acadcfg_string "increment"  "1"))
  (setq txthgt (restore_acadcfg_string "text_height" "12"))
 );  read_acadcfg

  
 ; callback - ok button
 (defun ctrl_accept ()
  (foreach key '("text_value" "increment" "text_height")
   (if (/= (setq val (get_tile key)) "")
    (save_acadcfg_string key val)
   )
  ); foreach  
   
  (done_dialog 1)  ; close dialog  
 ); ctrl_accept

  
 ; set dialog tiles value
 (defun set_tiles ()
  (vl-every
   '(lambda (key val)
     (set_tile key val)
    )
   '("text_value" "increment" "text_height") (list txtval inc txthgt) 
  ); vl-evrey
 ); set_tiles
  
 
 ; here start c:test command
 (setq APPDATAKEY "AppData/John Palmer/") ; const

 (if (setq dcl_id (load_dialog "test"))
  (progn
   (read_acadcfg)
   (setq what_next 2)
   
   (while (> what_next 1)
    (if (not (new_dialog "test_dialog" dcl_id "" '(-1 -1))); open dialog at center
     (exit)
    )

    (set_tiles) ; set tiles value
     
    (action_tile "accept" "(ctrl_accept)") ; install callback
    
    (setq what_next (start_dialog)) ; here start events

    (cond
     ((= what_next 0) ; user pick cancel button
      ; do clean up
      ; no tiles values to save
      ; ...
      ; ...
     ); case
     ((= what_next 1) ; user pick OK
      ; do clean up
      ; tiles values already saved
      ; ...
      ; ...
     ); case
    ); cond
   ); while

   (unload_dialog dcl_id) ; unload test.dcl
  ); progn
 ); if
 
 (princ)
); c:test
  

 

0 Likes