martes, 8 de junio de 2010
UN ULTIMO AJUSTE Y NUESTRO PROGRAMA FUNCIONANADO
Un último ajuste
Se nos ocurre que sería interesante que cuando el usuario se posicione en una de las líneas del ListBox pudiese visualizar el contenido completo de la línea, ya que pueden aparecer cortadas si son muy largas. Lo vamos a hacer de la siguiente forma: cuando el usuario hace doble click en una entrada, el contenido de la línea lo mostraremos en un cuadro de diálogo:
PUBLIC SUB ListBox1_DblClick()
IF ListBox1.Index >= 0 THEN
message.Info(ListBox1.Current.Text)
END IF
END
Nuestro programa funcionando
Acción "Abrir",Y Acción "Guardar"
Acción "Abrir"
¿Qué se supone que debe hacer?. Pues preguntarle al usuario por un archivo, leerlo y cargar el contenido en el ListBox. Veamos directamente la acción correspondiente:
PUBLIC SUB Abrir_Click()
DIM c AS String
DIM arr_cadenas AS String[]
Dialog.Title = "Seleccione un archivo"
Dialog.Filter = [ "Datos de agenda (*.data)", "Todos los ficheros (*.*)" ]
IF NOT Dialog.OpenFile() THEN
arr_cadenas = Split(File.LOAD(Dialog.Path), "\n")
ListBox1.Clear()
FOR EACH c IN arr_cadenas
ListBox1.Add(c)
NEXT
END IF
END
Este trozo de código se nos presenta una característica muy interesante de Gambas, las clases "no instanciables" o estáticas ( 3). Son clases que no pueden instanciarse pero pueden utilizarse directamente. En esta acción vemos en acción dos de estas clases: la clase " File" y "Dialog".
Por ejemplo, la clase Dialog proporciona acceso a los típicos cuadros de diálogo de selección de ficheros, colores, etc. Está documentada en gb.qt
En nuestra aplicación, queremos seleccionar un fichero y cargarlo. Para hacer ésto, utilizaremos la clase Dialog de la siguiente forma:
Dialog.Title = "Seleccione un archivo"
Dialog.Filter = [ "Datos de agenda (*.data)", "Todos los ficheros (*.*)" ]
IF NOT Dialog.OpenFile() THEN
' etc ...
Ajustamos el título del cuadro de diálogo, proporcionamos un filtro para la selección del tipo de fichero por extensión y finalmente invocamos el método OpenFile() de la clase. Curiosamente, si NO se selecciona un fichero (el usuario pulsa "Cancelar", etc ...), el valor de retorno del método OpenFile() es True. Una vez seleccionado el fichero por parte del usuario, podemos acceder a la ruta completa con la propiedad Dialog.Path
La clase File (su documentación se encuentra "colgando" de la entrada gb) proporciona varios métodos para trabajar con ficheros.
En la documentación de Gambas, en la sección "How do I ..." se muestran varios ejemplos para leer y escribir ficheros. Nosotros vamos a utilizar en nuestra aplicación el método Load(), que recibe como argumento la ruta de un fichero y devuelve un String con todo el contenido del fichero. Para separar las líneas que contiene el fichero, utilizamos la función Split(), que toma como argumentos la cadena que queremos "partir", el carácter a utilizar como separador (un salto de línea en nuestro caso, " \n") y devuelve un Array de Strings. Por ello hemos declarado la variable arr_cadenas como String[]:
DIM arr_cadenas AS String[]
Una vez que tenemos la lista de cadenas contenidas en el fichero, limpiamos el ListBox y vamos añadiendo una a una cada cadena utilizando el método Add() del ListBox.
Acción "Guardar"
Al pulsar el botón "Guardar" o la entrada equivalente en el menú, el programa debe volcar los contenidos a un fichero de texto. Mostraremos un cuadro de diálogo al usuario para que nos proporcione el nombre del fichero a utilizar. Este es el código correspondiente:
PUBLIC SUB Guardar_Click()
lineas AS String
destino AS String
numArchivo AS Integer
lineas = ListBox1.Contents
Dialog.Title = "Seleccione un archivo"
Dialog.Filter = [ "Datos de agenda (*.data)" ]
IF NOT Dialog.SaveFile() THEN
IF Right$(Dialog.Path, 5) <> ".data" THEN
destino = Dialog.Path & ".data"
ELSE
destino = Dialog.Path
END IF
File.Save(destino, lineas)
END IF
END
ACCIÓN DE BORRAR Y SALIR
Acción "Borrar"
Como en el caso anterior, el ListBox debe tener alguna línea, y el usuario debe haber seleccionado una al menos. El código es similar al del botón "Modificar":
PUBLIC SUB Borrar_Click()
i AS Integer
i = ListBox1.Index
IF i >= 0 THEN
ListBox1.Remove(i) ' El método Remove quita una línea, justo
' lo que queremos
ELSE IF ListBox1.Count > 0 AND i = -1 THEN
' Comprobamos que el ListBox no esté vacío y que
' haya algo seleccionado.
message.Info("Debe seleccionar la línea que desea borrar.")
END IF
END
Podemos observar que la implementación de estas cuatro acciones es común para los botones y las entradas equivalentes en el menú.
Ahora pasamos a implementar las acciones relativas al manejo de ficheros (Abrir, Guardar) y salir de la aplicación. Empezaremos por lo fácil:
Acción "Salir"
La función de este botón (y la correspondiente entrada en el menú) es cerrar la aplicación. Nada más sencillo:
PUBLIC SUB Salir_Click()
ME.Close(0) ' ME es una referencia al propio formulario
FInputBox
END
Se podría hacer un poco más amigable esta acción agregando un diálogo del tipo " ¿Está Ud. seguro de que quiere salir de la aplicación?" y actuar en consecuencia. Dejamos esta mejora como ejercicio para el lector.
ACCIÓN DE MODIFICAR
Al pulsar este botón, el usuario modificará alguna de las entradas que haya en el ListBox. Si no hay ninguna, el botón no debe hacer nada, y si no han seleccionado ninguna línea, mostrará un mensaje de aviso. Veamos la implementación del procedimiento asociado.
' Acción "Modificar"
PUBLIC SUB Modificar_Click()
f AS FInputBox
IF ListBox1.Count > 0 THEN ' Si no hay nada en el formulario,
' su propiedad Count es 0. En este caso,
' no hacemos nada.
IF ListBox1.Index = -1 THEN
' La propiedad Index nos devuelve el índice de la línea seleccionada.
' Si no hay seleccionada ninguna, devuelve -1. En este caso, avisamos
' al usuario y no hacemos más.
message.Info("Debe seleccionar la línea que desea modificar.")
ELSE
' El usuario ha seleccionado una línea en el ListBox.
' Mostramos nuestro InputBox, pasándole también el texto seleccionado.
' El texto seleccionado es la propiedad Text del objeto ListBoxItem
' seleccionado, al que se accede a su vez con la propiedad Selected
' del ListBox
f = NEW FInputBox("Modificar entrada",
"Modifique la línea seleccionada:",
ListBox1.Current.Text)
f.ShowModal()
' El cuadro de diálogo FInputBox modifica la variable compartida
' en el módulo MComun.
' Si no está vacía, la asignamos al ListBoxItem seleccionado.
IF MComun.texto THEN ListBox1.Current.Text = MComun.texto
' Como antes, "vaciamos" la variable compartida después de usarla.
MComun.texto = ""
END IF
END IF
END
ACCION DE AÑADIR
Acción "Añadir"
Esto ya es un poco más complicado. Queremos que el usuario, al pulsar el botón, pueda escribir una línea de texto que se cargue en el ListBox.
Gambas no proporciona por defecto un diálogo del tipo "InputBox", así que lo vamos a crear nosotros mismos. Creamos un nuevo formulario, pero ahora sí que queremos disponer de un constructor. ¿Por qué?. Porque en el momento de crearlo cambiaremos algunas propiedades como el título, algún mensaje de texto y un valor por defecto para la entrada de texto. Este es el diseño que proponemos:
El formulario no tiene mucha complicación. Dispone de una etiqueta o Label, una entrada de texto ( TextBox) y dos botones. Como buen cuadro de diálogo que se precie, es conveniente que se pueda cancelar con la tecla Escape y aceptar con la tecla Enter:
Los controles Button tienen dos propiedades adecuadas para este cometido. Son " Default" y " Cancel". Para el botón "Aceptar", ponemos " Default" a True y " Cancel" a False. Para el botón "Cancelar", al contrario.
De esta manera, cuando se abra el formulario, una pulsación en la tecla < ENTER> será equivalente a pulsar el botón "Aceptar" y una pulsación en la tecla < ESC> simulará el botón "Cancelar".
El siguiente problema que se nos plantea es cómo retornar el valor que introduce el usuario en la entrada de texto a la ventana principal. Hay que destacar que en Gambas no hay variables globales, así que tendremos que buscar otra solución. En el "Consejo del día nº 7", (menú "? > Consejos del día") se nos sugiere que creemos un módulo en el cual ponemos una variable PUBLIC, así puede ser accedida desde cualquier punto de la aplicación.
Creamos un módulo (botón derecho en "Módulos > Nuevo módulo") y lo llamamos MComun, por ejemplo. Esta sería la implementación del módulo:
' Gambas module file
PUBLIC texto AS String
Así, sin más. Ahora tenemos una variable visible desde cualquier punto del programa que puede ser accedida con la notación MComun.texto
Lo siguiente es implementar el formulario que hará las veces de "InputBox". Esta sería su implementación:
' Gambas class file
PUBLIC SUB _new(titulo AS String, mensaje AS String, OPTIONAL texto
AS String)
ME.Caption = titulo
Label1.Caption = mensaje
' un String se evalúa como False si está "vacío"
IF texto THEN TextBox1.Text = texto
END
PUBLIC SUB Button1_Click() ' Este es el botón Aceptar
MComun.texto = TextBox1.Text
ME.Close(0)
END
PUBLIC SUB Button2_Click() ' Este es el botón Cancelar
ME.Close(0)
END
El procedimiento _new es el constructor. Como nos interesa que el texto de la etiqueta, el título y el texto a editar sean distintos cada vez, los ajustamos al crear la ventana.
El botón "Aceptar" asigna el texto en el TextBox en la variable texto del módulo MComun y cierra el formulario. El botón "Cancelar" simplemente cierra la ventana.
Como la variable MComun.texto es común, tenemos que acordarnos de "limpiarla" cada vez que la utilicemos. Vamos a verlo ahora mismo.
El procedimiento para el botón "Añadir" del formulario principal es el siguiente. Es bastante autoexplicativo:
PUBLIC SUB Annadir_Click()
' Declaramos nuestro "Inputbox"
f AS FInputBox
' Creamos el InputBox, pasándole el título, mensaje a mostrar
' y un valor por defecto: la fecha y hora del momento y una flechita
f = NEW FInputBox("Escribir entrada",
"Escriba la línea que desea añadir:",
CStr(Now) & " -> ")
' Lo mostramos
f.ShowModal()
' Si han pulsado aceptar y han metido texto,
' estará en la variable MComun.texto
IF MComun.texto THEN 'Una cadena vacía es False
' El control ListBox tiene un método para añadir texto: .Add
ListBox1.Add(MComun.texto)
' "Vaciamos" la variable común
MComun.texto = ""
END IF
END
EMPEZAR A PROGRAMAR Y ACCIÓN DE LIMPIAR
Ya tenemos nuestro formulario diseñado. Ahora se trata de implementar funcionalidad a los controles.
Lo primero que vamos a hacer es que los botones "Añadir", "Modificar", "Borrar" y "Limpiar" (y las entradas correspondientes en los menús) funcionen.
Acción "Limpiar"
Este botón se encarga de borrar todas las entradas que haya en el ListBox. Para saber cómo hacer ésto, buscamos en el navegador de ayuda la documentación relativa al control ListBox:
La documentación se encuentra bajo el "árbol" gb.qt, que es donde se encuentra la documentación de todos los controles del tipo "visual" (botones, etiquetas, menús, etc...). Vemos que el ListBox proporciona un método " Clear", que precisamente hace lo que queremos: borrar todo el contenido del control.
Haciendo click en el botón "Limpiar", se abre el editor de código en el procedimiento correspondiente. Añadimos el siguiente código:
PUBLIC SUB Limpiar_Click()
ListBox1.Clear()
END
Fácil, ¿verdad?.