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 bugs 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: make:
file `Makefile' line 456: Syntax error
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
glibconfig.h.
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+,
configure 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 bindings
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 Hola Mundo 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 make?
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 configure. ¿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 fork() 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 delete_event 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.