Los ficheros y los directorios (carpetas) son esenciales en cualquier
programa de ordenador y Gtk contiene diversos componentes para facilitar
su manejo. La selección de ficheros y directorios en Gtk2Hs se
implementa a través del interfaz FileChooser
. Basicamente hay cuatro
modos, como se indica en el tipo FileChooserAction
. Sus constructores son:
FileChooserActionOpen
usado para que el usuario abra un ficheroFileChooserActionSave
usado para que el usuario guarde un ficheroFileChooserActionSelectFolder
usado para que el usuario seleccione un directorioFileChooserActionCreateFolder
usado para que el usuario cree un directorioEl interfaz
FileChooser
tiene atributos, métodos y señales, pero no es propiamente un widget. Hay tres
widgets que usan el interfaz de modo diferente,
FileChooserWidget
,
FileChooserButton
y
FileChooserDialog
. El widget a usar está restingido por la
FileChooserActionType
permitida. Como verás en los ejemplos siguientes,
el widget para guardar un fichero o para seleccionar un directorio puede contener
también un botón que permita al usuario crear un directorio. Además, el constructor
FileActionCreateFolder probablemente nuncá será usado en ninguno de tus programas.
Es importante indicar que, a pesar de que los widgets no guardan ni abren ficheros por sí mismos, le creación de los directorios (por el usuario) se implementa a través de widgets.
Nuestro primer ejemplo usará FileChooserWidget
, que puede emplearse en el modo
Abrir y Salvar.
fileChooserWidgetNew :: FileChooserAction -> IO FileChooserWidget
Aquí usamos FileChooserActionOpen
, y cuando el usuario elige definitivamente un fichero, ya sea
haciendo doble clic en él o pulsando la tecla Enter, la señal onFileActived
se emite. Usamos:
fileChooserGetFilename :: FileChooserClass self => self -> IO (Maybe FilePath)
Desde la ubicación del fichero, el programa puede abrir el fichero, o posiblemente hacer otra cosa.
El formato del filepath puede depender de la plataforma y está determinado por la variable de entorno
G_FILENAME_ENCODING. Hay también funciones en FileChooser
para formatos URI (Uniform Resource Identifier),
pero no las vamos a ver aquí.
Puedes permitir al usuario seleccionar múltiples ficheros con:
fileChooserSetselectMultiple :: FileChooserClass self => self -> Bool -> IO ()
y, con el FileChooserWidget
, puedes añadir fácilmente un botón check
para dejar al usuario determinarlo. La colocación de un widget de este tipo se hace de modo estándar
con:
fileChooserSetExtraWidget :: (FileChooserClass self, WidgetClass extraWidget) => self -> extraWidget -> IO ()
Otra utilidad es el uso de filtros para mostrar sólo ficheros de un tipo, ya sea especificando un tipo MIME, un pattern (plantilla) o un formato a medida. Los filtros de ficheros se documentan en Graphics.UI.Gtk.Selectors.FileFilter.
El siguiente trozo de código, parte del ejemplo siguiente, muestra los filtros. La última línea simplemente añade el filtro al widget selector de fichero y, como ocurre con el widget extra, el posicionamiento visual se hace automáticamente.
hsfilt <- fileFilterNew fileFilterAddPattern hsfilt "*.hs" fileFilterSetName hsfilt "Haskell Source" fileChooserAddFilter fch hsfilt
Puedes también añadir un widget "preview" (previsualización) con:
fileChooserSetPreviewWidget :: (FileChooserClass self, WidgetClass previewWidget) => self -> previewWidget -> IO ()
En el ejemplo se usa para previsualizar ficheros gráficos. El ejemplo usa un widget
Image
(documentado en Graphics.UI.Gtk.Display.Image) como los usados antes en el
Capítulo 4.1.
Allí usamos imageNewFromFile
para añadir gráficos a un botón; aquí construímos
un widget Image
vacío.
Para actualizarlo cuando haya cambios, tenemos una señal onUpdatePreview
,
que se emite cada vez que el usuario cambia la selección de fichero moviendo el ratón o con las teclas
de aceleración. Esta señal es más general que lo que su nombre sugiere, pero aquí se usa sólo para
previsualizar. El código es el siguiente:
onUpdatePreview fch $ do file <- fileChooserGetPreviewFilename fch case file of Nothing -> putStrLn "No File Selected" Just fpath -> imageSetFromFile img fpath
Hay funciones y atributos para controlar lo que se muestra, por ejemplo lo que sucede cuando el fichero seleccionado no es un fichero gráfico, pero no son estrictamente necesarios. En el resto del código los ficheros no gráficos se ignoran o se muestra un icono estándar. Así es como aparecen:
Fíjate en que el usuario también puede añadir y borrar bookmarks, y
FileChooser
tiene funciones para gestionar esto también. Sin
embargo, esta característica no se trata en el ejemplo FileChooserWidget
, que
tiene el siguiente código fuente:
import Graphics.UI.Gtk main :: IO () main = do initGUI window <- windowNew set window [windowTitle := "File Chooser Widget", windowDefaultWidth := 500, windowDefaultHeight := 400 ] fch <- fileChooserWidgetNew FileChooserActionOpen containerAdd window fch selopt <- checkButtonNewWithLabel "Multiple File Selection" fileChooserSetExtraWidget fch selopt hsfilt <- fileFilterNew fileFilterAddPattern hsfilt "*.hs" fileFilterSetName hsfilt "Haskell Source" fileChooserAddFilter fch hsfilt nofilt <- fileFilterNew fileFilterAddPattern nofilt "*.*" fileFilterSetName nofilt "All Files" fileChooserAddFilter fch nofilt img <- imageNew fileChooserSetPreviewWidget fch img onUpdatePreview fch $ do file <- fileChooserGetPreviewFilename fch case file of Nothing -> putStrLn "No File Selected" Just fpath -> imageSetFromFile img fpath onFileActivated fch $ do dir <- fileChooserGetCurrentFolder fch case dir of Just dpath -> putStrLn ("The current directory is: " ++ dpath) Nothing -> putStrLn "Nothing" mul <- fileChooserGetSelectMultiple fch if mul then do fls <- fileChooserGetFilenames fch putStrLn ("You selected " ++ (show (length fls)) ++ "files:") sequence_ (map putStrLn fls) else do file <- fileChooserGetFilename fch case file of Just fpath -> putStrLn ("You selected: " ++ fpath) Nothing -> putStrLn "Nothing" onToggled selopt $ do state <- toggleButtonGetActive selopt fileChooserSetSelectMultiple fch state widgetShowAll window onDestroy window mainQuit mainGUI
Nota: Con Gtk2Hs 0.9-12 y GHC 6.1 en Fedora Core 6, la selección múltiple de ficheros funciona visualmente (las teclas Ctrl y Shift funcionan como el usuario supone), pero la lista de direcciones de fichero sólo contiene la dirección del último fichero seleccionado.
El segundo modo de usar el interface FileChooser
es a través de FileChooserButton
.
fileChooserButtonNew :: String FileChooserAction -> String -> IO FileChooserButton
El parámetro tipo String
es el nombre de la ventana de diálogo que salta
cuando el usuario selecciona la opción 'other...' después de pulsar el botón. En el ejemplo hemos
construido un botón de selección de fichero con FileChooserActionSelectFolder. Tras seleccionar el directorio
"Test", se vería así.
Así es como se vería la ventana de diálogo:
Como puedes ver, hay un botón "Create Folder" (Crea Carpeta) en la parte superior derecha de la ventana de diálogo. La puedes usar para crear un directorio. Esto es lo que sucede si tratamos de crear una carpeta existente:
Crear o sobrescribir un directorio existente no tiene sentido y puede ser peligroso. Así
que Gtk2Hs automáticamente lo percibe y lo notifica al usuario. Cuando el usuario selecciona un
directorio existente, la señal onCurrentFolderChanged
se emite y el programa puede
tomar la acción apropiada. Al crear un directorio se selecciona automáticamente, así que, en ese caso,
la señal onCurrentFolderChanged
también puede ser usada. Aquí está el código del ejemplo:
import Graphics.UI.Gtk main :: IO () main = do initGUI window <- windowNew set window [windowTitle := "File Chooser Button", windowDefaultWidth := 250, windowDefaultHeight := 75 ] fchd <- fileChooserButtonNew "Select Folder" FileChooserActionSelectFolder containerAdd window fchd onCurrentFolderChanged fchd $ do dir <- fileChooserGetCurrentFolder fchd case dir of Nothing -> putStrLn "Nothing" Just dpath -> putStrLn ("You selected:\n" ++ dpath) widgetShowAll window onDestroy window mainQuit mainGUI
La tercera manera de usar el interfaz FileChooser
es a través de
FileChooserDialog
. Puede ser construido en modo abrir o salvar, y normalmente se aplica desde
un menú o una barra de herramientas.
FileChooserDialog
implementa tanto
FileChooser
como
Dialog
. Recuerda del Capítulo 4.5 que un "diálogo" es un widget compuesto con botones, normalmente
implementados con dialogRun
, que produce respuestas del tipo
ResponseId
. Un
FileChooserDialog
se construye con:
fileChooserDialogNew :: Maybe String -- título del diálogo o "por defecto" -> Maybe Window -- Ventana "padre" del diálogo o nada -> FileChooserAction -- modo abrir o salvar -> [(String, ResponseId)] -- lista de botones y sus códigos de respuesta -> IO FileChooserDialog
Todo lo que tienes que hacer es indicar los nombres de los botones y sus respuestas en el cuarto argumento, y serán automáticamente implementados.
El ejemplo usa
FileChooserActionSave
y la ventana de diálogo tiene tres botones. Así es como queda:
Como puedes ver aquí hay un botón en la parte superior derecha para crear una carpeta. Como en el ejemplo anterior, intentar crear una carpeta ya existente genera un mensaje de error. Sobreescribir un fichero, sin embargo, tiene sentido y es admitido por defecto. Puedes ahcer que el usuario confirme la sobreescritura de ficheros con:
fileChooserSetDoOverwriteconfirmation :: FileChooserClass self => self -> Bool -> IO ()
Como ya mencioné, no se realizan escrituras o sobrescrituras de ficheros con el
widget FileChooserDialog
; El programa simplemente obtiene el path del fichero.
Este es el código del tercer ejemplo:
import Graphics.UI.Gtk main :: IO () main = do initGUI fchdal <- fileChooserDialogNew (Just "Save As...Dialog") Nothing FileChooserActionSave [("Cancel", ResponseCancel), ("Save", ResponseAccept), ("Backup", ResponseUser 100)] fileChooserSetDoOverwriteConfirmation fchdal True widgetShow fchdal response <- dialogRun fchdal case response of ResponseCancel -> putStrLn "You cancelled..." ResponseAccept -> do nwf <- fileChooserGetFilename fchdal case nwf of Nothing -> putStrLn "Nothing" Just path -> putStrLn ("New file path is:\n" ++ path) ResponseUser 100 -> putStrLn "You pressed the backup button" ResponseDeleteEvent -> putStrLn "You closed the dialog window..." widgetDestroy fchdal onDestroy fchdal mainQuit mainGUI
Nota: Al probarlo con Gtk2Hs 0.9-12 y GHC 6.1 en Fedora Core 6,
pulsar la tecla "Enter" para guardar el fichero no tiene ningún efecto. Cuando
se elige un fichero existente, pulsar la tecla "Save" no tiene efecto la primera vez,
pero si se pulsa de nuevo provoca la aparición de la ventana de confirmación. Mi
opinión es que esto tiene algo que ver con la señal onConfirmOverwrite
y
su segundo argumento de tipo IO FileChooserConfirmation.
No entiendo bien su uso,
y quizá el error provenga de mi código.