28 de Junio del 2000 PUF GTK+ Tony Gale Shawn Amundson Emmanuel Deloget La intención de este documento, es responder las preguntas que con seguridad harán los programadores que utilizan GTK+, o por personas que simplemente están investigando como utilizar GTK+. PUF GTK+ Información General Antes que todo: los agradecimientos Los autores del PUF quieren dar las gracias a: Havoc Pennington Erik Mouw Owen Taylor Tim Janik Thomas Mailund Jensen Joe Pfeiffer Andy Kahn Federico Mena Quintero Damon Chaplin y a todos los miembros de la lista GTK+ ¡Si nos olvidamos de ti, por favor envíanos un correo electrónico! Gracias de nuevo (Ya se, es muy cortito :) Autores Los autores originales de GTK+ fueron: Peter Mattis Spencer Kimball Josh MacDonald Desde entonces, otros han agregado muchas cosas. Por favor, revisa el archivo AUTHORS en la distribución para conocer el Equipo GTK+. ¿Qué es GTK+? GTK+ es un conjunto pequeño y eficiente de widgets Nota del traductor: Widget significa Componente Gráfico Reutilizable. , diseñado con el aspecto y sensación general de Motif. En realidad, luce mucho mejor que Motif. Contiene widgets comunes y algunos widgets más complejos como un selector de archivos, y widgets para seleccionar colores. GTK+ provee algunas características únicas. (Al menos no conozco de otra librería de widgets que las provea). Por ejemplo, un botón no contiene una etiqueta, contiene un widget hijo, que en la mayoría de las instancias será una etiqueta. Sin embargo, el widget hijo también puede ser un pixmap, una imagen o cualquier combinación posible que el programador desee. Esta flexibilidad está adherida en toda la librería. ¿Qué es el + en GTK+? Peter Mattis informó a la lista de correo gtk que: Originalmente escribí gtk que incluía las tres librerías, libglib, libgdk y libgtk. Se caracterizaba por una jerarquía plana de widget. Osea que, no podías derivar un nuevo widget de uno existente. Contenía un mecanismo de callbacks más estándar en lugar del mecanismo de señales ahora presente en gtk+. El + fue agregado para distinguir entre la versión original de gtk y la nueva versión. Puedes pensar de ello como una mejora al gtk original que agrega características orientadas a objetos. GTK+ provee algunas características únicas. (Al menos no conozco de otra librería de widgets que las provea). Por ejemplo, un botón no contiene una etiqueta, contiene un widget hijo, que en la mayoría de las instancias será una etiqueta. Sin embargo, el widget hijo también puede ser un pixmap, una imagen o cualquier combinación posible que el programador desee. Esta flexibilidad está adherida en toda la librería. ¿La G de GTK+, GDK y GLIB significa? GTK+ == Gimp Toolkit (Juego de Herramientas de Gimp) GDK == GTK+ Drawing Kit (Herramientas de Dibujo de GTK+) GLib == G Libray (Librería G) ¿Dónde está la documentación para GTK+? En el directorio doc/ de la distribución de GTK+ encontrarás el material de referencia para GTK y GDK, este PUF y el Tutorial GTK. Además, puedes encontrar enlaces a versiones HTML de estos documentos dirigiéndote a http://www.gtk.org/. Una versión empaquetada del Tutorial GTK con versiones SGML, HTML, Postscript, DVI y texto, puede ser encontrado en ftp://ftp.gtk.org/pub/gtk/tutorial Nota del revisor: varios de estos documentos tienen su traducción al español en Gnome-es Hay un par de libros disponibles, los cuales lidian con programación GTK+, GDK y GNOME: El libro de Eric Harlow titulado «Developing Linux Applications with GTK+ and GDK», Desarrollo de aplicaciones Linux con GTK+ y GDK. El ISBN es 0-7357-0021-4 El código de ejemplo del libro de Eric está disponible en-línea en http://www.bcpl.net/~eharlow/book Havoc Pennington ha sacado un libro llamado «GTK+/GNOME Application Development», Desarrolo de aplicaciones GTK+/Gnome. El ISBN es 0-7357-0078-8 La versión libre/gratuita del libro vive aquí: http://developer.gnome.org/doc/GGAD/ Havoc mantiene información del libro y errata aquí: http://pobox.com/~hp/gnome-app-devel.html Nota del revisor: El libro de Havoc me parece más asequible para principiantes. El de Eric contiene información muy interesante para iniciar un proyecto completamente nuevo y trucos muy interesantes. ¿Existe una lista de correo (o archivo de lista de correo) para GTK+? Información sobre listas de correo relacionadas con GTK+ se puede encontrar en http://www.gtk.org/mailinglists.html Cómo obtener ayuda con GTK+ Primero, asegúrate que tu pregunta no está respondida en la documentación, este FAQ o el tutorial. ¿Ya lo hiciste? ¿Seguro que lo hiciste, correcto? En ese caso, el mejor lugar para enviar tus preguntas es a la lista de correo GTK+. Cómo reportar <foreignphrase>bugs</foreignphrase> en GTK+ Los bugs Nota del traductor: Bug significa Error de Programación deben ser reportados al sistema de GNOME para rastreo de bugs (http://bugs.gnome.org). Para reportar un problema sobre GTK+, envíe correo a submit@bugs.gnome.org. El sujeto del correo debe describir su problema. En el cuerpo del correo, debe incluir de primero un "pseudo-encabezado" que informe el paquete y número de versión. Esto debe ser separado de los encabezados reales por una línea en blanco. Paquete: gtk+ Versión: 1.2.0 Substituya 1.2.0 con la versión de GTK+ que tenga instalada. Entonces describa el bug. Incluya: Información sobre su sistema. Por ejemplo: Qué sistema operativo y la versión Qué versión de X Para Linux, qué versión de la librería de C Y cualquier otra cosa que crea que es relevante Cómo reproducir el bug. Si lo puede reproducir con el programa testgtk que se construye en el subdirectorio gtk/, sería lo más conveniente. De otro modo, por favor incluya un programa de prueba corto que muestre el comportamiento. Como último recurso, también puede guiarnos a una pieza de software mayor que pueda ser bajada. (Los bugs que pueden ser reproducidos dentro de GIMP son casi tan tan buenos como los que pueden ser reproducidos en testgtk. Si está reportando un bug encontrado con el GIMP, por favor incluya el número de versión de GIMP que está utilizando) Si el bug colgó el programa, el texto exacto que se imprimió al ocurrir. Información adicional, como rastreos de la pila, puede ser de utilidad, pero no es necesaria. En caso de que envía un rastreo de la pila, y el error es un error de X, será más útil si el rastreo de la pila es producido corriendo el programa de prueba con la opción de la línea de comando --sync. ¿Existe una versión de GTK+ para Windows? Hay un traslado activo de GTK+ a la plataforma windows, el cual está progresando impresionantemente. Ver http://www.iki.fi/tml/gimp/win32 para más información. ¿Qué aplicaciones se han escrito con GTK+? Una lista de algunas aplicaciones basadas en GTK+ se puede encontrar en el servidor de web de GTK+ en http://www.gtk.org/apps/ y contiene más de 350 aplicaciones. Si eso falla, busca un proyecto para trabajar para el proyecto GNOME, http://www.gnome.org/ Escribe un juego. Escribe algo que sea útil. Algunos de estos son: GIMP (http://www.gimp.org/), un programa de manipulación de imágenes AbiWord (http://www.abisource.com/), un procesador de palabras profesional Gzilla (http://www.levien.com/gzilla/), un navegador de web XQF ( http://www.botik.ru/~roma/quake/), un navegador y lanzador de servidores QuakeWorld/Quake2 GDK Imlib ( http://www.rasterman.com/imlib.html), una librería para GDK para manipulación y carga rápida de imágenes Glade (http://glade.pn.org/), una herramienta RAD basada en GTK+ que produce aplicaciones GTK+ Estoy planeando escribir una aplicación en GTK+. ¿Qué les parece un cliente IRC? Pregunta en la lista gtk para sugerencias. Hay por lo menos tres clientes IRC bajo desarrollo (probablemente más de hecho. El servidor en http://www.forcix.cx/irc-clients.html lista un montón de ellos). X-Chat. girc. (Incluído con GNOME) gsirc. (En el árbol CVS de gnome) Cómo encontrar, configurar, instalar e investigar fallas en GTK+ ¿Qué necesito para correr GTK+? Para compilar GTK+, todo lo que necesitas es un compilador de C (gcc) y el Sistema de Ventanas X y librerías asociadas en tu sistema. ¿Dónde puedo obtener GTK+? El sitio canónico esftp://ftp.gtk.org/pub/gtk. Este sitio tiende a sobre-cargarse alrededor del lanzamiento de un nuevo GTK+, así que intenta utilizar alguno de los sitios espejo listados en ftp://ftp.gtk.org/etc/mirrors Aquí hay algunos sitios espejos para que puedas iniciar: África - ftp://ftp.is.co.za/applications/gimp Australia - ftp://ftp.au.gimp.org/pub/gimp Finlandia - ftp://ftp.funet.fi/pub/sci/graphics/packages/gimp Alemania - ftp://infosoc.uni-koeln.de/pub/ftp.gimp.org" Japón - ftp://SunSITE.sut.ac.jp/pub/archives/packages/gimp Reino Unido - ftp://ftp.flirble.org/pub/X/gimp EUA - ftp://ftp.insync.net/pub/mirrors/ftp.gimp.org ¿Cómo configurar/compilar GTK+? Por lo general, todo lo que necesitas es ejecutar los comandos: ./configure make en el directorio gtk+-versión/. Cuando compilo GTK+ obtengo un error como: <literal>make: file `Makefile' line 456: Syntax error</literal> Asegúrate que utilizas GNU make (usa make -v para verificar). Hay muchas versiones raras y maravillosas de make por ahí, y no todas ellas pueden manejar los Makefiles generados automáticamente. ¡He compilado e instalado GTK+, pero no puedo lograr enlazar programa alguno con él! Este problema se encuentra cuando las librerías GTK+ no pueden ser localizadas o son de una versión incorrecta. Generalmente, el compilador se queja de un 'unresolved symbol'. Hay dos cosas que necesitas verificar: Asegúrate que las librerías se pueden encontrar. Quieres editar /etc/ld.so.conf para incluir los directorios que contienen las librerías GTK, de manera que luzca parecido a esto: /usr/X11R6/lib /usr/local/lib Entonces debes correr /sbin/ldconfig como root. Puedes averiguar en que directorio se encuentra GTK utilizando gtk-config --libs Si tu sistema no utiliza ld.so para encontrar librerías (como Solaris), entonces deberás utilizar la variable de entorno LD_LIBRARY_PATH (o compilara la ruta en tu programa, lo cual no voy a cubrir aquí). Así que, con una línea de comandos tipo Bourne, puedes (si tus librerías GTK están en /usr/local/lib): export LD_LIBRARY_PATH=/usr/local/lib y en csh, puedes hacer esto: setenv LD_LIBRARY_PATH /usr/local/lib Asegúrate que el enlazador está localizando el conjunto correcto de librerías. Si tienes una distribución Linux que instala GTK+ (ejemplo RedHat 5.0), entonces esta versión vieja puede ser utilizada. Ahora (asumiendo que tienes un sistema RedHat), utiliza el comando rpm -e gtk gtk-devel También puedes querer eliminar los paquetes que dependen de gtk (rpm te dirá cuales son). Si no tienes un sistema RedHat Linux, asegúrate que ni /usr/lib ni /usr/local/lib contienen algunas de las librerías libgtk, libgdk, libglib, o libgck. En caso de que existan, elimínalas (y cualquier archivo include de gtk, como /usr/include/gtk y /usr/include/gdk) y reinstala gtk. Cuando compilo programas con GTK+ obtengo mensajes de error del compilador sobre no ser capaz de encontrar <literal>glibconfig.h</literal>. El archivo de encabezado "glibconfig.h" fue movido al directorio $exec_prefix/lib/glib/include/. $exec_prefix es el directorio que era especificado al dar la bandera --exec-prefix a ./configure cuando se compiló GTK+. Su valor por omisión es $prefix, (especificado con --prefix), cuyo valor por omisión es /usr/local. Esto fue hecho porque "glibconfig.h" incluye información dependiente de la arquitectura, y el resto de los archivos include son colocados en $prefix/include, que puede ser compartido entre distintas arquitecturas. GTK+ incluye un archivo de comandos, /gtk-config/, que hace fácil el encontrar los caminos correctos de inclusión. El Tutorial GTK+ incluye un ejemplo de como utilizar /gtk-config/ para compilaciones simples desde la línea de comandos. Para información sobre configuraciones más complicadas, vea el archivo docs/gtk-config.txt en la distribución GTK+. Si está intentando compilar un programa viejo, puede resolver el problema configurándolo con una línea de comando como: setenv CPPFLAGS "-I/usr/local/include/glib/include" ./configure (Sustituya el valor apropiado de $exec_prefix para /usr/local.) Cuando instalo una aplicación GTK+, <foreignphrase>configure</foreignphrase> reporta que no puede encontrar a GTK. Hay varias razones comunes para esto: Tienes una versión vieja de GTK instalada en algún lugar. RedHat 5.0, por ejemplo, instala una copia vieja de GTK que no va a funcionar con las aplicaciones nuevas. Debes remover esta copia vieja, pero ten en cuenta que esto hará fallar las aplicaciones del control-panel. gtk-config (o algún otro componente de GTK) no está en la ruta de búsqueda, o hay una versión vieja en el sistema. Teclea: gtk-config --version para revisar ambos casos. Si regresa un valor diferente de lo que esperas, entonces tienes una versión vieja de GTK en tu sistema. El archivo de comandos ./configure no puede encontrar las librerías GTK. Ver la pregunta de arriba para obtener ayuda en esto. Si ninguna de las anteriores te sirve de ayuda, entonces revisa en config.log, el cual es generado por ./configure a medida que corre. Al final encontrarás la última acción que tomó antes de fallar. Si es una sección de código fuente, copia el código fuente a un archivo y compilalo con la línea justo encima de él en config.log. Si la compilación es exitosa, intenta ejecutarlo. Desarrollo de GTK+ ¿Qué es esta cosa CVS de la cual todo el mundo habla, y cómo puedo acceder? CVS es el Sistema de Versión Concurrente, y es una manera muy popular para controlar las versiones de proyectos de software. Está diseñado para permitir que autores múltiples operen simultáneamente en el mismo árbol fuente. Este árbol fuente se mantiene de manera central, pero cada desarrollador tiene un espejo local de este repositorio en donde hacen los cambios. Los desarrolladores de GTK+ utilizan un repositorio CVS para guardar la copia maestra de la versión actual en desarrollo de GTK+. Como tal, quien quiera contribuir parches a GTK+, los deben generar contra la versión CVS. La gente normal debe utilizar los lanzamientos en paquete. El conjunto de herramientas de CVS está disponible como paquetes RPM, en los lugares usuales de RedHat. La última versión está disponible en http://download.cyclic.com/pub/ Cualquiera puede bajar la última versión CVS de GTK+, utilizando el acceso anónimo siguiendo estos pasos: En una línea de comandos descendiente de bourne (como bash) teclea: CVSROOT=':pserver:anonymous@anoncvs.gnome.org:/cvs/gnome' export CVSROOT A continuación, la primera vez que se saca el árbol de desarrollo, se necesita un login cvs. cvs login Te preguntará por un clave. No hay clave para cvs.gimp.org, así que solo presiona retorno de carro. Para conseguir el árbol y colocarlo en un subdirectorio de tu directorio de trabajo actual, teclea el comando: cvs -z3 get gtk+ Ten en cuenta que con el árbol GTK+ 1.1, glib se ha movido a un módulo CVS separado, de manera que si no tienes glib instalado, también deberás obtenerlo: cvs -z3 get glib ¿Cómo puedo contribuir a GTK+? Es simple. Si algo no trabajo como crees que debería en un programa, revisa la documentación para asegurarte que no te hace falta algo. Si es un verdadero bug o funcionalidad perdida, rastrealo en el fuente de GTK+, cámbialo, y entonces genera un parche en la forma de un 'context diff'. Esto se puede hacer utilizando un comando como diff -ru <oldfile> <newfile>. Entonces sube el parche a: ftp://ftp.gtk.org/incoming junto con un archivo README. ¡Asegúrate que sigues las convenciones de nombramiento o tu parche simplemente será borrado! Los nombres de archivo deben ser de esta forma: gtk<nombreusuario>-<date yymmdd-n>.patch.gz gtk-<nombreusuario>-<date yymmdd-n>.patch.README La "n" en la fecha indica un número único (empezando de 0) de parches que has subido ese día. Debe ser 0, a menos que subas más de un parche en el mismo día. Ejemplo: gtk-gale-982701-0.patch.gz gtk-gale-982701-0.patch.README Una vez que subas lo que sea, envía el README a ftp-admin@gtk.org ¿Cómo puedo saber si mi parche fue aplicado, y si no, porqué razón no lo fue? Los parches subidos serán movidos a ftp://ftp.gtk.org/pub/gtk/patches, en donde alguien del equipo de desarrollo de GTK+ los recogerá. Si se aplican, se moverán a /pub/gtk/patches/old. Los parches que no se aplican, por cualquier razón, son movidos a /pub/gtk/patches/unapplied o /pub/gtk/patches/outdated. En este punto puedes preguntar en la lista de correo gtk-list el por qué tu parche no fue aplicado. Hay muchas razones posibles por las cuales un parche puede no ser aplicado, desde que no se aplica limpiamente, a no ser correcto. No te sientas mal si tu parche no lo logró la primera vez. ¿Cuál es la política sobre la incorporación de nuevos widgets a la librería? Esto le compete a los autores, así que deberás preguntarles una vez que hayas terminado con tu widget. Como guía general, los widgets que son generalmente útiles, trabajan, y no son una desgracia al conjunto de widget, serán incluidos agradecidamente. ¿Hay alguien trabajando en <foreignphrase>bindings</foreignphrase> para otros lenguajes además de C? La página hogar de GTK+ (http://www.gtk.org/) presenta una lista de bindings para GTK+. Hay varios envoltorios de C++ para GTK+. el paquete gtk--, el cual es un envoltorio bastante pequeño para GTK+. Puedes encontrar su página hogar en http://www.cs.tut.fi/~p150650/gtk/gtk--.html. El sitio FTP es ftp://ftp.gtk.org/pub/gtk/gtk--. el paquete VDK, el cual fue construido como el paquete base de un constructor de aplicaciones GTK+ parecido a Borland. La página hogar se encuentra en http://www.guest.net/homepages/mmotta/VDKHome. El paquete wxWindows/Gtk, que es una librería C++ para desarrollo de interfaces gráficas de usuario en plataformas cruzadas. La página hogar de este paquete se encuentra en http://www.freiburg.linux.de/~wxxt/. Hay tres bindings conocidos para Objective-c actualmente en desarrollo: El paquete seleccionado http://www.gnome.org/ es objgtk. Objgtk está basado en la clase Object y es mantenido por Elliot Lee. Aparentemente, objgtk está siendo aceptado como el binding Objective-c `estándar' para GTK+. Si te inclinas más al proyecto GNUstep, puede que quieras revisar GTKKit por Helge Heß. La intención es crear un binding GTK+ utilizando el FoundationKit. GTKKit incluye cosas agradables como escribir un archivo plantilla tipo XML para construir una interface GTK+. El paquete GToolKit, que puede encontrarse en ftp://ftp.gtk.org/pub/gtk/objc-gtoolkit/. Bindings para Perl ftp://ftp.gtk.org/pub/gtk/perl Bindings para Guile. La página hogar se encuentra en http://www.ping.de/sites/zagadka/guile-gtk. Es importante mencionar que Guile es la implementación de Scheme R4RS del Proyecto GNU (el estándar). Si te gusta Scheme, querrás darle un vistazo a esto. David Monniaux reporta: Inicié un sistema de binding gtk-O'Caml. Lo básico del sistema, incluyendo callbacks, trabaja bien. El desarrollo actual se encuentra en http://www.ens-lyon.fr/~dmonniau/arcs Se han hecho varios bindings de Python: pygtk está en http://www.daa.com.au/~james/pygtk y ftp://ftp.gtk.org/pub/gtk/python python-gtk está en http://www.ucalgary.ca/~nascheme/python-gtk Hay un par de widgets OpenGL/Mesa disponibles para GTK+. Sugiero que empieces en http://www.student.oulu.fi/~jlof/gtkglarea/index.html Por último, hay varios bindings para otros lenguajes más como Eiffel, TOM, Pascal, Pike, etc. Desarrollando con GTK+: primeros pasos ¿Cómo empiezo? Después de haber instalado GTK+, hay un par de cosas que pueden ayudarte a empezar a desarrollar aplicaciones con él. Está el Tutorial GTK+ http://www.gtk.org/tutorial/, el cual está en desarrollo. Esto te introducirá a la escritura de aplicaciones utilizando C. El Tutorial no contiene (todavía) información en todos los widgets que existen en GTK+. Para código de ejemplo en como utilizar lo básico de todos los widgets GTK+, debes ver el archivo gtk/testgtk.c (y archivos fuentes asociados) dentro de la distribución. Revisar la distribución te dará una buena base sobre que pueden hacer los widgets. Traté de compilar un pequeño <command>Hola Mundo</command> propio, pero falló. ¿Alguna pista? Ya que eres bueno codificando, no lidiaremos con errores en tiempo de compilación :) La línea de comando clásica para compilar un programa basado en GTK+ es gcc -o myprog [c files] `gtk-config --cflags --libs` Debes darte cuenta del carácter de comilla hacia atrás que es utilizado en esta línea de comando. Un error común cuando inicias un desarrollo basado en GTK+, es utilizar comillas en lugar de comillas hacia atrás. Si lo haces, el compilador se quejará sobre un archivo desconocido llamado gtk-config --cflags --libs. El texto en comillas hacia atrás es una instrucción a tu procesador de línea de comando para substituir la salida de la ejecución de este texto en la línea de comando. La línea de comando de arriba se asegura de: se utilizarán las opciones correctas para el compilador C para compilar el programa (incluyendo la lista completa de encabezados C) tu programa será enlazado con las librerías necesarias. ¿Qué hay sobre el uso de la utilidad <command>make</command>? Este es un archivo make de ejemplo que compila un programa basado en GTK+: # archivo make para aplicación básica GTK+ SOURCES = myprg.c foo.c bar.c OBJS = ${SOURCES:.c=.o} CFLAGS = `gtk-config --cflags` LDADD = `gtk-config --libs` CC = gcc PACKAGE = myprg all : ${OBJS} ${CC} -o ${PACKAGE} ${OBJS} ${LDADD} .c.o: ${CC} ${CFLAGS} -c $< # fin del archivo Para más información sobre la utilidad make, debes leer la página man o la página info relevante. Utilizo las comillas hacia atrás en mis archivos make, pero mi proceso make falló. La construcción de comillas hacia atrás parece no ser aceptada por algunas utilidades make viejas. Si utilizas alguna de estas, el proceso make probablemente fallará. Para poder tener la sintaxis de comillas hacia atrás funcionando, debes utilizar la utilidad GNU make ( consíguela en el servidor ftp de GNU en ftp://ftp.gnu.org/). Quiero agregar algunas cosas de <command>configure</command>. ¿Cómo puedo hacer esto? Para utilizar autoconf/automake, primero debes instalar los paquetes relevantes. Estos son: el preprocesado m4 v1.4 o mejor autoconf v2.13 o mejor automake v1.4 o mejor Encontrarás estos paquetes en el servidor principal de ftp de GNU (ftp://ftp.gnu.org/) o en cualquier espejo GNU. Para poder utilizar el poderoso esquema autoconf/automake, debes crear un configure.in que puede lucir como: dnl Procesa este archivo con autoconf para producir un configure script. dnl configure.in para un programa basado en GTK+ AC_INIT(myprg.c)dnl AM_INIT_AUTOMAKE(mypkgname,0.0.1)dnl AM_CONFIG_HEADER(config.h)dnl dnl Chequea programas AC_PROG_CC dnl chequea el compilador c dnl deberías agregar CFLAGS="" aquí, porque es fijado a -g por PROG_CC dnl Chequea librerías. AM_PATH_GTK(1.2.0,,AC_MSG_ERROR(mypkgname 0.1 needs GTK))dnl AC_OUTPUT( Makefile )dnl Debes agregar un archivo Makefile.am: bin_PROGRAMS = myprg myprg_SOURCES = myprg.c foo.c bar.c INCLUDES = @GTK_CFLAGS@ LDADD = @GTK_LIBS@ CLEANFILES = *~ DISTCLEANFILES = .deps/*.P Si tu proyecto contiene más de un subdirectorio, deberás crear un Makefile.am en cada directorio además de un Makefile.am maestro que lucirá como: SUBDIRS = midir1 midir2 midir3 entonces, para utilizar estos, simplemente teclea lo siguientes comandos: aclocal autoheader autoconf automake --add-missing --include-deps --foreign Para más información, deberías revisar la documentación de autoconf y automake (los archivos info incluidos son bastante fáciles de entender, y hay una gran cantidad de recursos en el web que lidian con autoconf y automake). Trato de eliminar errores en mi aplicación GTK+ con gdb, pero cuelga mi servidor X cuando llego a algún punto de parada. ¿Alguna idea? De Federico Mena Quintero: X no esta trancado. Lo más seguro es que estás llegando a un punto de parada dentro de un callback llamado desde un lugar en Gtk que tiene el ratón agarrado. Corre tu programa con la opción --sync; hará más fácil la eliminación de errores. También, puede que quieras utilizar la consola para correr el debugger, y simplemente dejes el programa corriendo en otra consola con el servidor X. Eric Mouw tiene otra solución: Una terminal vieja conectada a un puerto serie sin usar, también es grandioso para eliminar errores en programas X. Terminales viejas vt100/vt220 son baratísimas pero un poco difíciles de conseguir (aquí en los Países Bajos, YMMV Nota del traductor: YMMV == Tu Millaje Puede Variar) Desarrollo con GTK+: preguntas generales ¿Qué widgets hay en GTK+? El Tutorial GTK+ lista los siguientes widgets: GtkObject +GtkData | +GtkAdjustment | `GtkTooltips `GtkWidget +GtkContainer | +GtkBin | | +GtkAlignment | | +GtkEventBox | | +GtkFrame | | | `GtkAspectFrame | | +GtkHandleBox | | +GtkItem | | | +GtkListItem | | | +GtkMenuItem | | | | `GtkCheckMenuItem | | | | `GtkRadioMenuItem | | | `GtkTreeItem | | +GtkViewport | | `GtkWindow | | +GtkColorSelectionDialog | | +GtkDialog | | | `GtkInputDialog | | `GtkFileSelection | +GtkBox | | +GtkButtonBox | | | +GtkHButtonBox | | | `GtkVButtonBox | | +GtkHBox | | | +GtkCombo | | | `GtkStatusbar | | `GtkVBox | | +GtkColorSelection | | `GtkGammaCurve | +GtkButton | | +GtkOptionMenu | | `GtkToggleButton | | `GtkCheckButton | | `GtkRadioButton | +GtkCList | `GtkCTree | +GtkFixed | +GtkList | +GtkMenuShell | | +GtkMenuBar | | `GtkMenu | +GtkNotebook | +GtkPaned | | +GtkHPaned | | `GtkVPaned | +GtkScrolledWindow | +GtkTable | +GtkToolbar | `GtkTree +GtkDrawingArea | `GtkCurve +GtkEditable | +GtkEntry | | `GtkSpinButton | `GtkText +GtkMisc | +GtkArrow | +GtkImage | +GtkLabel | | `GtkTipsQuery | `GtkPixmap +GtkPreview +GtkProgressBar +GtkRange | +GtkScale | | +GtkHScale | | `GtkVScale | `GtkScrollbar | +GtkHScrollbar | `GtkVScrollbar +GtkRuler | +GtkHRuler | `GtkVRuler `GtkSeparator +GtkHSeparator `GtkVSeparator ¿Es GTK+ seguro con hilos? ¿Cómo escribo aplicaciones multi-hilo con GTK+? La librería GLib puede ser utilizada en un modo seguro-con-hilos, al llamar g_thread_init() antes de hacer cualquier otra llamada GLib. En este modo GLib tranca automáticamente todas las estructuras de datos internas que necesite trancar. Esto no significa que dos hilos puedan acceder simultáneamente, por ejemplo, un tabla hash, pero pueden acceder dos tablas hash diferentes simultáneamente. Si dos hilos distintos necesitan acceder la misma tabla hash, la aplicación es responsable de asegurarse a sí misma. Cuando GLib se inicializa en modo seguro-con-hilos, GTK+ esta atento a los hilos. Hay una cerradura global que debes obtener con gdk_threads_enter() antes de hacer cualquier llamada GDK, y debes liberar con gdk_threads_leave(). Un programa main mínimo para una aplicación GTK+ con hilos luce como: int main (int argc, char *argv[]) { GtkWidget *window; g_thread_init(NULL); gtk_init(&argc, &argv); window = create_window(); gtk_widget_show(window); gdk_threads_enter(); gtk_main(); gdk_threads_leave(); return(0); } Los callbacks requieren un poco de atención. Los callbacks de (señales) GTK+ se hacen dentro de la cerradura GTK+. Sin embargo, las llamadas desde GLib (timeouts, IO callbacks, y funciones inactivas) se hacen fuera de la cerradura GTK+. Así que, dentro de un manejador de señales no necesitas llamar a gdk_threads_enter(), pero desde los otros tipos de callbacks, si tienes que hacerlo. Erik Mouw contribuyó el siguiente código de ejemplo para ilustrar cómo utilizar hilos dentro de programas GTK+. /*------------------------------------------------------------------------- * Nombre de archivo: gtk-thread.c * Versión: 0.99.1 * Copyright: Copyright (C) 1999, Erik Mouw * Autor: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> * Descripción: Ejemplo de hilos GTK. * Creado en: Dom 17 Oct 21:27:09 1999 * Modificado por: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> * Modificado en: Dom 24 Oct 17:21:41 1999 *-----------------------------------------------------------------------*/ /* * Compilar con: * * cc -o gtk-thread gtk-thread.c `gtk-config --cflags --libs gthread` * * Gracias a Sebastian Wilhelmi y Owen Taylor por señalar algunos * bugs. * */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <gtk/gtk.h> #include <glib.h> #include <pthread.h> #define YES_IT_IS (1) #define NO_IT_IS_NOT (0) typedef struct { GtkWidget *label; int what; } yes_or_no_args; G_LOCK_DEFINE_STATIC (yes_or_no); static volatile int yes_or_no = YES_IT_IS; void destroy(GtkWidget *widget, gpointer data) { gtk_main_quit(); } void *argument_thread(void *args) { yes_or_no_args *data = (yes_or_no_args *)args; gboolean say_something; for(;;) { /* duerme un rato */ sleep(rand() / (RAND_MAX / 3) + 1); /* trancar la yes_or_no_variable */ G_LOCK(yes_or_no); /* ¿tenemos que decir algo? */ say_something = (yes_or_no != data->what); if(say_something) { /* asignar la variable */ yes_or_no = data->what; } /* Soltar la variable yes_or_no */ G_UNLOCK(yes_or_no); if(say_something) { /* conseguir la cerradura GTK */ gdk_threads_enter(); /* asignar el texto de la etiqueta */ if(data->what == YES_IT_IS) gtk_label_set_text(GTK_LABEL(data->label), "O yes, it is!"); else gtk_label_set_text(GTK_LABEL(data->label), "O no, it isn't!"); /* soltar la cerradura del hilo GTK */ gdk_threads_leave(); } } return(NULL); } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *label; yes_or_no_args yes_args, no_args; pthread_t no_tid, yes_tid; /* inicializar hilos */ g_thread_init(NULL); /* inicializar gtk */ gtk_init(&argc, &argv); /* inicializar generador de números aleatorios */ srand((unsigned int)time(NULL)); /* crear una ventana */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(destroy), NULL); gtk_container_set_border_width(GTK_CONTAINER (window), 10); /* crear una etiqueta */ label = gtk_label_new("And now for something completely different ..."); gtk_container_add(GTK_CONTAINER(window), label); /* mostrar todo */ gtk_widget_show(label); gtk_widget_show (window); /* crear los hilos */ yes_args.label = label; yes_args.what = YES_IT_IS; pthread_create(&yes_tid, NULL, argument_thread, &yes_args); no_args.label = label; no_args.what = NO_IT_IS_NOT; pthread_create(&no_tid, NULL, argument_thread, &no_args); /* entrar al ciclo principal de GTK */ gdk_threads_enter(); gtk_main(); gdk_threads_leave(); return(0); } ¿Por qué ocurre este extraño error 'x io error' cuando hago un <literal>fork()</literal> en mi aplicación GTK+ Este no es en realidad un problema de GTK+, y el problema no está relacionado tampoco con fork(). Si ocurre 'x io error', entonces probablemente estás usando la función exit() para salir de un proceso hijo. Cuando GDK abre una pantalla X, crea un descriptor de archivo de socket. Cuando utilizas la función exit(), implícitamente cierras todos los descriptores de archivos abiertos, y esto no le agrada a la librería X subyacente. La función correcta a utilizar en este caso es _exit(). Erik Mouw contribuyó el siguiente código de ejemplo para ilustrar el manejo de fork() y exit(). /*------------------------------------------------------------------------- * Nombre de archivo: gtk-fork.c * Versión: 0.99.1 * Copyright: Copyright (C) 1999, Erik Mouw * Autor: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> * Descripción: Ejemplo de fork en GTK+ * Creado en: Jue 23 Sep 21:37:55 1999 * Modificado por: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> * Modificado en: Jue 23 Sep 22:39:39 1999 *-----------------------------------------------------------------------*/ /* * Compilar con: * * cc -o gtk-fork gtk-fork.c `gtk-config --cflags --libs` * */ #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <gtk/gtk.h> void sigchld_handler(int num) { sigset_t set, oldset; pid_t pid; int status, exitstatus; /* blockear otras señales SIGCHLD que vengan */ sigemptyset(&set); sigaddset(&set, SIGCHLD); sigprocmask(SIG_BLOCK, &set, &oldset); /* esperar por el hijo */ while((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) { if(WIFEXITED(status)) { exitstatus = WEXITSTATUS(status); fprintf(stderr, "Parent: child exited, pid = %d, exit status = %d\n", (int)pid, exitstatus); } else if(WIFSIGNALED(status)) { exitstatus = WTERMSIG(status); fprintf(stderr, "Parent: child terminated by signal %d, pid = %d\n", exitstatus, (int)pid); } else if(WIFSTOPPED(status)) { exitstatus = WSTOPSIG(status); fprintf(stderr, "Parent: child stopped by signal %d, pid = %d\n", exitstatus, (int)pid); } else { fprintf(stderr, "Parent: child exited magically, pid = %d\n", (int)pid); } } /* re-instalar el manejador de señal (algunos sistemas necesitan esto) */ signal(SIGCHLD, sigchld_handler); /* y desbloquearlo */ sigemptyset(&set); sigaddset(&set, SIGCHLD); sigprocmask(SIG_UNBLOCK, &set, &oldset); } gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) { return(FALSE); } void destroy(GtkWidget *widget, gpointer data) { gtk_main_quit(); } void fork_me(GtkWidget *widget, gpointer data) { pid_t pid; pid = fork(); if(pid == -1) { /* ouch, falló el fork() */ perror("fork"); exit(-1); } else if(pid == 0) { /* hijo */ fprintf(stderr, "Child: pid = %d\n", (int)getpid()); execlp("ls", "ls", "-CF", "/", NULL); /* si exec() retorna, hay algo malo */ perror("execlp"); /* salir del hijo. nota el uso de _exit() en lugar de exit() */ _exit(-1); } else { /* padre */ fprintf(stderr, "Parent: forked a child with pid = %d\n", (int)pid); } } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *button; gtk_init(&argc, &argv); /* las cosas básicas: hacer una ventana y colocar callbacks para destruír y * borrar eventos */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC(delete_event), NULL); gtk_signal_connect(GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(destroy), NULL); #if (GTK_MAJOR_VERSION == 1) && (GTK_MINOR_VERSION == 0) gtk_container_border_width(GTK_CONTAINER (window), 10); #else gtk_container_set_border_width(GTK_CONTAINER (window), 10); #endif /* agregar un botón para hacer algo útil */ button = gtk_button_new_with_label("Fork me!"); gtk_signal_connect(GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC(fork_me), NULL); gtk_container_add(GTK_CONTAINER(window), button); /* mostrar todo */ gtk_widget_show (button); gtk_widget_show (window); /* instalar un manejador de señal para señales SIGCHLD */ signal(SIGCHLD, sigchld_handler); /* ciclo principal */ gtk_main (); exit(0); } ¿Por qué el contenido de un botón no se mueve cuando el botón se presiona? Aquí les tengo preparado un parche para que funcione de esa manera... De: Peter Mattis La razón por la cual los botones no mueven su hijo hacia abajo y la derecha cuando son presionados es porque no me parece que eso es lo que está ocurriendo visualmente. Mi visión de los botones es que los estás mirando en línea recta. Osea que, la interface del usuario está en un plano y tú estás encima de este mirándolo recto. Cuando un botón es oprimido se mueve directamente lejos de ti. Para ser absolutamente correcto yo supongo que el hijo debería encogerse un poquito. Pero no veo por que el hijo se debería mover hacia abajo y a la izquierda. Recuerda, se supone que el hijo está pegado a la superficie del botón. No es bueno que aparezca como si el hijo resbalara en la superficie del botón. En una nota más práctica, yo sí implemente esto en algún momento y decidí que no se veía bien y lo eliminé. ¿Cómo identifico la ventana superior de un widget u otro ancestro? Hay un par de maneras de encontrar el padre superior de un widget. La más fácil es llamar la función gtk_widget_top_level() que devuelve un puntero a GtkWidget que es la ventana superior. Una manera más complicada de hacer esto (pero menos limitada, ya que permite al usuario obtener el ancestro más cercano de un tipo conocido) es utilizar gtk_widget_get_ancestor() como en: GtkWidget *widget; widget = gtk_widget_get_ancestor(w, GTK_TYPE_WINDOW); Ya que virtualmente todos los GTK_TYPEs pueden ser usados como el segundo parámetro de esta función, puedes obtener cualquier widget padre de un widget en particular. Supón que tienes un hbox que contiene un vbox, que en su lugar contiene algún otro widget atómico (entry, label, etc. Para encontrar el hbox maestro utilizando el widget entry simplemente usa: GtkWidget *hbox; hbox = gtk_widget_get_ancestor(w, GTK_TYPE_HBOX); ¿Cómo consigo el ID de Ventana de un GtkWindow La ventana Gdk/X se creará cuando el widget se realiza. Puedes conseguir el ID de Ventana con: #include <gdk/gdkx.h> Window xwin = GDK_WINDOW_XWINDOW (GTK_WIDGET (my_window)->window); ¿Cómo atrapo un evento de doble click (por ejemplo, en un widget de lista)? Tim Janik envió a gtk-list (un poco modificado): Definición de un manejador de señal: gint signal_handler_event(GtkWiget *widget, GdkEvenButton *event, gpointer func_data) { if (GTK_IS_LIST_ITEM(widget) && (event->type==GDK_2BUTTON_PRESS || event->type==GDK_3BUTTON_PRESS) ) { printf("I feel %s clicked on button %d\", event->type==GDK_2BUTTON_PRESS ? "double" : "triple", event->button); } return FALSE; } Y conecta el manejador a tu objeto: { /* lista, asuntos de inicialización de lista*/ gtk_signal_connect(GTK_OBJECT(list_item), "button_press_event", GTK_SIGNAL_FUNC(signal_handler_event), NULL); /* y/o */ gtk_signal_connect(GTK_OBJECT(list_item), "button_release_event", GTK_SIGNAL_FUNC(signal_handler_event), NULL); /* algo más */ } y, Owen Taylor escribió: Ten en cuenta que presionar un botón será recibido de antemano, y si estás haciendo esto para un botón, por lo tanto obtendrás una señal "clicked" para el botón. (Esto es cierto para cualquier juego de herramientas, ya que las computadoras no son buenas leyendo la mente.) ¿Cuáles son las diferencias entre señales y eventos? Antes que nada, Havoc Pennington da una descripción bastante completa de las diferencias entre eventos y señales en su libro libre (puede encontrar dos capítulos en http://www106.pair.com/rhp/sample_chapters.html). Además, Havoc envió esto a la gtk-list Los eventos son un flujo de mensajes recibido del servidor X. Ellos manejan el ciclo main de Gtk; que más o menos es "esperar por eventos, procesarlos" (no exactamente, es en realidad más general que eso y puede esperar en varios flujos de entrada distintos al mismo tiempo). Los eventos son un concepto de Gdk/Xlib Las señales son una característica de GtkObject y su subclases. No tienen nada que ver con algún flujo de entrada; en realidad, una señal es solo una manera de mantener una lista de callbacks a mano e invocarlos ("emitir" la señal). Hay una gran cantidad de detalles y características extras. Las señales son emitidas por instancias de objetoc, y no tienen relación con el ciclo main Gtk. Convencionalmente, las señales son emitidas "cuando algo cambia" en los objetos que emiten la señal. Las señales y los eventos solo se juntan porque sucede que GtkWidget emite señales cuando recibe eventos. Esto es puramente una conveniencia, de manera que puedas conectar callbacks para que sean invocadas cuando un widget en particular recibe un evento particular. No hay nada sobre esto que haga los eventos y las señales conceptos inherentemente relacionados, no más que emitir una señal cuando presionas un botón hace el presionar y la señal conceptos relacionados. Los datos que paso a <literal>delete_event</literal> o a otro manejador de eventos, se corrompe. Todos los manejadores de eventos toman un argumento adicional que contiene información sobre el evento que disparó al manejador. De manera que, un manejador delete_event debe ser declarado como: gint delete_event_handler (GtkWidget *widget, GdkEventAny *event, gpointer data); Tengo mi señal conectada al evento (lo que sea), pero parece que no lo atrapa. ¿Qué está mal? Para atrapar algunos eventos en particular se debe hacer una inicialización especial. De hecho, debes colocar la máscara de bit de evento correcta de tu widget antes de obtener algún evento particular. Por ejemplo, gtk_widget_add_events(window, GDK_KEY_RELEASE_MASK); te permite atrapar eventos de soltar tecla. Si quieres atrapar todos los eventos, simplemente utiliza la máscara de eventos GDK_ALL_EVENTS_MASK. Todas las máscaras de eventos se definen en el archivo gdktypes.h. Necesito agregar una nueva señal a un widget GTK+. ¿Alguna idea? Si la señal que quieres agregar puede ser beneficiosa para otros usuarios GTK+, puede que quieras enviar un parche que presente tus cambios. Chequea el tutorial para más información sobre cómo agregar señales a una clase widget. Si no crees que ese sea el caso, o si tu parche no es aplicado, tendrás que usar la función gtk_object_class_user_signal_new. gtk_object_class_user_signal_new te permite agregar una nueva señal a un widget GTK+ predefinido sin modificación alguna al código fuente de GTK+. La nueva señal puede ser emitida con gtk_signal_emit y puede ser manejada en la misma forma que otras señales. Tim Janik puso este pedacito de código: static guint signal_user_action = 0; signal_user_action = gtk_object_class_user_signal_new (gtk_type_class (GTK_TYPE_WIDGET), "user_action", GTK_RUN_LAST | GTK_RUN_ACTION, gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); void gtk_widget_user_action (GtkWidget *widget, gpointer act_data) { g_return_if_fail (GTK_IS_WIDGET (widget)); gtk_signal_emit (GTK_OBJECT (widget), signal_user_action, act_data); } Si quieres que tu nueva señal tenga algo más que el parámetro clásico gpointer, tendrás que jugar con los marshallers de GTK+. ¿Es posible desplegar cierto texto el cual sea truncado para que quepa dentro de su destino? El comportamiento de GTK (sin recorte) es una consecuencia de sus intentos de conservar recursos X. Los widgets etiqueta (entre otros) no tienen su propia ventana X - simplemente dibujan su contenido en su ventana padre. Aunque es posible el hacer recorte colocando la máscara de recorte antes de dibujar el texto, esto probablemente causaría una penalidad substancial en el desempeño. Es posible que, a largo plazo, la mejor solución a esos problemas simplemente sea el cambiar gtk para que le de ventanas X a las etiquetas. Una solución a corto plazo es poner el widget de etiqueta dentro de otro widget que si obtiene su propia ventana - una candidato posible puede ser el widget puerto de vista. viewport = gtk_viewport (NULL, NULL); gtk_widget_set_usize (viewport, 50, 25); gtk_viewport_set_shadow_type (GTK_VIEWPORT(viewport), GTK_SHADOW_NONE); gtk_widget_show(viewport); label = gtk_label ("a really long label that won't fit"); gtk_container_add (GTK_CONTAINER(viewport), label); gtk_widget_show (label); Si hicieras esto para un montón de widgets, podrías querer copiar gtkviewport.c y arrancar la funcionalidad de ajuste y sobra (tal vez lo podrías llamar GtkClipper). ¿Cómo hago que mi ventana sea modal? / ¿Cómo hago que una sola ventana esté activa? Después de crear tu ventana, haz un gtk_grab_add(my_window). Y después de cerrar la ventana haz un gtk_grab_remove(my_window). ¿Por qué mi widget (eje. barra de progreso) no se actualiza? Probablemente estás haciendo todos los cambios dentro de una función sin devolver el control a gtk_main(). Este puede ser el caso si haces algún calculo largo en tu código. La mayoría de las actualizaciones de dibujo solo se ponen en una cola, la cual es procesada dentro de gtk_main(). Puedes forzar el procesamiento de la cola de dibujo utilizando algo como esto: while (g_main_iteration(FALSE)); dentro de la función que cambia el widget. Lo que el pedacito anterior hace es correr todos los eventos pendientes y funciones ociosas de alta prioridad, para luego regresar inmediatamente (el dibujado se hace en una función ociosa de alta prioridad). ¿Cómo pego datos a algún objeto/widget GTK+? Antes que nada, los datos pegados se almacenan en el campo object_data de un GtkObject. El tipo de este campo es GData, el cual se define en glib.h. Por lo tanto deberías leer muy cuidadosamente el archivo gdataset.c en tu directorio de los fuentes de glib. Hay dos maneras (fáciles) de pegar algún dato a un objeto gtk. Utilizando gtk_object_set_data() y gtk_object_get_data() parece ser la manera más común de hacer esto, ya que provee una interface poderosa para conectar objetos y datos. void gtk_object_set_data(GtkObject *object, const gchar *key, gpointer data); gpointer gtk_object_get_data(GtkObject *object, const gchar *key); Ya que un ejemplo corto es mejor que un discurso largo: struct my_struct p1,p2,*result; GtkWidget *w; gtk_object_set_data(GTK_OBJECT(w),"p1 data",(gpointer)&p1); gtk_object_set_data(GTK_OBJECT(w),"p2 data",(gpointer)&p2); result = gtk_object_get_data(GTK_OBJECT(w),"p1 data"); Las funciones gtk_object_set_user_data() y gtk_object_get_user_data() hacen los mismo que las funciones anteriores, pero no te permiten especificar el parámetro "key". En su lugar, utiliza una llave estándar llamada "user_data". Ten en cuenta que el uso de estas funciones se desaprueba en la versión 1.2. Simplemente proveen un modo de compatibilidad con algunos paquetes gtk viejos. ¿Cómo elimino los datos que he pegado a un objeto? Cuando pegas los datos al objeto, puedes usar la función gtk_object_set_data_full(). Los primeros tres argumentos de la función son los mismos que en gtk_object_set_data(). El cuarto es un puntero a una función callback que es llamada cuando los datos son destruidos. Los datos son destruidos cuando: destruyes el objeto reemplazas los datos con nuevos datos (con la misma llave) reemplazas los datos con NULL (con la misma llave) ¿Cómo doy un nuevo padre a un widget? La manera normal de dar un nuevo padre (eje. cambiar el dueño) a un widget debería ser utilizando la función: void gtk_widget_reparent (GtkWidget *widget, GtkWidget *new_parent) Pero esto es solo un "debería" ya que esta función no hace su trabajo correctamente en algunos widgets. El objetivo principal de gtk_widget_reparent() es evitar de-realizar un widget si tanto widget como new_parent están realizados (en este caso, widget->window tiene un cambio exitoso de padre). El problema aquí es que algunos widgets en la jerarquía GTK+ pegadas múltiples subventanas X y este es notablemente el caso para el widget GtkSpinButtton. Para esos, gtk_widget_reparent() fallará dejando un hijo sin realizar donde no debería. Para evitar este problema, simplemente utiliza el siguiente pedacito de código: gtk_widget_ref(widget); gtk_container_remove(GTK_CONTAINER(old_parent), widget); gtk_container_add(GTK_CONTAINER(new_parent), widget); gtk_widget_unref(widget); ¿Cómo puedo obtener la posición de cualquier widget? Tal como Tim Janik señaló, hay casos distintos, y cada caso requiere una solución diferente. Si quieres la posición de un widget en relación con su padre, debes usar widget->allocation.x y widget->allocation.y. Si quieres la posición de una ventana en relación con la ventana raíz de X, debes usar gdk_window_get_geometry() gdk_window_get_position() o gdk_window_get_origin(). Si quieres obtener la posición de la ventana (incluyendo las decoraciones del Administrador de Ventana), debes usar gdk_window_get_root_origin(). Por último pero no menos importante, si quieres obtener la posición de un marco del Administrador de Ventana, debes usar gdk_window_get_deskrelative_origin(). Tu elección de Administrador de Ventana tendrá un efecto en los resultados de las funciones anteriores. Debes tener esto en mente cuando escribas tu aplicación. Esto es dependiente de como los Administradores de Ventana manejan las decoraciones que agregan alrededor de las ventanas. ¿Cómo asigno el tamaño de una ventana/widget? ¿Cómo prevengo que el usuario cambie el tamaño de mi ventana? La función gtk_widget_set_uposition() se usa para asignar la posición de cualquier widget. La función gtk_widget_set_usize() se usa para asignar el tamaño de un widget. Para poder usar todas las características que esta función provee cuando actúa en una ventana, querrás utilizar la función gtk_window_set_policy. Las definiciones de estas funciones son: void gtk_widget_set_usize (GtkWidget *widget, gint width, gint height); void gtk_window_set_policy (GtkWindow *window, gint allow_shrink, gint allow_grow, gint auto_shrink); Auto_shrink encogerá automáticamente la ventana cuando el tamaño solicitado del widget hijo vaya por debajo del tamaño actual de la ventana. Allow_shrink le dará al usuario la autorización de hacer la ventana más pequeña de lo que normalmente debería ser. Allow_grow le dará al usuario la posibilidad de hacer la ventana más grande. Los valores por defecto de estos parámetros son: allow_shrink = FALSE allow_grow = TRUE auto_shrink = FALSE La función gtk_widget_set_usize() no es la manera más fácil de asignar un tamaño de ventana ya que no puedes disminuir el tamaño de esta ventana con otra llamada a esta función, a menos que la llames dos veces, como en: gtk_widget_set_usize(your_widget, -1, -1); gtk_widget_set_usize(your_widget, new_x_size, new_y_size); Otra manera de asignar el tamaño y/o mover una ventana es usando la función gdk_window_move_resize() la cual suele trabajar bien tanto para crecer como para encoger la ventana: gdk_window_move_resize(window->window, x_pos, y_pos, x_size, y_size); ¿Cómo agrego un menú desplegable a mi aplicación GTK+? El ejemplo de menú en el directorio examples/menu de la distribución GTK+, implementa un menú desplegable con esta técnica: static gint button_press (GtkWidget *widget, GdkEvent *event) { if (event->type == GDK_BUTTON_PRESS) { GdkEventButton *bevent = (GdkEventButton *) event; gtk_menu_popup (GTK_MENU(widget), NULL, NULL, NULL, NULL, bevent->button, bevent->time); /* Decirle al código que llama que hemos manejado este evento; el * pase de carga termina aquí. */ return TRUE; } /* Decirle al código que llama que no hemos manejado este evento; * pasarlo */ return FALSE; } ¿Cómo deshabilito o habilito un widget, como el botón? Para deshabilitar (o habilitar) un widget, utiliza la función gtk_widget_set_sensitive(). El primer parámetro es tu puntero a widget. El segundo parámetro es un valor buleano: cuando es TRUE, el widget se habilita. ¿El argumento de texto en las funciones gtk_clist_* no debería ser declarado const? Por ejemplo: gint gtk_clist_prepend (GtkCList *clist, gchar *text[]); Repuesta: No, aunque un tipo "gchar*" (puntero a char) puede ser moldeado a un "const gchar*" (puntero a const char), esto no se aplica para "gchar *[]" (cadena de un número no especificado de punteros a char) a un "const gchar *[]" (cadena de un número no especificado de punteros a const char). El calificador de tipo "const" puede estar sujeto a moldeado automático, pero en el caso de la cadena, no es la cadena en sí la que necesita el molde calificado (const), si no sus miembros, por lo tanto cambiando todo el tipo. ¿Cómo dibujo pixels (datos de imagen) en la pantalla? Hay varias maneras de atacar esto. La manera más simple es utilizar GdkRGB, mira en gdk/gdkrgb.h. Puedes crear un buffer RGB, dibujar en buffer RGB, y entonces usar rutinas GdkRGB para copiar el buffer RGB a un widget área de dibujo o a un widget hecho por uno mismo. El libro "GTK+/Gome Application Development" da algunos detalles; GdkRBG también está documentado en la documentación de referencia de GTK+. Si estás escribiendo un juego u otro aplicación intensiva con las gráficas, puedes considerar una solución más elaborada. OpenGL es el estándar gráfico que te permitirá acceder a la aceleración hardware en versiones futuras de XFree86; así que, para máxima velocidad, probablemente te interese usar OpenGL. Un widget GtkGLArea está disponible para usar OpenGL en GTK+ (pero GtkGLArea no viene con GTK+). También hay varias librerías para juegos con código fuente abierto, tal como ClanLib y la librería SimpleDirectMedia Layer (SDL) de Loki's. NO deberías utilizar gdk_draw_point(), porque sería extremadamente lento. ¿Cómo puedo crear un pixmap sin tener que mostrar/realizar mi ventana? Funciones tal como gdk_pixmap_create_from_xpm() requieren una ventana válida como parámetro. Al transcurrir la fase de inicialización de una aplicación, una ventana válida puede no estar disponible sin mostrar una ventana, lo que puede no ser apropiado. Para evitar esto, se puede usar una función como gdk_pixmap_colormap_create_from_xpm(), como en: char *pixfile = "foo.xpm"; GtkWidget *top, *box, *pixw; GdkPixmap *pixmap, *pixmap_mask; top = gtk_window_new (GKT_WINDOW_TOPLEVEL); box = gtk_hbox_new (FALSE, 4); gtk_conainer_add (GTK_CONTAINER(top), box); pixmap = gdk_pixmap_colormap_create_from_xpm ( NULL, gtk_widget_get_colormap(top), &pixmap_mask, NULL, pixfile); pixw = gtk_pixmap_new (pixmap, pixmap_mask); gdk_pixmap_unref (pixmap); gdk_pixmap_unref (pixmap_mask); Desarrollo con GTK+: preguntas específicas sobre widgets ¿Cómo averiguo la selección de un GtkList? Obtén la selección con algo así: GList *sel; sel = GTK_LIST(list)->selection; Así es como GList está definido (citando glist.h): typedef struct _GList GList; struct _GList { gpointer data; GList *next; GList *prev; }; Una estructura GList es simplemente una estructura para listas doblemente enlazadas. En glib.h existen varias funciones g_list_*() para modificar una lista enlazada. Sin embargo, la selección GTK_LIST(MyGtkList)->selection es mantenida por las funciones gtk_list_*() y no debe ser modificada. El selection_mode del GtkList determina las facilidades de selección de un GtkList y por lo tanto los de GTK_LIST(AnyGtkList)->selection: selection_mode GTK_LIST()->selection contents GTK_SELECTION_SINGLE selection es NULL o contiene un puntero a GList* para un artículo singular seleccionado. GTK_SELECTION_BROWSE selection es NULL si la lista no contiene widgets, de otra manera contiene un puntero a GList* para una estructura GList. GTK_SELECTION_MULTIPLE selection es NULL si no hay artículos de la lista seleccionados o un puntero a GList* para el primer artículo seleccionado, que en su lugar apunta a una estructura GList para el segundo artículo seleccionado y continúa. GTK_SELECTION_EXTENDED selection es NULL. El campo de datos GTK_LIST(MyGtkList)->selection de la estructura GList apunta al primer GtkListItem que está seleccionado. Así que si quieres determinar que artículos de la lista están seleccionados deberías hacer algo como esto: { gchar *list_items[]={ "Item0", "Item1", "foo", "last Item", }; guint nlist_items=sizeof(list_items)/sizeof(list_items[0]); GtkWidget *list_item; guint i; list=gtk_list_new(); gtk_list_set_selection_mode(GTK_LIST(list), GTK_SELECTION_MULTIPLE); gtk_container_add(GTK_CONTAINER(AnyGtkContainer), list); gtk_widget_show (list); for (i = 0; i < nlist_items; i++) { list_item=gtk_list_item_new_with_label(list_items[i]); gtk_object_set_user_data(GTK_OBJECT(list_item), (gpointer)i); gtk_container_add(GTK_CONTAINER(list), list_item); gtk_widget_show(list_item); } } Para conocer sobre la selección: { GList *items; items=GTK_LIST(list)->selection; printf("Selected Items: "); while (items) { if (GTK_IS_LIST_ITEM(items->data)) printf("%d ", (guint) gtk_object_get_user_data(items->data)); items=items->next; } printf("\n"); } ¿Cómo evito que los encabezados de columna de un GtkCList desaparezcan cuando la lista se enrolla? Esto ocurre cuando una GtkCList se empaqueta en un GtkScrolledWindow usando la función gtk_scroll_window_add_with_viewport(). El método preferido para agregar un CList a una ventana que se enrolla es usando la función gtk_container_add, como en: GtkWidget *scrolled, *clist; char *titles[] = { "Title1" , "Title2" }; scrolled = gtk_scrolled_window_new(NULL, NULL); clist = gtk_clist_new_with_titles(2, titles); gtk_container_add(GTK_CONTAINER(scrolled), clist); No quiero que el usuario de mi aplicación introduzca texto en un GtkCombo. ¿Alguna idea? Un GtkCombo tiene una entrada asociada a la cual se puede acceder usando las expresiones siguientes: GTK_COMBO(combo_widget)->entry Si no quieres que el usuario pueda modificar el contenido de esta entrada, puedes usar la función gtk_entry_set_editable(): void gtk_entry_set_editable(GtkEntry *entry, gboolean editable); Asigna FALSE al parámetro editable para deshabilitar el tecleado en la entrada. ¿Cómo atrapo un cambio en un combo box? La entrada asociado con tu GtkCombo envía una señal "changed" cuando: se tecleada algún texto se cambia la selección del combo box Para atrapar cualquier cambio en el combo box, simplemente conecta tu manejador de señal con gtk_signal_connect(GTK_COMBO(cb)->entry, "changed", GTK_SIGNAL_FUNC(my_cb_change_handler), NULL); ¿Cómo puedo definir una línea de separación en un menú? Revisa el Tutorial para obtener información sobre cómo crear menús. Sin embargo, para crear una línea de separación en un menú, simplemente inserta un artículo vacío en el menú: menuitem = gtk_menu_item_new(); gtk_menu_append(GTK_MENU(menu), menuitem); gtk_widget_show(menuitem); ¿Cómo puedo darle justificación derecha a un menú? Dependiendo de si usas el MenuFactory o no, hay dos maneras de proceder. Con el MenuFactory, usa algo como lo que sigue: menu_path = gtk_menu_factory_find (factory, "<MyApp>/Help"); gtk_menu_item_right_justify(menu_path->widget); Si no usas el MenuFactory, usa simplemente: gtk_menu_item_right_justify(my_menu_item); ¿Cómo agrego aceleradores subrayados a los artículos del menú? Damon Chaplin, la fuerza técnica detrás del proyecto Glade, proporcionó el siguiente ejemplo de código (este código es una salida de Glade). Crea un artículo de menú pequeño File con un solo hijo (New). La F en File y la N en New están subrayadas, y se crean los aceleradores relevantes. menubar1 = gtk_menu_bar_new (); gtk_object_set_data (GTK_OBJECT (window1), "menubar1", menubar1); gtk_widget_show (menubar1); gtk_box_pack_start (GTK_BOX (vbox1), menubar1, FALSE, FALSE, 0); file1 = gtk_menu_item_new_with_label (""); tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (file1)->child), _("_File")); gtk_widget_add_accelerator (file1, "activate_item", accel_group, tmp_key, GDK_MOD1_MASK, 0); gtk_object_set_data (GTK_OBJECT (window1), "file1", file1); gtk_widget_show (file1); gtk_container_add (GTK_CONTAINER (menubar1), file1); file1_menu = gtk_menu_new (); file1_menu_accels = gtk_menu_ensure_uline_accel_group (GTK_MENU (file1_menu)); gtk_object_set_data (GTK_OBJECT (window1), "file1_menu", file1_menu); gtk_menu_item_set_submenu (GTK_MENU_ITEM (file1), file1_menu); new1 = gtk_menu_item_new_with_label (""); tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (new1)->child), _("_New")); gtk_widget_add_accelerator (new1, "activate_item", file1_menu_accels, tmp_key, 0, 0); gtk_object_set_data (GTK_OBJECT (window1), "new1", new1); gtk_widget_show (new1); gtk_container_add (GTK_CONTAINER (file1_menu), new1); ¿Cómo puedo rescatar el texto de un GtkMenuItem? Usualmente puedes obtener la etiqueta de un GtkMenuItem específico con: if (GTK_BIN (menu_item)->child) { GtkWidget *child = GTK_BIN (menu_item)->child; /* hacer cosas con el hijo */ if (GTK_IS_LABEL (child)) { gchar *text; gtk_label_get (GTK_LABEL (child), &text); g_print ("menu item text: %s\n", text); } } Para obtener el artículo activo de un GtkOptionMenu puedes hacer: if (GTK_OPTION_MENU (option_menu)->menu_item) { GtkWidget *menu_item = GTK_OPTION_MENU (option_menu)->menu_item; } Pero hay una trampa. Para este caso específico, no puedes obtener el widget etiqueta de menu_item con el código anterior, porque el menú de opción cambia el padre del hijo de menu_item temporalmente para desplegar los contenidos actualmente activos. Así que para rescatar el hijo de menu_item actualmente activo de un menú de opción, debes hacer esto: if (GTK_BIN (option_menu)->child) { GtkWidget *child = GTK_BIN (option_menu)->child; /* hacer cosas con el hijo */ } ¿Cómo justifico a la derecha (o de otra manera) un GtkLabel? ¿Estás seguro de querer justificar las etiquetas? La clase label contiene la función gtk_label_set_justify() que se usa para controlar la justificación de una etiqueta con múltiples líneas. Lo que probablemente quieres hacer es asignar la alineación de la etiqueta, ej alinearlo a la derecha, centrarlo o alinearlo a la izquierda. Si quieres hacer esto, debes usar: void gtk_misc_set_alignment (GtkMisc *misc, gfloat xalign, gfloat yalign); dónde los valores xalign y yalign son de punto flotante con rango [0.00;1.00]. GtkWidget *label; /* horizontal : alinear a la izquierda, vertical : arriba */ gtk_misc_set_alignment(GTK_MISK(label), 0.0f, 0.0f); /* horizontal : centreado, vertical : centreado */ gtk_misc_set_alignment(GTK_MISK(label), 0.5f, 0.5f); /* horizontal : alineado a la derecha, vertical : abajo */ gtk_misc_set_alignment(GTK_MISK(label), 1.0f, 1.0f); ¿Como asigno el color de fondo de un widget GtkLabel? El widget GtkLabel es uno de los pocos widgets GTK+ que no crean su propia ventana para dibujarse a sí mismos. En cambio, se dibujan en la ventana de su padre. Esto significa que para asignar el color de fondo de un widget GtkLabel, necesitas cambiar el color de fondo de su padre, ej. el objeto en el que lo empaquetaste. ¿Cómo asigno el color y tipo de letra de un GtkLabel usando un archivo de Recursos? El camino del nombre de widget construido para una etiqueta se compone con los nombres de widgets de su jerarquía de objetos, ej. window (name: humphrey) hbox label (name: mylabel) El camino del widget que tu patrón necesita igualar debe ser: humphrey.GtkHBox.mylabel El archivo de recursos puede lucir algo como esto: style "title" { fg[NORMAL] = {1.0, 0.0, 0.0} font = "-adobe-helvetica-bold-r-normal--*-140-*-*-*-*-*-*" } widget "*mylabel" style "title" En tu programa, también tendrías que dar un nombre al widget etiqueta, lo que puede hacerse usando: label = gtk_label_new("Some Label Text"); gtk_widget_set_name(label, "mylabel"); gtk_widget_show(label); ¿Cómo configuro «Tooltips» en un archivo de Recursos? La ventana del tooltip se llama "gtk-tooltips", GtkTooltips no es un GtkWidget en sí mismo (aunque es un GtkObject) y como tal no intenta empatar con algún estilo de widget. Por lo tanto, tu archivo de recursos debe lucir algo como esto: style "postie" { bg[NORMAL] = {1.0, 1.0, 0.0} } widget "gtk-tooltips*" style "postie" No puedo agregar más de (algo como) 2000 caracteres en un GtkEntry. ¿Qué está mal? Hay un problema conocido en el widget GtkEntry. En la función gtk_entry_insert_text(), las siguientes líneas limitan el número de caracteres en la entrada a 2047. /* The algorithms here will work as long as, the text size (a * multiple of 2), fits into a guint16 but we specify a shorter * maximum length so that if the user pastes a very long text, there * is not a long hang from the slow X_LOCALE functions. */ if (entry->text_max_length == 0) max_length = 2047; else max_length = MIN (2047, entry->text_max_length); ¿Cómo hago activo un widget GtkEntry al presionar la tecla de Retorno? El widget Entry emite la señal 'activate' cuando se pulsa la tecla 'return'. Simplemente conecte con la señal activa de GtkEntry y haga lo que desee. Un código típico seria el siguiente: entry = gtk_entry_new(); gtk_signal_connect (GTK_OBJECT(entry), "activate", GTK_SIGNAL_FUNC(entry_callback), NULL); ¿Cómo valido/limito/filtro la entrada a un GtkEntry? Si quieres validar el texto que introduce un usuario a un widget GtkEntry, puede pegarse a la señal "insert_text" de la entrada, y modificar el texto dentro de la función callback. El ejemplo siguiente fuerza todos los caracteres a ser mayúsculas, y limita el rango de caracteres en A-Z. Nota que la entrada está moldeada a un objeto de tipo GtkEditable, del cual se deriva GtkEntry. #include <ctype.h> #include <gtk/gtk.h> void insert_text_handler (GtkEntry *entry, const gchar *text, gint length, gint *position, gpointer data) { GtkEditable *editable = GTK_EDITABLE(entry); int i, count=0; gchar *result = g_new (gchar, length); for (i=0; i < length; i++) { if (!isalpha(text[i])) continue; result[count++] = islower(text[i]) ? toupper(text[i]) : text[i]; } if (count > 0) { gtk_signal_handler_block_by_func (GTK_OBJECT (editable), GTK_SIGNAL_FUNC (insert_text_handler), data); gtk_editable_insert_text (editable, result, count, position); gtk_signal_handler_unblock_by_func (GTK_OBJECT (editable), GTK_SIGNAL_FUNC (insert_text_handler), data); } gtk_signal_emit_stop_by_name (GTK_OBJECT (editable), "insert_text"); g_free (result); } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *entry; gtk_init (&argc, &argv); /* create a new window */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW (window), "GTK Entry"); gtk_signal_connect(GTK_OBJECT (window), "delete_event", (GtkSignalFunc) gtk_exit, NULL); entry = gtk_entry_new(); gtk_signal_connect(GTK_OBJECT(entry), "insert_text", GTK_SIGNAL_FUNC(insert_text_handler), + NULL); gtk_container_add(GTK_CONTAINER (window), entry); gtk_widget_show(entry); gtk_widget_show(window); gtk_main(); return(0); } ¿Cómo utilizo scrollbars horizontales con un widget GtkText? La respuesta corta es que no puedes. La versión actual del widget GtkText no soporta enrrollamiento horizontal. Hay intención de reescribir completamente el widget GtkText, momento en el cual esta limitación será solventada. ¿Cómo cambio el font de un widget GtkText? Hay un par de maneras de hacerlo. Como GTK+ permite que la apariencia de las aplicaciones cambie en tiempo de ejecución utilizando recursos, puedes usar algo como lo siguiente en el archivo apropiado: style "text" { font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*" } Otra manera de hacerlo es cargando un font dentro del programa, y entonces usar esto en las funciones para agregar texto a widget de texto. Puedes cargar un font usando, por ejemplo: GdkFont *font; font = gdk_font_load("-adobe-helvetica-medium-r-normal--*-140-*-*-*-*-*-*"); ¿Cómo coloco la posición de un cursor en un objeto GtkText? Ten en cuenta que la respuesta es válida para cualquier objeto que hereda de la clase GtkEditable. ¿Estás seguro que deseas mover la posición del cursor? La mayor parte del tiempo, mientras la posición del cursor es buena, el punto de inserción no se empareja con la posición del cursor. Si esto se aplica a lo que quieres en realidad, entonces deberías usar la función gtk_text_set_point(). Si quieres colocar el punto de inserción en la posición actual del cursor, usa lo siguiente: gtk_text_set_point(GTK_TEXT(text), gtk_editable_get_position(GTK_EDITABLE(text))); Si quieres que el punto de inserción siga al cursor todo el tiempo, probablemente deberías atrapar el evento de presión de botón, y entonces mover el punto de inserción. Se cuidadoso: deberás atraparlo después de que el widget ha cambiado la posición del cursor. Thomas Mailund Jensen propuso el siguiente código: static void insert_bar (GtkWidget *text) { /* brincar a la marca del cursor */ gtk_text_set_point (GTK_TEXT (text), gtk_editable_get_position (GTK_EDITABLE (text))); gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, "bar", strlen ("bar")); } int main (int argc, char *argv[]) { GtkWidget *window, *text; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); text = gtk_text_new (NULL, NULL); gtk_text_set_editable (GTK_TEXT (text), TRUE); gtk_container_add (GTK_CONTAINER (window), text); /* conectar después de todo lo demás */ gtk_signal_connect_after (GTK_OBJECT(text), "button_press_event", GTK_SIGNAL_FUNC (insert_bar), NULL); gtk_widget_show_all(window); gtk_main(); return 0; } Ahora, si en realidad quieres cambiar la posición del cursor, debes usar la función gtk_editable_set_position(). Acerca de GDK ¿Qué es GDK? GDK básicamente es un envoltorio alrededor de funciones estándares de Xlib. Si tienes familiaridad con Xlib, te será fácil acostumbrarte a la mayoría de las funciones de GDK. Todas la funciones están escritas para proveer una manera de acceder a las funciones Xlib de una manera más fácil e intuitiva. Además, ya que GDK usa GLib (mirar abajo), será más portable y seguro de usar en múltiples plataformas. ¿Cómo utilizo la asignación de color Una de las cosas agradables sobre GDK, es que está basada sobre Xlib; esto también es un problema, especialmente en el área de administración de color. Si quieres usar color en tu programa (dibujar un rectángulo o algo así), tu código debe lucir algo como esto: { GdkColor *color; int width, height; GtkWidget *widget; GdkGC *gc; ... /* primero, crea un Contexto Gráfico para dibujar en él */ gc = gdk_gc_new(widget->window); /* encontrar dimensiones apropiadas para el rectángulo */ gdk_window_get_size(widget->window, &width, &height); /* el color que queremos utilizar */ color = (GdkColor *)malloc(sizeof(GdkColor)); /* rojo, verde, y azul son valores pasados, indicando la tripleta RGB * del color que queremos dibujar. Nota que los valores de los * componentes RGB dentro de GdkColor son tomados de 0 a 65536, no 0 a 255. */ color->red = red * (65535/255); color->green = green * (65535/255); color->blue = blue * (65535/255); /* el valor de pixel indica el índice en el mapa de color del color. * simplemente es una combinación de los valores RGB que asignamos * anteriormente */ color->pixel = (gulong)(red*65536 + green*256 + blue); /* Sin embargo, el valor de pixel sólo es verdaderamente válido en * dispositivos de 24-bit (ColorVerdadero). Por lo tanto, esta llamada * es requerida para que GDK y X puedan darnos el color más cercano * disponible en el mapa de color */ gdk_color_alloc(gtk_widget_get_colormap(widget), color); /* asignar la parte frontal a nuestro color */ gdk_gc_set_foreground(gc, color); /* dibujar el rectángulo */ gdk_draw_rectangle(widget->window, gc, 1, 0, 0, width, height); ... } Sobre GLib ¿Qué es GLib? GLib es una librería de funciones y definiciones útiles disponibles para usarse cuando se crean aplicaciones GDK y GTK. Provee reemplazos para algunas funciones estándares de libc, como malloc, las cuales tienen problemas en algunos sistemas. También provee rutinas para manejar: Listas Doblemente Enlazadas Listas con Enlace Simple Temporizadores Manejo de Cadenas Un Rastreador Léxico Funciones de Error ¿Cómo puedo usar las listas doblemente enlazadas? El objeto GList se define como: typedef struct _GList GList; struct _GList { gpointer data; GList *next; GList *prev; }; Para usar los objetos GList, simplemente: GList *list = NULL; GList *listrunner; gint array[] = { 1, 2, 3, 4, 5, 6 }; gint pos; gint *value; /* agregar datos a la lista */ for (pos=0;pos < sizeof array; pos++) { list = g_list_append(list, (gpointer)&array[pos]); } /* atravesar la lista */ listrunner = g_list_first(list); while (listrunner) { value = (gint *)listrunner->data; printf("%d\n", *value); listrunner = g_list_next(listrunner); } /* eliminando datos de la lista */ listrunner = g_list_first(list); list = g_list_remove_link(list, listrunner); list = g_list_remove(list, &array[4]); El mismo código se puede utilizar con listas simples (objetos GSList) reemplazando las funciones g_list_* con las g_slist_* relevantes (g_slist_append, g_slist_remove, ...). Simplemente recuerda que ya que no puedes retroceder en una lista simplemente enlazada, no hay función g_slist_first - necesitaras mantener una referencia al primer nodo de la lista. Parece que no se libera memoria cuando libero los nodos de la lista que había asignado GLib trata de ser "inteligente" en este asunto especial: asume que seguramente reutilizaras los objetos, así que guarda la memoria asignada en un cache. Si no quieres utilizar este comportamiento, probablemente querrás crear un colocador especial. Citando a Tim Janik: Si tienes cierta porción de código que usa *muchos* GLists o GNodes, y sabes que seria mejor liberarlos todos después de un rato, querrás utilizar un GAllocator. Empujar un colocador a un g_list hará privado el espacio de memoria de ese colocador para todas las operaciones glist subsecuentes (y por lo tanto tienes que tener cuidado de sacar el colocador otra vez, antes de hacer llamadas externas): GAllocator *allocator; GList *list = NULL; guint i; /* asignar un nuevo espacio de colocación para nodos GLis */ allocator = g_allocator_new ("list heap", 1024); g_list_push_allocator (allocator); /* hacer operaciones de lista */ for (i = 0; i < 4096; i++) list = g_list_prepend (list, NULL); list = g_list_reverse (list); /* ten cuidado de sacar el colocador antes de llamar funciones externas */ g_list_pop_allocator (); gtk_label_set_text (GTK_LABEL (some_label), "some text"); /* y asigna nuestro espacio privado de glist otra vez */ g_list_push_allocator (allocator); /* hacer operaciones de lista */ g_list_free (list); list = NULL; for (i = 0; i < 4096; i++) list = g_list_prepend (list, NULL); /* y sal otra vez (al mismo tiempo que liberas todos los nodos) */ g_list_pop_allocator (); g_allocator_free (allocator); ¿Por qué utilizar las funciones g_print, g_malloc, g_strdup y compañeras de glib? Gracias a Tim Janik que escribió a gtk-list: (un poco modificado) Con respecto a g_malloc(), g_free() y hermanos, estas funciones son más segura que sus equivalentes libc. Por ejemplo, g_free() sólo regresa si se llama con NULL. También, si USE_DMALLOC se define, la definición para estas funciones cambia (en glib.h) para usar MALLOC(), FREE() etc... Si MEM_PROFILE o MEM_CHECK se definen, incluso tenemos pequeñas estadísticas que cuentan el tamaño de los bloque usados (mostrado por g_mem_profile() / g_mem_check()). Considerando el hecho de que glib provee una interface para que los pedazos de memoria salven espacio si tienes muchos bloques que siempre son del mismo tamaño y para marcarlos ALLOC_ONLY si es necesario, es fácil crear un pequeño envoltorio ahorrador alrededor de las cosas malloc/free normales - tal como gdk cubre Xlib. ;) Usando g_error() y g_warning() dentro de las aplicaciones como GIMP que se apoyan por completo en gtk, incluso da la oportunidad de hacer aparecer una ventana mostrando los mensajes dentro de una ventana gtk con tu propio manejador (usando g_set_error_handler()) al estilo de gtk_print() (dentro de gtkmain.c). ¿Qué es un GScaner y cómo lo utilizo? Un GScanner convertirá en símbolos (tokens) tu texto, lo que significa, que regresará un entero para cada palabra o número que aparece en su flujo de entrada, siguiendo ciertas reglas (adaptables) para realizar esta traducción. Todavía necesitas escribir las funciones de análisis sintáctico por tu propia cuenta. Aquí hay un pequeño programa de prueba provisto por Tim Janik que hará análisis sintáctico <SYMBOL> = <OPTIONAL-MINUS> <NUMBER> ; constructs, while skipping "#\n" and "/**/" style comments. #include <glib.h> /* un poco de texto de prueba para alimentar el rastreador */ static const gchar *test_text = ( "ping = 5;\n" "/* slide in some \n" " * comments, just for the\n" " * fun of it \n" " */\n" "pong = -6; \n" "\n" "# the next value is a float\n" "zonk = 0.7;\n" "# redefine ping\n" "ping = - 0.5;\n" ); /* definir valores de enumeración a ser regresados por símbolos específicos */ enum { SYMBOL_PING = G_TOKEN_LAST + 1, SYMBOL_PONG = G_TOKEN_LAST + 2, SYMBOL_ZONK = G_TOKEN_LAST + 3 }; /* cadena de símbolos */ static const struct { gchar *symbol_name; guint symbol_token; } symbols[] = { { "ping", SYMBOL_PING, }, { "pong", SYMBOL_PONG, }, { "zonk", SYMBOL_ZONK, }, { NULL, 0, }, }, *symbol_p = symbols; static gfloat ping = 0; static gfloat pong = 0; static gfloat zonk = 0; static guint parse_symbol (GScanner *scanner) { guint symbol; gboolean negate = FALSE; /* esperar un símbolo válido */ g_scanner_get_next_token (scanner); symbol = scanner->token; if (symbol < SYMBOL_PING || symbol > SYMBOL_ZONK) return G_TOKEN_SYMBOL; /* esperar '=' */ g_scanner_get_next_token (scanner); if (scanner->token != '=') return '='; /* característica opcional '-' */ g_scanner_peek_next_token (scanner); if (scanner->next_token == '-') { g_scanner_get_next_token (scanner); negate = !negate; } /* esperar un float (los enteros son convertidos a float inmediatamente) */ g_scanner_get_next_token (scanner); if (scanner->token != G_TOKEN_FLOAT) return G_TOKEN_FLOAT; /* asegúrate que el siguiente token es un ';' */ if (g_scanner_peek_next_token (scanner) != ';') { /* no es así, come el no-punto-y-coma y manda error */ g_scanner_get_next_token (scanner); return ';'; } /* asignar valores, comer el punto y coma y salir exitosamente */ switch (symbol) { case SYMBOL_PING: ping = negate ? - scanner->value.v_float : scanner->value.v_float; break; case SYMBOL_PONG: pong = negate ? - scanner->value.v_float : scanner->value.v_float; break; case SYMBOL_ZONK: zonk = negate ? - scanner->value.v_float : scanner->value.v_float; break; } g_scanner_get_next_token (scanner); return G_TOKEN_NONE; } int main (int argc, char *argv[]) { GScanner *scanner; guint expected_token; scanner = g_scanner_new (NULL); /* ajustar el comportamiento léxico para nuestra necesidades */ /* convertir no-float (valores octale, hexadecimales ...) a G_TOKEN_INT */ scanner->config->numbers_2_int = TRUE; /* convertir G_TOKEN_INT a G_TOKEN_FLOAT */ scanner->config->int_2_float = TRUE; /* no regresar G_TOKEN_SYMBOL, sino el valor del símbolo */ scanner->config->symbol_2_token = TRUE; /* cargar los símbolos al rastreador */ while (symbol_p->symbol_name) { g_scanner_add_symbol (scanner, symbol_p->symbol_name, GINT_TO_POINTER (symbol_p->symbol_token)); symbol_p++; } /* alimentar el texto */ g_scanner_input_text (scanner, test_text, strlen (test_text)); /* dar al manejador de errores una idea de cómo se nombra la entrada */ scanner->input_name = "test text"; /* ciclo de rastreo, analisamos la entrada hasta alcanzar el final, * encontrar un error por el rastrador, o nuestra subrutina encuentre * sintaxis inválida */ do { expected_token = parse_symbol (scanner); g_scanner_peek_next_token (scanner); } while (expected_token == G_TOKEN_NONE && scanner->next_token != G_TOKEN_EOF && scanner->next_token != G_TOKEN_ERROR); /* dar un mensaje de error en los errores de sintaxis */ if (expected_token != G_TOKEN_NONE) g_scanner_unexp_token (scanner, expected_token, NULL, "symbol", NULL, NULL, TRUE); /* finalizar el análisis */ g_scanner_destroy (scanner); /* imprimir resultados */ g_print ("ping: %f\n", ping); g_print ("pong: %f\n", pong); g_print ("zonk: %f\n", zonk); return 0; } Necesitas entender que el rastreador analizará su entrada y la convertirá en símbolos, te toca a ti interpretar estos símbolos, y no definir su tipo antes de que sean analizados, ej. observa gscanner analizar una cadena: "hi i am 17" | | | | | | | v | | v TOKEN_INT, value: 17 | v TOKEN_IDENTIFIER, value: "am" v TOKEN_CHAR, value: 'i' TOKEN_IDENTIFIER, value: "hi" Si configuras el rastreador con: scanner->config->int_2_float = TRUE; scanner->config->char_2_token = TRUE; scanner->config->scan_symbols = TRUE; y agregas "am" como símbolo con g_scanner_add_symbol (scanner, "am", "symbol value"); GScanner lo analizará como "hi i am 17" | | | | | | | v | | v TOKEN_FLOAT, value: 17.0 (conversión automática int->float) | | TOKEN_SYMBOL, value: "symbol value" (una búsqueda exitosa en la tabla segmentada | | convirtió un TOKEN_IDENTIFIER a un | | TOKEN_SYMBOL y se apoderó de | v el valor del símbolo) v 'i' ('i' también puede ser un símbolo válido, como todos los caracteres >0 y <256) TOKEN_IDENTIFIER, value: "hi" Necesitas emparejar la secuencia de token con tu código, y si encuentras algo que no quieres, manda un error: /* espera un identificador("hi") */ g_scanner_get_next_token (scanner); if (scanner->token != G_TOKEN_IDENTIFIER) return G_TOKEN_IDENTIFIER; /* espera un token 'i' */ g_scanner_get_next_token (scanner); if (scanner->token != 'i') return 'i'; /* espera un símbolo ("am") */ g_scanner_get_next_token (scanner); if (scanner->token != G_TOKEN_SYMBOL) return G_TOKEN_SYMBOL; /* espera un float (17.0) */ g_scanner_get_next_token (scanner); if (scanner->token != G_TOKEN_FLOAT) return G_TOKEN_FLOAT; Si pasaste de ahí, has analizado "hi i am 17" y también podrías haber aceptado "dook i am 42" y " bah i am 0.75", pero no habrías aceptado "hi 7 am 17" o "hi i hi 17". Contribuciones al PUF de GTK+, revisores y derecho de autor («Copyright») Si quieres hacer una contribución al PUF, envía a alguno de nosotros un mensaje por correo electrónico con el texto exacto que crees que debería ser incluido (pregunta y respuesta). ¡Con tu ayuda, este documento puede crecer y ser más útil! Este documento es mantenido por Tony Gale <gale@gtk.org> Nathan Froyd <maestrox@geocities.com>, y Emmanuel Deloget <logout@free.fr>. El PUF fue creado por Shawn T. Amundson <amundson@gimp.org> quien continúa dando soporte. Las contribuciones deben enviarse a Tony Gale <gale@gtk.org> Copia original de la licencia de este documento The GTK+ FAQ is Copyright (C) 1997-2000 by Shawn T. Amundson, Tony Gale, Emmanuel Deloget and Nathan Froyd. Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this document under the conditions for verbatim copying, provided that this copyright notice is included exactly as in the original, and that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this document into another language, under the above conditions for modified versions. Traducción de la licencia (a titulo informativo) Los derechos de autor de las PUF pertenecen a Shawn T. Amundson, Tony Gale, Emmanuel Deloget y Nathan Froyd. Se autoriza la copia y distribución de copias integras de este manual siempre que se incluya una copia de los derechos de autor y esta nota sobre los permisos en todas las copias. Se autoriza la copia y distribución de versiones modificadas de este documento en las condiciones de la copia integra, siempre que se incluya la nota sobre los derechos de autor exactamente igual que en original, y que el trabajo derivado completo se distribuya bajo términos idénticos a los de esta nota. Se autoriza la copia y distribución de traducciones de este documento a otros idiomas, bajo las condiciones descritas anteriormente para las versiones modificadas. Si tienes intenciones de incorporar este documento a un trabajo publicado, por favor contacta a uno de los mantenedores, y haremos un esfuerzo para asegurarnos que tengas disponible la información más actualizada. No hay garantía de que este documento logre cumplir completamente su propósito. Simplemente es distribuye como un recurso libre. Como tal, los autores y mantenedores de la información incluida no pueden dar garantía alguna de que la información sea incluso exacta. Traducción al Español La traducción fue realizada por Ramsés Morales, de Panamá. Para cualquier comentario o sugerencia me puedes escribir a ramses@computer.org Los derechos de autor de la traducción pertenecen a Ramsés Morales.