1 /* vim: set et ts=4 sw=4 fdm=indent tw=80 fdl=1 fdn=1 fo+=t:
3 * Taller de Programación (75.42).
6 * Graficador de figuras.
8 * Copyleft 2003 - Leandro Lucarella <llucare@fi.uba.ar>
9 * Puede copiar, modificar y distribuir este programa bajo los términos de
10 * la licencia GPL (http://www.gnu.org/).
12 * Creado: dom oct 5 20:39:33 ART 2003
17 /** \mainpage Trabajo Práctico V
20 También puede ver <a href="../latex/refman.pdf">este documento en formato
25 También puede ver este documento en formato HTML en html/index.html.
28 \section objetivo Objetivos.
29 - Que el alumno tome contacto con las capas de API gráficas,
30 adquiriendo la capacidad de generar gráficos simples de figuras
32 - Entender el concepto de Contexto Gráfico, y de las propiedades más
33 elementales del mismo.
34 - Manejar el concepto de Eventos, siendo el alumno capaz de armar luego
35 programas sobra la capa de API de una funcionalidad y potencia
37 - Comprender el concepto de ventana y dialogo.
38 - Aplicar todos los conceptos aprendidos en la materia hasta el momento
42 \section recursos Recursos utilizados.
43 - Herencia y Polimorfismo.
44 - ANSI C++ en general (para la codificación del programa).
45 - Funciones Callback, API <a href="http://www.gtk.org/">GDK/GTK</a>.
47 \note La utilización de la API <a href="http://www.gtk.org/">GDK/GTK</a>
48 para desarrollar el trabajo práctico bajo una plataforma libre
49 (GNU/Linux en este caso) fue autorizada por Andrés Veiga el día
50 30 de Septiembre de 2003.
53 \section gdk_gtk Descripción de API GDK/GTK y comparación con GDI de Windows.
54 GTK significa GIMP ToolKit ya que originalmente fue creada sólamente para
55 hacer el programa <a href="http://www.gimp.org/">GIMP</a> (GNU Image
56 Manipulation Program, una especie de Photoshop libre). Actualmente ha
57 crecido mucho, hasta superando en importancia ese proyecto inicial.
58 Creció tanto que incluso en la actualidad se compone a su vez de 2 capas
59 más, cada una siendo de más bajo nivel que la original. La capa de más bajo
60 nivel es la GLib, que implementa funciones básicas y tipos de datos comunes,
61 sirviendo de capa base para hacer aplicaciones multiplataforma. Sobre ésta,
62 está construida la biblioteca GDK (GTK+ Drawing Kit), que serviría de base
63 para hacer aplicaciones gráficas multiplataforma (aunque fuertemente
64 influída por la forma de funcionar de X Window, plataforma para la cual fue
65 creada inicialmente). GDK es entoncess de relativo bajo nivel (un nivel
66 cercano a Xlib, biblioteca de X Window, que sólo permite dibujar en pantalla
67 y manejar eventos, como el movimiento de un mouse el presionar una tecla o
68 un requerimiento para redibujar una porción de la pantalla). Sobre GDK se
69 crean una serie de \e widgets (componentes como botones, etiquetas, barras
70 de menú, etc) y a este conjunto de widgets se los llama GTK.
74 \section desarrollo Desarrollo.
75 El ejercicio consiste en un programa ejecutable utilizando las
76 bibliotecas <a href="http://www.gtk.org/">GDK/GTK</a> que reutilizando
77 la jerarquía de clases del Ejercicio Número 3 permita al usuario graficar
78 por pantalla ejecutando el método dibujar de la clase Dibujo. Para ello se
79 diseñó una ventana como la siguiente:
81 \image html ventana.png
82 \image latex ventana.eps "Ventana Principal"
84 En ésta el usuario puede seleccionar el tipo de figura a dibujar, los
85 datos necesarios para dibujarla y finalmente agregarla al dibujo.
86 El programa es capaz de agregar una figura sin dibujarla y luego dibujar
87 todo al presionar el botón Actualizar.
89 La funcionalidad de los botones es la siguiente:
90 - \b Actualizar: Redibuja la lista interna contenida en el dibujo.
91 - \b Limpiar: Limpia el área de dibujo.
92 - \b Agregar: Inserta una nueva figura al dibujo sin graficar la misma.
93 - \b Salir: Finaliza la ejecución del programa (igual que el botón X del
94 manejador de ventanas).
96 Solo puede insertarse una figura a la vez y sólo se habiliten los campos
97 correspondientes a los atributos de esa figura. Por ejemplo, si se marca
98 el \e toggle de un Rectángulo, no se puede ingresar el Radio ya que esa
99 figura no tiene dicho atributo.
102 \section resolucion Resolución.
103 \note Sólo se comentan los cambios realizados al Trabajo Práctico 3 para
104 cumplir los objetivos del actual. Por lo tanto no se hará referencia
105 a componentes como la lista interna de Dibujo y otros aspectos que ya
106 fueron evaluados en el trabajo anterior.
108 \subsection ventana Ventana principal.
109 Todos los componentes de la ventana principal (y la ventana en sí) se
110 encapsularon en un objeto TP5Window. Al inicializar el objeto se
111 inicializan todos los componentes de la ventana, inclusive el Dibujo que
112 contiene los objetos de tipo Figura que se van agregando. Dentro de
113 estos componentes se encuentran varios widgets GTK que se van agregando
114 a la ventana principal, guardandose un puntero como atributo de
115 TP5Window de solo aquellos widgets que se necesitarán a la hora de
116 obtener la entrada del usuario. Del resto de los widgets no es necesario
117 guardar un puntero ya que se liberan automáticamente al llamar a la
118 función gtk_main_quit().
119 TP5Window también tiene métodos para convertir o facilitar la conversión
120 de los datos que ingresa el usuario.
121 \note Para crear la ventana principal se utilizó una herramienta
122 gráfica que luego genera código automáticamente llamada Glade.
123 Sobre el código generado se cambiaron varias cosas y se
124 eliminaron llamadas a funciones inecesarias. Con este
125 programa también puede generarse un archivo XML con la disposición
126 gráfica de todos los elementos y luego cargarlo en tiempo de
127 ejecución mediante la biblioteca libglade, pero se dibujó toda la
128 interfaz con código para mostrar bien por dentro el proceso
129 realizado por dicha biblioteca.
131 \subsection callbacks Funciones callbacks.
132 Cada evento recibido por un widget GTK (y en general cualquier señal que
133 pueda manejar la GLib) de ser \e conectado a una función callback que se
134 encargue de menejarlo. Todas las funciones callback se agruparon en el
135 archivo callbacks.h y callbacks.cpp. Se implementaron callbacks para los
138 Este evento se \e dispara cuando un GtkButton es presionado. Se usa
139 para borrar el dibujo, actualizarlo, agregar figuras y salir del
140 programa y es manejado por las funciones on_button_borrar_clicked(),
141 on_button_graficar_clicked(), on_button_agregar_clicked(),
142 on_button_salir_clicked().
144 Este evento lo \e dispara un GtkToggleButton cuando es seleccionado
145 o deseleccionado. Se usa para activar y desactivar campos para la
146 entrada de usuario al seleccionar un tipo de figura y es manejado
147 por las siguientes funciones \e callbacks:
148 on_radiobutton_linea_toggled(), on_radiobutton_cuadrado_toggled(),
149 on_radiobutton_rectangulo_toggled(),
150 on_radiobutton_circulo_toggled().
152 Este Evento es \e disparado cuando se debe redibujar un área de la
153 pantalla, en este caso sólo se conecta el evento para el área de
154 dibujo para dibujar todas las figuras en la pantalla. La función que
155 maneja este evento es on_drawingarea_expose_event().
157 Este evento se \e dispara cuando se solicita \e borrar una ventana.
158 Esto pasa cuando se le pide a un manejador de ventanas que \e borre
159 o \e cierre esa ventana (ya sea por la típica X en la decoración de la
160 ventana o un menú contextual, por ejemplo).
162 Una clara diferencia entre estos es que los dos primeros son \e internos
163 del programa, es decir, \e disparados por el mismo programa, mientras
164 que los dos últimos son eventos \e externos, generados ya sea por el
165 manejador de ventanas o por el mismo sistema gráfico (X Window o el
166 sistema operativo, dependiendo de la plataforma).
168 \subsection dibujando Dibujando.
169 El dibujo de las figuras comienza en la \e callback que maneja el \c
170 expose_event del área de dibujo. En ella se invoca al método
171 Dibujar::dibujar() quien a su vez llama al método Figura::dibujar() que
172 corresponda para cada dibujo en su lista interna.
173 Figura::dibujar() en sí es un método abstracto, pero hay otro método,
174 Figura::get_gc() que es utilizado por todos los métodos dibujar()
175 sobreescritos que cambia el contexto gráfico, asignando los
176 valores apropiedos al grueso y color del trazo utilizado para dibujar.
177 En cada método dibujar() sobreescrito se dibuja la figura en sí, con las
178 propiedades del contexto gráfico obtenidas de Figura::get_gc().
181 \section requerimientos Requerimientos y modo de uso.
182 \subsection plataforma Plataforma y compilador.
183 Este trabajo práctico fue realizado y probado bajo la plataforma Debian
184 GNU/Linux sid. El compilador utilizado fue GNU GCC versión 3.3.1. Se
185 utilizó la librería GTK versión 2.2.4.
186 El ejecutable entregado corre bajo esta plataforma y basta con
187 ejecutarlo para ver el resultado.
189 \subsection compilacion Compilación.
190 Para compilarlo basta con tener el programa \c make y los paquetes con
191 librerías para desarrollo de GTK instalados (esto incluye también
192 librerías de desarrollo de Xlib, Glib y GDK). En el caso de Debian
193 GNU/Linux sid basta con instalar los paquetes: \c make, \c xlibs-dev,
194 \c libglib2\c .0-dev, \c libgtk2\c .0-dev.
195 Teniendo estos paquetes, basta ejecutar \c make para compilar el
199 \section conclusiones Conclusiones.
200 Este trabajo me sirvió para profundizar sobre algunos aspectos de
201 programación de aplicaciones gráficas que no manejaba. También me sirvió
202 para saldar una cuenta que tenía hace tiempo, que era aprender sobre GTK y
203 en general Xlib (para conocer el sistema X Window a más bajo nivel) ya que
204 hasta ahora, en cuanto a aplicaciones gráficas, sólo había programado en
205 Delphi y Java, dos lenguajes (y/o API) de mucho más alto nivel.
206 Un problema con el que me encontré, a modo de anécdota, fue el manejo de
207 colores. En un principio había programado el método
208 Figura::traducir_color() pensando en mi configuración de 24 bits. Todas las
209 operaciones las realizaba operando con máscaras de bits presuponiendo 8 bits
210 por color RGB, escribiendo en pantalla \e directamente el píxel resultante.
211 Al querer probar el programa en mi trabajo, con una configuración de 16 bits
212 por píxel, por supuesto que sucedieron cosas extrañas. Fue en ese momento
213 que tuve que reescribir dicho método, utilizando las propiedades \c red,
214 \c green y \c blue de la estructura GdkColor y las funciones de la API para
215 \e preguntarle al X si el color estaba disponible y buscar un color similar
216 de no estarlo, en vez de poner directamente el color en la propiedad \c
217 pixel que almacena el valor real del píxel que se usara para poner en la