bad argument type: stringp nil when getting values from dcl

bad argument type: stringp nil when getting values from dcl

tpottebaum
Contributor Contributor
1,243 Views
17 Replies
Message 1 of 18

bad argument type: stringp nil when getting values from dcl

tpottebaum
Contributor
Contributor

Hello,

 

I am trying to use a DCL to obtain values in my lisp file but I keep getting the stringp nil error. I've narrowed down the error to the following block of code. 

 

     (princ "\nStarting dialog...")
      (if (= (start_dialog) 1)
        (progn
          ;; Retrieve user input
          (princ "\nDialog completed successfully.")
          (setq numCols (get_tile "numCols"))
          (setq numRows (get_tile "numRows"))
          (setq colSpacing (get_tile "colSpacing"))
          (setq rowSpacing (get_tile "rowSpacing"))
          (setq topEdgeSpacing (get_tile "topEdgeSpacing"))
          (setq bottomEdgeSpacing (get_tile "bottomEdgeSpacing"))
          

 

 

I get "Dialog completed successfully" to return, but right after this I get the stringp: nil error. Below is my DCL code. Can you help me figure out what I'm doing wrong?

 

 

array_dialog : dialog {
    label = "Array Configuration";
    : column {
        : edit_box { label = "Number of Columns"; key = "numCols"; }
        : edit_box { label = "Number of Rows"; key = "numRows"; }
        : edit_box { label = "Horizontal Spacing (in inches)"; key = "colSpacing"; }
        : edit_box { label = "Vertical Spacing (in inches)"; key = "rowSpacing"; }
        : edit_box { label = "Top Edge Spacing (in inches)"; key = "topEdgeSpacing"; }
        : edit_box { label = "Bottom Edge Spacing (in inches)"; key = "bottomEdgeSpacing"; }
        : row {
            : button { label = "OK"; is_default = true; key = "accept"; }
            : button { label = "Cancel"; is_cancel = true; key = "cancel"; }
        }
    }
}

 

 

0 Likes
Accepted solutions (2)
1,244 Views
17 Replies
Replies (17)
Message 2 of 18

paullimapa
Mentor
Mentor

I assume you are filling out each of the edit boxes before clicking OK right?  I don’t see anything from the code you posted that may cause that error. What if you comment out each of the setq one at a time and see which one would cause that to come up? Also keep in mind that the values you retrieve are all strings. So to use them as numbers you’ll have to use one of the conversion functions:

atof = converts strings to real numbers

atoi = converts strings to integers 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 3 of 18

Moshe-A
Mentor
Mentor

@tpottebaum hi,

 

after the dialog is close... 

(if (= (start_dialog) 1)

 

you can not call (get_tile) function it is only relevant when dialog is on and active

so i guess this is your error

 

move all the retrieving tiles to call back function as...

 

(action_tile "accept" "(ctrl_accept)")

(if (= (start_dialog) 1)

 (progn

  (princ "\nDialog completed successfully.")

  ...

  ...

 ); progn

); if

 

; call-back function

(defun ctrl_accept ()

  ;; Retrieve user input

  (setq numCols (get_tile "numCols"))
  (setq numRows (get_tile "numRows"))
  (setq colSpacing (get_tile "colSpacing"))
  (setq rowSpacing (get_tile "rowSpacing"))
  (setq topEdgeSpacing (get_tile "topEdgeSpacing"))
  (setq bottomEdgeSpacing (get_tile "bottomEdgeSpacing"))

 

  (done_dialog 1)

)

 

 

Message 4 of 18

tpottebaum
Contributor
Contributor

I tried doing this, but I'm getting nil for all my variables after using get_tile. For more context, I do have default values set for each variable so they are not empty/nil prior to calling get_tile. I'm very new to DCL's, so I'm sure I'm missing something fairly obvious. I understand that the input values will come back as strings and will need to be converted, but I should still be able to get the new input values and return them as strings?

0 Likes
Message 5 of 18

paullimapa
Mentor
Mentor

Could you share your entire dcl and lsp files here?


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 6 of 18

tpottebaum
Contributor
Contributor

Here they are. The goal of this code is to return an array of lines with the dimensions inputted from the user. I haven't worked on the array itself much... just focusing on getting the DCL to function properly. The code seems to run properly until the get_tile functions.

0 Likes
Message 7 of 18

Moshe-A
Mentor
Mentor

@tpottebaum  hi,

 

i guess the error in coming from here

the variables assigned from the text_boxes are already string (no need converting them to string)

 

Moshe

 

 

  ;; Debugging output
  (princ (strcat "\nStarting Point: " (rtos x1 2 2) ", " (rtos y1 2 2)))
  (princ (strcat "\nnumCols: " (rtos numCols) ", numRows: " (rtos numRows)))
  (princ (strcat "\ncolSpacing: " (rtos colSpacing 2 2) ", rowSpacing: " (rtos rowSpacing 2 2)))
  (princ (strcat "\ntopEdgeSpacing: " (rtos topEdgeSpacing 2 2) ", bottomEdgeSpacing: " (rtos bottomEdgeSpacing 2 2)))

 

 

0 Likes
Message 8 of 18

tpottebaum
Contributor
Contributor

This is an updated version I'm working with. I'm not calling out the callback function to get the new inputted values here. Whenever I try to use it, I get nil values in place of the default. Just not sure what I'm doing wrong yet...

0 Likes
Message 9 of 18

paullimapa
Mentor
Mentor
Accepted solution

Here's the issue. You have to place the ctrl_accept function near the top other wise it's not defined

  ;; Initialize the dialog
  (if (not (new_dialog "array_dialog" dcl_id))
    (alert "Dialog initialization failed.")
    (progn
; place ctrl_accept function before the rest of the dcl code
      (defun ctrl_accept ()
        (princ "\nGetting new values...")
        (setq numCols (get_tile "numCols"))
        (setq numRows (get_tile "numRows"))
        (setq colSpacing (get_tile "colSpacing"))
        (setq rowSpacing (get_tile "rowSpacing"))
        (setq topEdgeSpacing (get_tile "topEdgeSpacing"))
        (setq bottomEdgeSpacing (get_tile "bottomEdgeSpacing"))

        ;; Debugging outputs
        (princ (strcat "\nRetrieved numCols: " (if numCols numCols "nil")))
        (princ (strcat "\nRetrieved numRows: " (if numRows numRows "nil")))
        (princ (strcat "\nRetrieved colSpacing: " (if colSpacing colSpacing "nil")))
        (princ (strcat "\nRetrieved rowSpacing: " (if rowSpacing rowSpacing "nil")))
        (princ (strcat "\nRetrieved topEdgeSpacing: " (if topEdgeSpacing topEdgeSpacing "nil")))
        (princ (strcat "\nRetrieved bottomEdgeSpacing: " (if bottomEdgeSpacing bottomEdgeSpacing "nil")))

        (done_dialog 1)
     )

Then when you call that in your action_tile it'll find that function:

      ;; Define actions for dialog buttons
;      (action_tile "accept" "(done_dialog 1)")
      (action_tile "accept" "(ctrl_accept)")      
      (action_tile "cancel" "(done_dialog 0)")

Also since you've already defined these at the beginning of the file:

(setq numCols "23" numRows "8" colSpacing "16" rowSpacing "16" topEdgeSpacing "9" bottomEdgeSpacing "9")

Then all you have to do is set_tile like this:

      ;; Set default values for the fields
      (set_tile "numCols" numCols)
      (set_tile "numRows" numRows)
      (set_tile "colSpacing" colSpacing)
      (set_tile "rowSpacing" rowSpacing)
      (set_tile "topEdgeSpacing" topEdgeSpacing)
      (set_tile "bottomEdgeSpacing" bottomEdgeSpacing)
;      (set_tile "numCols" "23")
;      (set_tile "numRows" "8")
;      (set_tile "colSpacing" "16")
;      (set_tile "rowSpacing" "16")
;      (set_tile "topEdgeSpacing" "9")
;      (set_tile "bottomEdgeSpacing" "9")

And I like to debug output with alerts:

          ;; Debugging outputs
(alert (strcat "numCols= " numCols " numRows=" numRows " colSpacing=" colSpacing " rowSpacing=" rowSpacing " topEdgeSpacing=" topEdgeSpacing " bottomEdgeSpacing=" bottomEdgeSpacing))          
;          (princ (strcat "\nRetrieved numCols: " (if numCols numCols "nil")))
;          (princ (strcat "\nRetrieved numRows: " (if numRows numRows "nil")))
;          (princ (strcat "\nRetrieved colSpacing: " (if colSpacing colSpacing "nil")))
;          (princ (strcat "\nRetrieved rowSpacing: " (if rowSpacing rowSpacing "nil")))
;          (princ (strcat "\nRetrieved topEdgeSpacing: " (if topEdgeSpacing topEdgeSpacing "nil")))
;          (princ (strcat "\nRetrieved bottomEdgeSpacing: " (if bottomEdgeSpacing bottomEdgeSpacing "nil")))

 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 10 of 18

tpottebaum
Contributor
Contributor

Thank you! That fixed my issue. I appreciate your help and advice!

0 Likes
Message 11 of 18

paullimapa
Mentor
Mentor

you are welcome...happy programming & cheers!!!


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 12 of 18

ec-cad
Collaborator
Collaborator

Changed a couple of things. Sets those variables now. I had to add a path to the .dcl file,

you may want to undo that in the Lisp..

Also modified the .dcl (probably not necessary), so use the old one..

 

ECCAD

 

0 Likes
Message 13 of 18

ec-cad
Collaborator
Collaborator

Looks like I was late again. Paulli has it covered.

ECCAD

0 Likes
Message 14 of 18

Sea-Haven
Mentor
Mentor

Another fix this is a library routine can be used in any code returns a single list of the edit boxes as strings so can get at each value. Using a (nth x ans), it will work with blank entries, also can save each previous size so auto fills with last used values.

 

SeaHaven_0-1735261834986.png

 

 

 

 

; change the 15 14 for box size
(if (not AH:getvalsm)(load "Multi Getvals.lsp"))
(setq ans (AH:getvalsm (list "Enter values " "Number of Columns " 15 14 "6" "Number of Rows" 15 14 "10" "Horizontal Spacing (in inches)" 15 14 "6" "Vertical Spacing (in inches)" 15 14 "6" "Top Edge Spacing (in inches)" 15 14 "5" "Bottom Edge Spacing (in inches)" 15 14 "5")))

 

 

 

("6" "10" "6" "6" "5" "5")

0 Likes
Message 15 of 18

Moshe-A
Mentor
Mentor

@tpottebaum hi,

 

i see you already got good solutions but i would like upgrade this a little bit ‌‌😀

so here is my ARRAY-TEST command which includes (handle-dialog-box) function.

 

I assume you know that AutoLISP is based on Classic LISP and this means a programing language who works with lists and has many tools (functions) that manipulates lists. the thread you presented here is very much candidate to be manipulate by lists at least for the retrieving\setting of 6 edit_box values in from a dialog box.

 

First the whole code is encapsulated under the definition of (see line #3) c:array-test function inside all the declaration of local functions and variables, this protects them from interfering with other outside our command.

 

The program start at line #198 and on line #205 you see the call for (handle-dialog-box) which is what we interest on. at line #114 the function starts. a call to find array-test.dcl file is made and load the dialog in  memory (see lines #116-117)

 

On line #120 a call to (init_setup) function where main data definition is made (see lines #11-38)

a variable keys-lst is set with all the keys name from the dialog box as a list (line #13)

on lines #16-25 a variable variables-lst is load with all the variables (exactly same as you declare them) but is hold in a list. to create this, i use (mapcar) & (lambda) functions which are key functions to build lists. what (mapcar) does is apply a function [on this case (lambda)] on a list of data items and actually the list holds the variables as quoted variables (i explain this next)

on line #26 a variable values-lst hold the initial values of the edit boxes.

on lines#28-38 a loop to set all 6 variables with the initial values from values-lst.

 

you declared all your variables as string, this is obvious as the data retrieved from dialog boxes all strings but eventually you need to convert them to be integers \ reals. In variables-lst i holds them as they should be integers & reals ready for the array. 

 

about quoted variables:

a variable that declares as quote, can be set with (set) function, this known in lisp as indirect assignment. see line #31 and line #47.

 

bottom line after (init_setup) is done, variables-lst will be set with values-lst. the same as you did but much more lisp style ‌‌

 

(vl-every) by the way is an AutoLISP function that applies a function [in this case (lambda)] on lists very similar to (mapcar). the different between them is (vl-every) return T if the applied function return T for all the items in the lists whereas (mapcar) return a list created from all returns values from each iteration of (lambda).

 

back to our code, line #124 you see a big (while) this control the appearance of the dialog box. a variable what_next initially set to 2 and depending on your dialog it could be any higher value. as long as what_next is higher then 1 the dialog will be reopen untill the user pick OK (what_next = 1) or Cancel (what_next = 0)

 

inside the (while) line #125 the dialog is open at center of screen

on line #131 a call to (set_tiles_values) to show the current variables-lst values.

 

on lines #41-55 implements (set_tiles_values)

cause our data is lists i again use (vl-every) to set the tiles. the huge advantage of (vl-every)  [also (mapcar)] is it accepts lists as arguments (for the calling function lambda) as you can see on lines #52-53 

 

on line #133-145 (vl-every) used again to defines the (action_tile) for the edit boxes. a functions (check_int_value) & (check_real_value) is called to validate the values, if it's under min & max values you can specify on the (action_tile) calls.

 

lines #149-150 covers the "accept" "cancel" buttons - this i assumes you already knows.

review (ctrl_accept) function to learn how the retrieve of the tiles is done.

 

on line #152 the call:

(setq what_next (start_dialog)) ; start dialog

 

after the dialog box is close, process continue on line #154

 

enjoy

Moshe

 

(vl-load-com) ; load activex support

(defun c:array-test (/ handle-dialog-box  ; local function
		       p1 x1 y1 currentX  ;| local variables |;)

 (defun handle-dialog-box (/ init_setup set_tiles_values check_int_value check_real_value ctrl_accept 	; local functions
			     keys-lst variables-lst values-lst dclfname dcl_id what_next 	     	;  local variables
			     numCols numRows colSpacing rowSpacing topEdgeSpacing bottomEdgeSpaceing 	;| local variables |;)

  ; prepare variables before dialog open
  (defun init_setup ()
   ; declare dialog keys
   (setq keys-lst '("numCols" "numRows" "colSpacing" "rowSpacing" "topEdgeSpacing" "bottomEdgeSpacing"))
   
   ; declar dialog variables base on keys-lst
   (setq variables-lst (mapcar
		        (function
			  (lambda (key)
			    (eval (quote (read key))) ; convert string to symbol\variable
			  )
			); function
			keys-lst
		      ); mapcar
   ); setq

   (setq values-lst '(23 8 16.0 16.0 9.0 9.0)) ; initial values
    
   (vl-every
     (function
       (lambda (var val)
         (set var val) ; indirect assignment
       )
     ); function
     ; dialog variables
     variables-lst
     values-lst
   ); vl-evrey
  ); init_setup

   
  (defun set_tiles_values ()
    ;; Set default values for the fields
    (vl-every
      (function
	(lambda (key val)
	 (if (eq (type val) 'INT)
          (set_tile key (itoa val))
	  (set_tile key (rtos val 2))
	 ); if
	); lambda
      ); function
      keys-lst
      values-lst
    ); vl-every
  ); set_tiles_value


  (defun check_int_value (key val low high)
   (if (not
	 (and
	   (numberp (atoi val))
	   (>= (atoi val) low) (<= (atoi val) high)
         )
       )
    (progn
     (vlr-beep-reaction)
     (set_tile "error" "Invalid integer value.")
     (mode_tile key 3)
    ); progn
    (progn
     (set_tile key (rtos (atoi val) 2 0))
     (set_tile "error" "")
    ); progn
   ); if
  ); check_numeric_value

   
  (defun check_real_value (key val low high)
   (if (not
	 (and
	   (numberp (atof val))
	   (>= (atof val) low) (<= (atof val) high)
         )
       )
    (progn
     (vlr-beep-reaction)
     (set_tile "error" "Invalid real value.")
     (mode_tile key 3)
    ); progn
    (progn
     (set_tile key (rtos (atof val) 2))
     (set_tile "error" "")
    ); progn
   ); if
  ); check_real_value

   
  (defun ctrl_accept (/ i)
   (setq i -1)
   (vl-every
     (function
       (lambda (var key)
	(set var (apply (if (<= (setq i (1+ i)) 1) 'atoi 'atof) (list (get_tile key))))
       ); lambda
     ); function
     variables-lst
     keys-lst
   ); vl-every

   (done_dialog 1)
  ); ctrl_accept
 
   
  ; here start handle-dialog-box
  (if (and
       (setq dclfname (findfile "array-test.dcl")) ; get dcl file from supprt files search path
       (setq dcl_id (load_dialog dclfname))	   ; get dcl id
      )
   (progn
    (init_setup) ; prepare variables before dialog open
    
    (setq what_next 2)
       
    (While (> what_next 1)
     (if (not (new_dialog "array_test" dcl_id "" '(-1 -1)))	; open dialog
      (exit)
     )

      
     ; set tiles here, list, popup_list edit_box etc...
     (set_tiles_values)

     ;; Define actions for dialog buttons
     (vl-every
       (function
	 (lambda (var key)
          (if (eq (type (vl-symbol-value var)) 'INT)
	    (action_tile key "(check_int_value $key $value 1 99)")
	    (action_tile key "(check_real_value $key $value 0.1 99.9)")
	  )
	 ); lambda
       ); function
       variables-lst
       keys-lst
     ); vl-every

      
     ;; Define actions for dialog buttons
     (action_tile "accept" "(ctrl_accept)")
     (action_tile "cancel" "(done_dialog 0)")
     
     (setq what_next (start_dialog)) ; start dialog

     (cond
      ((= what_next 1) ; user pick OK
       
       ; do your job after dialog is close
       ; ....
       ; ....

       ; print dialog edit-box values
       (terpri)
       (princ (mapcar
	 	(function
	   	  (lambda (key var)
            	    (cons key (vl-symbol-value var)) 
 	   	  )
	        ); function
	        keys-lst
	        variables-lst
              )
       ); princ
       
      ); case
      ((= what_next 0) ; user pick cancel
       
       ; do cleanup
       ; .....
       ; .....
       ; .....

      ); case
      ((= what next 2) ; handle other code here
       ; .....
       ; .....
       ; reopen dialog?
       
      ); case
     ); cond
    ); while

    (unload_dialog dcl_id)
   ); progn
  ); if
 ); handle-dialog-box


 ; here start c:array-test
 (if (setq p1 (getpoint "\nSpecify starting point for the array: "))
  (progn
   (setq x1 (car p1) y1 (cadr p1))
   (setq currentX x1 currentY y1)

   ;; Call the dialog box to get array configuration
   (handle-dialog-box)

   ; do your array command here
   ; .....
   ; .....
   ; .....

  ); progn
 ); if

 (princ)
); c:array-test

 

 

array_test : dialog {
 label = "Array Configuration";

 : boxed_column {
   :edit_box {
     label = "Number of Columns:\t\t\t\t\t";
     key = "numCols";
     edit_width = 7;
   }
   :edit_box {
     label = "Number of Rows:\t\t\t\t\t\t\t\t\t\t\t";
     key = "numRows";
     edit_width = 7;
   }
   :edit_box {
     label = "Horizontal Spacing:\t\t\t\t\t\t";
     key = "colSpacing";
     edit_width = 7;
   }
   :edit_box {
     label = "Vertical Spacing:\t\t\t\t\t\t\t\t\t\t\t";
     key = "rowSpacing";
     edit_width = 7;
   }
   :edit_box {
     label = "Top Edge Spacing:\t\t\t\t\t\t";
     key = "topEdgeSpacing";
     edit_width = 7;
   }
   :edit_box {
     label = "Bottom Edge Spacing:";
     key = "bottomEdgeSpacing";
     edit_width = 7;
   }
 }// column

 spacer_1;
 ok_cancel_err;
}
0 Likes
Message 16 of 18

tpottebaum
Contributor
Contributor

Thank you, Moshe! I've been running through this and I think I'm understanding it more, but when trying to implement a simple array command, it's not taking numRows, numCols, etc... It says they are nil. The variables should be in the correct form - int and real, correct? Maybe I'm not understanding fully quite yet...

0 Likes
Message 17 of 18

Moshe-A
Mentor
Mentor
Accepted solution

@tpottebaum  hi,

 

You absolute right, thought about it after 🤣

it happen cause the variables-lst was declared as local variable in (handle-dialog-box) function which also declared as local function in c:array-text command. i moved up them to be local in c:array-test and even moved the output of the retrieving edit_box after the call to (handle-dialog-box) function and it works.

 

and a word about the scope of variables and function (very important 😀)

a variable\function that NOT declared as local is known (can be accessed) from any other function that stay in memory also from new loaded lisp functions.

so to protect them from been change or override, better declare them as local.

 

Moshe

 

 

 

 

 

0 Likes
Message 18 of 18

tpottebaum
Contributor
Contributor

Ahh, I see. I made the change and it's working for me now too. Definitely have some learning to do here and your code helps a ton. Thank you!

0 Likes