[Tutorial-Port-Spanish Chapters 1,2,3 and index hthiel.char@zonnet.nl**20071208154317] { addfile ./docs/tutorial/Tutorial_Port/es-chap1.xhtml hunk ./docs/tutorial/Tutorial_Port/es-chap1.xhtml 1 - + + + +
+ +Del sitio web de Gtk2Hs: Gtk2Hs es una libreria GUI (Interfaz gráfico de usuario) para Haskell basada en +Gtk+. Gtk+ es un conjunto de herramientas extensivo y maduro, multi-plataforma para crear interfaces gráficos de usuario.
+El objetivo de este tutorial es el programador intermedio de Haskell, y cubre todo lo necesario para escribir los interfaces de usuario más +comunes. En particular los contenidos están indicados en el índice de este tutorial. Para los usuarios avanzados la +documentación de la API de Gtk2Hs contiene mucha más información.
+Gtk+ es fundamentalmente una biblioteca de componentes llamados 'widgets', +que se organizan en una jerarquía orientada a objetos. En Gtk2Hs esto +se hace dando a cada widget tanto un tipo como una clase de tipo (type y type +class) de Haskell. De este modo el sistema de tipos de Haskell está rigurosamente +preservado, y el programador tiene todas las ventajas de la comprobación +de tipos del compilador y del interprete de Haskell de Glasgow (ghc). +Gtk2Hs tiene también muchas funciones para el casting de tipos.
+Los widgets tienen atributos que controlan su comportamiento y apariencia, +existiendo dos funciones generales para obtener y establecer los atributos +de los mismos.
+Para dar/establecer (set) uno o más atributos a un widget usa:
+set widget [widgetAttr1 := foo, widgetAttr2 := fie, widgetAttr3 := bar]+
Para obtener el valor de un atributo (get) de un widget usa:
+x <- get widget widgetAttr1+
Casi siempre hay funciones específicas para obtener y establecer +los atributos de los widgets, pero en el futuro serán eliminadas. +Actualmente, sin embargo es más común el uso de dichas funciones.
+Todo lo que sucede en una aplicación en Gtk2Hs está determinado
+por las acciones del usuario, como pulsaciones de ratón, presionado de
+botones, selección de botones de radio y demás.A esto se le llama eventos y resultan en señales emitidas.
+La información sobre los eventos, por ejemplo cuando el usuario pulsa el
+botón derecho o izquierdo del ratón, se captura en campos de
+datos que pueden ser consultados por el programador de Gtk2Hs.
+Normalmente las señales se gestionan por funciones específicas de cada
+tipo de widget. Se listan en la sección señales (*Signals*) de
+la documentación de la API de ese widget. La mayoría toman una función
+de tipo IO ()
como parámetro, pero algunas tienen algún resultado.
+El programador de Gtk2Hs, por supuesto, debe escribir esas funciones que determinan la
+respuesta del sistema a la acción del usuario.
Finalmente, el etablecimiento del árbol de widgets para tu aplicación y +la determinación de la disposición (layout) visual (empaquetado) puede +hacerse con el diseñador de interfaces de Glade. +Gtk2Hs se integra perfectamente con Glade y hay un tutorial introductorio +en el sitio web de Gtk2Hs. +Se recomienda el uso de Glade para el seguimiento de este tutorial.
+ + + addfile ./docs/tutorial/Tutorial_Port/es-chap2.xhtml hunk ./docs/tutorial/Tutorial_Port/es-chap2.xhtml 1 - + + + + + ++Lo primero es bajarse el Gtk2Hs e instalarlo. Siempre se puede conseguir la última +versión de http://haskell.org/gtk2hs/ y +diversas versiones de Linux disponen de sus propios paquetes de Gtk2Hs. Hay un instalador +disponible para Windows. +
+ ++Lo siguiente es abrir la documentación de referencia de la API para tu versión. +La vas a usar a menudo para encontrar los nombres de los widgets, los métodos, +atributos y señales que podrías querer usar. +Se listan todos los módulos y además se dispone de un índice. +Dentro de cada descripción de módulo se muestra la jerarquía de clases. +Si un método, atributo o señal que esperas encontrar no aparece, podría +pertenecer a una de las superclases de las que tu tipo de widget es una instancia. +
+ ++Nuestra introducción a Gtk2Hs, empieza con el programa más simple posible. +Este programa crea una ventana de 200x200 pixeles y no hay modo de salir de ella, +exceptuando que sea "matada" usando la shell.
+ +import Graphics.UI.Gtk + +main :: IO () +main = do + initGUI + window <- windowNew + widgetShowAll window + mainGUI ++ +
+Puedes compilar el programa que aparece arriba con el compilador de Haskell +de Glasgow (Glasgow Haskell Compiler) GHC usando: +
+ +ghc --make GtkChap2.hs -o Chap2 ++ +
+asumiendo que GtkChap2.hs
es el nombre del fichero. También puedes usar
+el intérprete interactivo GHCi para probarlo, para versiones anteriores y posteriores
+de Gtk2Hs. Debido a problemas con los hilos (threads) las versiones intermedias no funcionarán
+interactivamente. (Gtk2Hs no funciona con Hugs.)
+
+La primera línea del programa importa la librería gráfica de Gtk2Hs. +
+ +
+Todos los programas de Gtk2Hs se ejecutan en main
. La primera línea de
+la función es un bloque do
:
+
initGUI ++ +
+es una función usada en todas las aplicaciones de Gtk2Hs. +
+ ++Las siguientes dos líneas del bloque crean y muestran una ventana y sus contenidos: +
+ +window <- windowNew + widgetShowAll window ++ +
+En vez de crear una ventana de tamaño 0x0, una ventana sin hijos se crea por defecto de +200x200 puntos, de modo que pueda ser manipulada por el usuario. Los Widgets que se pueden +visualizar (no todos pueden visualizarse) deben ser mostrados u ocultados usando sus propios +métodos, pero la segunda línea funciona en el widget (aquí la ventana) y todos sus hijos. +
+ +
+La última línea de main
activa el bucle principal de proceso de Gtk2Hs:
+
mainGUI ++ +
+que es otra llamada que verás en todas las aplicaciones Gtk2Hs. Cuando el +control alcanza este punto, Gtk2Hs dormirá esperando algún evento (pulsado +de un botón o de una tecla), timeouts, o notificaciones en los ficheros de entrada/salida. +En nuestro ejemplo anterior, sin embargo, los eventos son ignorados. +
+ ++Vamos ahora por un programa con un widget (un botón): El clásico "hello world" 'a +la Gtk2Hs'. +
+ +
+Si se pulsa el botón, mostrará el texto "Hello World". Esto está implementado
+en la función Haskell hello
con un botón b como argumento.
+La declaración de tipos establece la variable o como una instancia de la clase
+ButtonClass
. Gtk2Hs usa extensivamente las clases de Haskell para reflejar la
+jerarquía de objetos de los widgets GTK originales. Cada widget en Gtk2Hs tiene, por
+supuesto, un tipo de Haskell.
+
+Los widgets, y las clases a las que sus tipos pertenecen, normalmente tienen atributos.
+Estos pueden establecerse, por métodos específicos o por la función
+general set
, que usa una notación tipo lista como se muestra a continuación.
+De especial interés es el atributo containerChild
de la ventana (de una superclase
+de la ventana) que indica la relación con el botón. Debido a que existe esta relación
+widgetShowAll window
hará el botón visible.
+
+Los widgets están conectados a otros widgets en un árbol de dependencia gráfica +(no debería confundirse con la jerarquía de clases). Gtk2Hs también trabaja con el +diseñador de interfaz visual de Glade y, si usas Glade, estas relaciones serían visibles +en el panel del Inspector. +Hay un tutorial independiente sobre como usar +Glade con Gtk2Hs.
+ +import Graphics.UI.Gtk + +hello :: (ButtonClass o) => o -> IO () +hello b = set b [buttonLabel := "Hello World"] + +main :: IO () +main = do + initGUI + window <- windowNew + button <- buttonNew + set window [windowDefaultWidth := 200, windowDefaultHeight := 200, + containerChild := button, containerBorderWidth := 10] + onClicked button (hello button) + onDestroy window mainQuit + widgetShowAll window + mainGUI ++ +
+Gtk2Hs está guiado por los sucesos (event driven). La función
+mainGUI
dormirá hasta que algo suceda, como que el ratón
+sea pulsado, una ventana sea destruida o alguna otra cosa típica de un widget.
+Los sucesos entonces activan señales que a su vez activan las funciones que ha
+definido el usuario para ser evaluadas si se da esa circunstancia.
+En este caso la señal onClicked
, emitida por el usuario al pulsar el botón,
+se engancha al texto que será mostrado en ese mismo botón.
+Al destruir la ventana, no como en el primer programa, main
ahora sale limpiamente.
+
+Al crear una aplicación, puede que quieras colocar más de un widget dentro de
+una ventana. Nuestro primer ejemplo "hello world" únicamente usaba un widget,
+por lo que pudimos usar set
para especificar un widget containerChild
+(contenedor de hijo) para window
, o usar containerAdd
(Añade contenedor)
+para empaquetar el widget en la ventana. Pero cuando quieres poner más de un widget en
+una ventana, cómo puedes controlar la posición del mismo? Para esto sirve el
+empaquetado.
+La mayoría del empaquetado se hace creando cajas. Hay contenedores invisibles de +widgets en los que podemos empaquetarlos. Los encontramos de dos tipos: +Una caja horizontal y una caja vertical. +Al empaquetar los objetos en una caja horizontal, los objetos se insertan horizontalmente +de izquierda a derecha o de derecha a izquierda dependiendo de la llamada utilizada. +En la caja vertical, los widgets se empaquetan de arriba abajo o viceversa. +Puedes usar cualquier combinación de cajas dentro o junto a otras para crear el +efecto deseado. +
+
+Para crear una nueva caja horizontal, usamos hBoxNew
, y para una caja vertical,
+vBoxNew
. Ambos usan un parámetro de tipo Bool
y otro de
+tipo Int
. El primer parámetro dará espacios iguales a cada widget hijo si se le pasa el
+ valor True y el segundo establece el número de píxeles a colocar 'por defecto' entre los hijos.
+
+Las funciones boxPackStart
y boxPackEnd
se usan para colocar objetos dentro de esos
+contenedores. La función boxPackStart
colocará los widgets de arriba abajo en una caja vertical, VBox
, y de izquierda a derecha en una caja horizontal. boxPackEnd
+hará lo opuesto, de abajo arriba en una caja vertical VBox
, y de derecha a izquierda en una
+HBox
.
+Mediante estas funciones podremos justificar por la derecha o por la izquierda nuestros widgets y mezclarlos
+de modo que obtengamos el efecto deseado. En la mayoría de nuestros ejemplos usaremos boxPackStart
.
+
+Un objeto opuede ser otro contenedor o un widget. De hecho, muchos widgets son a la vez contenedores, incluyendo
+ button
,(botón) pero habitualmente sólo usamos una label
(etiqueta) dentro de un button
.
+
import Graphics.UI.Gtk + +main :: IO () +main = do + initGUI + window <- windowNew + hbox <- hBoxNew True 10 + button1 <- buttonNewWithLabel "Button 1" + button2 <- buttonNewWithLabel "Button 2" + set window [windowDefaultWidth := 200, windowDefaultHeight := 200, + containerBorderWidth := 10, containerChild := hbox] + boxPackStart hbox button1 PackGrow 0 + boxPackStart hbox button2 PackGrow 0 + onDestroy window mainQuit + widgetShowAll window + mainGUI ++ +
+Usando boxPackStart
o boxPackEnd
, GTK sabe dónde quieres colocar
+tus widgets por lo que puede cambiar automáticamente el tamaño y otras cosas interesantes.
+
boxPackStart :: (WidgetClass child, BoxClass self) => self -> child -> Packing -> Int -> IO () ++ +
boxPackEnd :: (WidgetClass child, BoxClass self) => self -> child -> Packing -> Int -> IO () ++ +
+El parámetro de Packing
(empaquetado) especifica el modo en que los
+widgets del contenedor se comportan cuando la ventana cambia de tamaño. PackNatural
+indica que los widgets mantendrán su tamaño y posición, PackGrow
+que su tamaño será adaptado, y con PackRepel
los widgets serán
+espaciados entre ambos lados (manteniendo el tamaño). El último parámetro es un
+Int
, que especifica el espaciado que se dejará entre este "hijo" y sus
+vecinos.
+
+El empaquetado sólo se aplica a la dimensión principal de la caja (horizontal o vertical).
+Si, por ejemplo, indicas PackNatural
en vez de PackGrow
+en el ejemplo anterior, el cambio de tamaño horizontal mantendrá los botones
+en su tamaño original, pero los cambios verticales sí le harán cambiar de tamaño.
+Esto se debe a que los botones se colocan homogéneamente en la caja horizontal.
+(El primer parámetro es True) y la caja se adapta a los cambios de la ventana.
+El próximo ejemplo clarificará más las cosas.
+
+En este ejemplo, la base de todos los widgets es una caja vertical, que a su vez es hija
+de la ventana. Los widgets hijos no se muestran de modo homogéneo y no hay un espacio adicional
+(diferente del espaciado estándar).
+Hay seis cajas horizontales en la caja vertical, tal y como se definen con la función
+ makeBox
, que tiene el tipo mostrado. Además, hay dos etiquetas en la caja vertical
+así como dos separadores horizontales. El último widget es el botón Quit
+cuya señal onClicked
se engancha a la función mainQuit
.
+
+Los separadores se crean con hSeparatorNew
y están espaciados por boxPackStart
+con un espacio de separación de diez píxeles. Las etiquetas (label) se crean con labelNew
+con un Maybe String
y su posición se establece con miscSetAlignment
para estar
+justificados a la izquierda y arriba.
+
+La función makeBox :: Bool -> Int -> Packing -> Int -> IO
+HBox
demuestra como los widgets de Gtk2Hs encajan dentro del sistyema de tipos de haskell.
+Packing
es simplemente un tipo, como Int
y Bool
+y IO HBox
es como IO String
en el típico IO
.
+La función crea cinco botones, los etiqueta con el texto apropiado y los empaqueta en una
+caja horizontal. Entonces la función se emplea en el programa principal para crear los modos de
+empaquetado deseados.
import Graphics.UI.Gtk + +main :: IO () +main = do + initGUI + window <- windowNew + vbox <- vBoxNew False 0 + set window [containerBorderWidth := 10, + windowTitle := "Packing Demonstration", + containerChild := vbox] + label1 <- labelNew (Just "hBoxNew False 0") + miscSetAlignment label1 0 0 + boxPackStart vbox label1 PackNatural 0 + box1 <- makeBox False 0 PackNatural 0 + boxPackStart vbox box1 PackNatural 0 + box2 <- makeBox False 0 PackRepel 0 + boxPackStart vbox box2 PackNatural 0 + box3 <- makeBox False 0 PackGrow 0 + boxPackStart vbox box3 PackNatural 0 + sep1 <- hSeparatorNew + boxPackStart vbox sep1 PackNatural 10 + label2 <- labelNew (Just "hBoxNew True 0") + miscSetAlignment label2 0 0 + boxPackStart vbox label2 PackNatural 0 + box4 <- makeBox True 0 PackNatural 0 + boxPackStart vbox box4 PackNatural 0 + box5 <- makeBox True 0 PackRepel 0 + boxPackStart vbox box5 PackNatural 0 + box6 <- makeBox False 0 PackGrow 0 + boxPackStart vbox box6 PackNatural 0 + sep <- hSeparatorNew + boxPackStart vbox sep PackNatural 10 + quitbox <- hBoxNew False 0 + boxPackStart vbox quitbox PackNatural 0 + quitbutton <- buttonNewWithLabel "Quit" + boxPackStart quitbox quitbutton PackRepel 0 + onClicked quitbutton mainQuit + onDestroy window mainQuit + widgetShowAll window + mainGUI + + +makeBox :: Bool -> Int -> Packing -> Int -> IO HBox +makeBox homogeneous spacing packing padding = do + box <- hBoxNew homogeneous spacing + button1 <- buttonNewWithLabel "boxPackStart" + boxPackStart box button1 packing padding + button2 <- buttonNewWithLabel "box" + boxPackStart box button2 packing padding + button3 <- buttonNewWithLabel "button" + boxPackStart box button3 packing padding + button4 <- case packing of + PackNatural -> buttonNewWithLabel "PackNatural" + PackRepel -> buttonNewWithLabel "PackRepel" + PackGrow -> buttonNewWithLabel "PackGrow" + boxPackStart box button4 packing padding + button5 <- buttonNewWithLabel (show padding) + boxPackStart box button5 packing padding + return box ++ +
+La imagen de abajo muestra los efectos de modificar el tamaño de la ventana horizontalmente. +En el primer grupo, con homogeneous en False, las modificaciones +horizontales dejan la frimera fila de botones como estaban, espacian la segunda fila de modo equilibrado +y agrandan los botones de la tercera fila. En el segundo grupo, los botones se empaquetan homogeneamente, y +las primeras dos filas se verán iguales. Si modificamos el tamaño verticalmente, simplemente añadirá +espacios adicionales al final, ya que la caja vertical ha sido inicializada con False. +
+ ++Vamos a aprender otro modo de empaquetar: las tablas. Pueden ser extremadamente +útiles en determinadas situaciones. Usando tablas, creamos una cuadrícula (grid) en la que podemos situar +los widgets. Los widgets pueden ocupar tantos espacios como especifiquemos. +
+ +
+En primer lugar consideraremos la función tableNew
(Nueva tabla)):
+
tableNew :: Int -> Int -> Bool -> IO Table ++ +
+El primer argumento es el número de filas que tendrá la tabla, y el segundo, +el número de columnas. +
+ ++El argumento booleano (homogéneo) indica como se establece el tamaño +de las cajas de la tabla. Si homogéneo se establece a True, +entonces las cajas se crean al tamaño del mayor widget de la tabla, y todas iguales. Si +homogéneo se establece a False, el tamaño de las cajas de la tabla +está determinado por el widget más alto de su fila, and el widget más ancho de su columna. +Por ejemplo, supón que tienes una matríz de 10x10 botones cada uno etiquetado con los numeros +del 1 al 100. Así, te aparecen tres posibles anchuras, la de los números del 1 al 9, +la de los números del 10 al 99 y la del 100. Si has establecido en True el valor de +homogéneo, todos los botones serán iguales y el ancho corresponderá al del botón 100. +Sin embargo, si el valor que has puesto es Falso, la última columna mantendrá el ancho del botón de 100 +y las otras el de los botones correspondientes al 10 al 99. En este ejemplo las alturas de los botones +serían iguales con las dos opciones, ya que sólo tenemos una posible altura. +
+ +
+Las filas y colunas se numeran desde 0 hasta n, donde
+n es el número especificado en la llamada a tableNew
. Así
+si especificas filas = 2 y columnas =
+2, la distribución parecerá algo así:
+
0 1 2 +0+----------+----------+ + | | | +1+----------+----------+ + | | | +2+----------+----------+ ++ +
+Fíjate en que el sistema de coordenadas empieza en la esquina superior +izquierda. Para colocar un widget en una caja, usa la siguiente función: +
+ +tableAttach :: (TableClass self, WidgetClass child) + => self -- self - La tabla. + -> child -- child - El widget a añadir. + -> Int -- leftAttach - El número de la columna para situar el lado izquierdo + -- del widget a colocar. + -> Int -- rightAttach - El número de la columna para situar el lado derecho + -- del widget a colocar. + -> Int -- topAttach - El número de la fila donde situar la parte superior del + -- widget a colocar. + -> Int -- bottomAttach - El número de la fila donde situar la parte inferior del + -- widget a colocar. + -> [AttachOptions] -- xoptions - Se emplea para indicar las propiedades del widget hijo + -- cuando la tabla modifica su tamaño (horizontales) + -> [AttachOptions] -- yoptions - Igual que xoptions, exceptuando que el campo determina + -- el comportamiento vertical + -> Int -- xpadding - Un valor entero que especifica el espacio en blanco a + -- izquierda y derecha de la tabla del widget que se añade. + -> Int -- ypadding - El espacio arriba y abajo del widget. + -> IO () ++ +
+El primer argumento, (self), es la tabla que has creado y el segundo, +(child), es el widget que quieres colocar en la tabla. +
+ ++Los argumentos izquierdo y derecho (leftAttach, rightAttach) indican donde debe ponerse +el widget, y cuantas cajas usar. Si quieres poner un botón en la parte de abajo a la derecha +de tu tabla de 2x2, y quieres colocar solo esa entrada, +leftAttach debería ser = 1, rightAttach = +2, topAttach = 1, y bottomAttach += 2. +
+ ++Ahora, si quieres que un widget llene completamenta la fila superior +de nuestra tabla de 2x2, deberías susar los siguientes valores: +leftAttach = 0, rightAttach = +2, topAttach = 0, and bottomAttach = +1. +
+ ++xoptions y yoptions se usan para indicar las opciones de empaquetado y +la lista puede contener más de una para permitir múltiples opciones. +
+ +Las posibles opciones son:
+ +Fill
Fill
,
+ el widget se expandirá hasta ocupar todo el espacio disponible. (como un gas cualquiera).
+ Shrink
Shrink
+ los widgets de reducirán con la tabla.
+ Expand
+ El padding funciona como en las cajas, dejando un área libre alrededor + del widget, del tamaño especificado en píxeles +
+ +
+tableAttach
tiene muchas opciones, así que ponemos un resumen:
+
tableAttachDefaults :: (TableClass self, WidgetClass widget) + => self -- self - La tabla. + -> widget -- widget - El widget a añadir. + -> Int -- leftAttach - El número de la columna en el que se situará el lado + -- izquierdo del widget a añadir. + -> Int -- rightAttach - El número de la columna en el que se situará el lado + -- derecho del widget a añadir. + -> Int -- topAttach - El número de la fila en el que se situará el lado superior + -- del widget. + -> Int -- bottomAttach - El número de la fila en el que se situará el lado inferior + -- del widget. + -> IO () ++ +
+Los valores usados para los parámetros de [AttachOptions]
son
+[Expand, Fill], y el espacio (padding) se fija en 0. El resto de los
+argumentos son los mismos que para la función previa.
+
+También tenemos tableSetRowSpacing
y
+tableSetColSpacing
. Estas funciones colocan espacios en la fila (row) o columna (col) indicada.
+
tableSetRowSpacing :: TableClass self=> self-> Int -> Int -> IO () ++ +
tableSetColSpacing :: TableClass self => self -> Int -> Int -> IO () ++ +
+El primer argumento Int
es la fila/columna y el segundo es el espacio
+en píxeles. Fíjate que en el caso de las columnas, el espacio se situa a la derecha de
+la columna y en las filas, bajo la fila.
+
+Puedes fijar un espacio fijo para todas las filas y/o las columnas con: +
+ +tableSetRowSpacings :: TableClass self=> self-> Int -> IO () ++ +
y:
+ +tableSetColSpacings :: TableClass self => self -> Int -> IO () ++ +
+Fíjate que con estas llamadas, la última fila y la última columna no +consiguien ningún espacio. +
+ ++Vamos a hacer una ventana con tres botones en una tabla 2x2. Los primeros dos botones +se colocarán en la fila de arriba. Un tercer botón (Quit) se colocará en la fila +de abajo, ocupando ambas columnas. Quedará así: +
+ +Aquí está el código fuente:
+ +import Graphics.UI.Gtk + +main :: IO () +main = do + initGUI + window <- windowNew + set window [windowTitle := "Table", containerBorderWidth := 20, + windowDefaultWidth := 150, windowDefaultHeight := 100] + table <- tableNew 2 2 True + containerAdd window table + button1 <- buttonNewWithLabel "On" + onClicked button1 (buttonSwitch button1) + tableAttachDefaults table button1 0 1 0 1 + button2 <- buttonNewWithLabel "Off" + onClicked button2 (buttonSwitch button2) + tableAttachDefaults table button2 1 2 0 1 + button3 <- buttonNewWithLabel "Quit" + onClicked button3 mainQuit + tableAttachDefaults table button3 0 2 1 2 + onDestroy window mainQuit + widgetShowAll window + mainGUI + +buttonSwitch :: Button -> IO () +buttonSwitch b = do + txt <- buttonGetLabel b + let newtxt = case txt of + "Off" -> "On" + "On" -> "Off" + buttonSetLabel b newtxt ++ +
+La función buttonSwitch
es asocia a ambos botones de la fila superior.
+La función buttonGetLabel
es un ejemplo de cómo conseguir un atributo
+de un widget usando un método estándar. Hay una alternativa más general get
+(análoga a set
) que toma un widget y un atributo.
+En el ejemplo anterior sería:
+
txt <- get b buttonLabel ++ +
+con el mismo resultado. +
+ + + + + addfile ./docs/tutorial/Tutorial_Port/es-index.xhtml hunk ./docs/tutorial/Tutorial_Port/es-index.xhtml 1 - + + + + + +El Tutorial GTK tiene el Copyright © 1997 Ian Main.
+Copyright © 1998-2002 Tony Gale.
+Copyright © 2007 Hans van Thiel and Alex Tarkovsky.
+Copyright © 2007 Traducción española Laszlo Keuschnig
+Se permite hacer y distribuir copias exactas de este manual + siempre que la información del copyright y esta nota + sobre los permisos se mantenga en todas las copias.
+Se permite copiar y distribuir versiones modificadas de este + documento bajo las condiciones de copia exacta, siempre que esta + información sobre el copyright se incluya exactamente como + en el original, y que el trabajo completo derivado de este se + distribuya bajo las condiciones de una nota de permisos idéntica + a esta.
+Se permite copiar y distribuir traducciones de este documento + en otras lenguas bajo las condiciones expresadas para las versiones + modificadas.
+Si pretende incorporar este documento a un trabajo impreso, por + favor contacte con el responsable del mantenimiento del mismo, y + haremos un esfuerzo para asegiurarle que dispone de la informació + más actualizada disponible.
+No hay garantíde que este documento responda a sus + propósitos. Se proporciona como un recurso libre y gratuito. + Por tanto, los autores y los responsables del mantenimiento de + la información no pueden dar ninguna garantía de que + la información sea correcta.
+Hemos usado y agradecemos su uso 'Programming with gtkmm' de Murray Cumming et al. + y el tutorial del Inti (Integrated Foundation Classes) tutorial del Inti team.
+El Capítulo 7 está basado principalmente en el Capítulo 11 de 'Programming with gktmm' y la demo + incluída en la distribución de Gtk2Hs.
+ + + }