Para construir cuadros de diálogo y usarlos como interfaz de una aplicación Lisp, es necesario echarle mano a un lenguaje de programación denominado DCL. DCL son las abreviaturas en inglés de Dialog Control Language, y con este lenguaje se construye de una forma sencilla cuadros de diálogo que pueden ser enlazados con el código de las rutinas Lisp para mostrar una UI.

Aunque la sintaxis de este lenguaje no es complicada, prácticamente se debe de “construir” todo el cuadro de diálogo deseado, para ello siempre priman las costumbres de cada programador, en mi caso, primero diseño la interfaz a mano como un bosquejo y luego la elaboro un poco mejor, una vez que tengo definidas las áreas principales y como se organizan todas ellas, empiezo por ahí.

Normalmente la programación de los cuadros de diálogo se construye en un archivo independiente del código Lisp, mientras que una rutina LISP está contenida en un archivo “.lsp”, un cuadro de diálogo DCL debe de estar contenido en un archivo con la extensión “.dcl”, en este caso, para una rutina lisp con un cuadro de diálogo, siempre deberán de existir 2 archivos, normalmente ubicados en la misma carpeta o en una carpeta que pueda ser leída como de soporte por AutoCAD por ejemplo.

En este post no pretendo mostrar cómo se programa en DCL, sino mostrar un pequeño artilugio para evitar generar el archivo DCL adicional y no estar cargando con este a donde quiera que vaya la rutina lisp.

Para ello vamos a “incrustar” el codigo DCL dentro del código Lisp, para ello vamos a crear una pequeña aplicación lisp con un cuadro de diálogo para interactual con el.

; Rutina Lisp de ejemplo para programar un cuadro de diálogo en el mismo código Lisp

; Programa desarrollado por Mario Torres Pejerrey
; https://www.devcadlisp.com/
; https://www.mariotorres.pe/

;-------------------------------------------------------------------------------------------------------------------------------
; CODIGO FUENTE DE LA RUTINA LISP.
;______________________________________________________

(vl-load-com)
(setq AcadObj (vlax-get-acad-object))
(setq AcadDocument (vla-get-activedocument AcadObj))
(setq mspace (vla-get-modelspace AcadDocument))

(setq VersionLsp "Ejemplo DCL V1.0")

(defun c:ejdcl ()

	(Defecto)
	(crear_CuadroDialogo)

	(setq ind (load_dialog fname))
	(if (not (new_dialog "dlg_edcl" ind))(exit))

	(valores)
	(Acciones)

	(if (= 1 (start_dialog))(EjecutarComando))
	
	(unload_dialog ind)
	(close fn)
	(vl-file-delete fname)

	(princ)
)
	
(defun Defecto ()
	(if (null TamanoDibujoDef)(setq TamanoDibujoDef 100))
)

(defun valores ()
  
	(cond
		((= TamanoDibujoDef 100)
			(Setq ValorRBTamano100 "1")
			(Setq ValorRBTamano80 "0")
		)
		((= TamanoDibujoDef 80)
			(Setq ValorRBTamano100 "0")
			(Setq ValorRBTamano80 "1")

		)
	)

	(set_tile "rbTamano100" ValorRBTamano100)
	(set_tile "rbTamano80" ValorRBTamano80)
	(set_tile "error" "")

)

(defun acciones ()
	(action_tile "rbTamano100" "(setq TamanoDibujoDef 100)")
	(action_tile "rbTamano80" "(setq TamanoDibujoDef 80)")

	(action_tile "accept" "(aceptar)")
	(action_tile "cancel" "(done_dialog)")
	(action_tile "Acerca" "(acercade)")
)

(defun aceptar ()
	(setq TamanoDibujo TamanoDibujoDef)
	(done_dialog 1)
)


(defun acercade ()
	(acet-ui-message (strcat "Programa " VersionLsp
		"\nLisp Desarrollado por Mario Torres Pejerrey"
		"\nPuedes encontrar esta aplicación y otras rutinas lisp en:"
		"\nhttps://www.devcadlisp.com/"
		) VersionLsp (+ (+ 0 64) 0)
	)
)

(defun EjecutarComando ()

	(setq PuntoInicio (getpoint "\nPunto de inicio del cuadrado: "))
	
	(if (EQ (type PuntoInicio) 'LIST)
		(progn
			
			(setq XPuntoInicio (car PuntoInicio))
			(setq YPuntoInicio (cadr PuntoInicio))
			
			(setq P1 PuntoInicio)
			(setq P2 (list (+ XPuntoInicio TamanoDibujo) YPuntoInicio 0))
			(setq P3 (list (+ XPuntoInicio TamanoDibujo) (- YPuntoInicio TamanoDibujo) 0))
			(setq P4 (list XPuntoInicio (- YPuntoInicio TamanoDibujo)0))
			
			(setq coord (append p1 p2 p3 p4 p1))
			(setq points (vlax-make-safearray vlax-vbDouble '(1 . 15)))
			(setq var_coord (vlax-make-variant (vlax-safearray-fill points coord)))
			(setq plineObj (vla-AddPolyline mspace var_coord))
			
		)
	)

)

(defun crear_CuadroDialogo ()

	(setq fname (vl-filename-mktemp "cuadroejemplo.dcl"))
	(setq fn (open fname "w"))

	(setq CodigoDcl (strcat "dlg_edcl : dialog {
		label = \"" VersionLsp "\";
		: boxed_column {
			label=\"Tamaño del cuadrado\";
	
			: row {
				: radio_button {
					key = \"rbTamano100\";
					label = \"100m\";
					value = \"1\";
				}
	        
				: radio_button {
					key = \"rbTamano80\" ;
					label = \"80m\" ;
					value = \"0\" ;
				}
			}
		} 
  
		spacer;
	
		: row {
			: button {
				fixed_width=true;
				width=11;
				key=\"accept\";
				is_default=true;
				label= \"&Aceptar\";
			}
			: button {
				fixed_width=true;
				width=11;
				is_cancel=true;
				key=\"cancel\";
				label= \"&Cancelar\";
			}
		
			: button {
				fixed_width=true;
				width=11;
				is_cancel=false;
				key=\"Acerca\";
				label= \"Acerca &de...\";
			}
		} 
	
		: text_part {
			key=\"error\";
			label=\"\";
		}
	}"))

	(write-line CodigoDcl fn)
	(close fn)

)

(princ)

Como puedes ver en esta rutina de ejemplo, la clave es crear un archivo temporal y luego abrirlo, según este ejemplo de código:

(setq fname (vl-filename-mktemp "cuadroejemplo.dcl"))
(setq fn (open fname "w"))

Posteriormente creamos una variable que almacenará una cadena de texto, si el cuadro de diálogo es muy grande y tiene mucho texto, se debe de divir la cadena en varias variables y al final concatenarlas, este es el ejemplo:

(setq CodigoDcl (strcat "dlg_edcl : dialog {
		label = \"" VersionLsp "\";
		: boxed_column {
			label=\"Tamaño del cuadrado\";
	...mas codigo...
		: text_part {
			key=\"error\";
			label=\"\";
		}
	}")
)

Como podrás observar la variable “CodigoDcl”, contiene una cadena de texto, pero no es una cadena de texto cualquiera, combina texto y variables, en este caso una sola variable denominada “VersionLsp”, es por ello que usamos tambien la función “strcat”.

Además tambien podrás ver que se usa el caracter “\”, este caracter se usa como caracter de escape para color un valor de texto dentro de una cadena de texto, por ejemplo:

En lenguaje DCL con un archivo independiente esto se escribiría asi:

: text_part {
	key="error";
	label="";
}

Pero como estamos creando un código DCL dentro de una variable, los textos o cadenas del DCL deben de ir con el caracter de escape “\”.

: text_part {
	key=\"error\";
	label=\"\";
}

Finalmente, una vez que se ha creado la variable que contiene el código DCL, escribimos en el archivo abierto este valor y lo cerramos.

(write-line CodigoDcl fn)
(close fn)

Eso es todo, es por ello que la función principal del comando, se hace referencia al archivo temporal y a la definición del cuadro de diálogo creado, al final de todo, lo eliminamos:

(defun c:ejdcl ()

	(Defecto)
	(crear_CuadroDialogo)

	(setq ind (load_dialog fname))
	(if (not (new_dialog "dlg_edcl" ind))(exit))

	(valores)
	(Acciones)

	(if (= 1 (start_dialog))(EjecutarComando))
	
	(unload_dialog ind)
	(close fn)
	(vl-file-delete fname)

	(princ)
)

Descargar app: