Portada Sobre GTK Curso de programación en C moderno Bucle principal (II Edición) Señales Callbacks Glade y builder Neira Ayuso, Pablo Ejemplo simple Tema 14 Juego de la vida GTK main gui object drawing area Timer Ejercicio Índice Portada Sobre GTK Bucle principal Señales Callbacks Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio Falgueras García, Carlos 1 Sobre GTK 2 Bucle principal 3 Señales 4 Callbacks 5 Glade y builder 6 Ejemplo simple 7 Juego de la vida main gui object drawing area Timer Ejercicio Sobre GTK Portada • GTK (Gimp ToolKit) es una biblioteca libre y multiplataforma para el desarrollo de interfaces gráficas de usuario Sobre GTK Bucle principal • Está escrita en C, aunque tiene bindings a una infinidad de Señales lenguajes distintos (C++, C#, Python, Lua, Haskel, . . . ) • Se trata de una biblioteca muy popular, con mucha Callbacks Glade y builder comunidad alrededor y muchos años de madurez • Las interfaces se construyen mediante la anidación de Ejemplo simple diversos widget. Tiene una enorme variedad de widgets (ejecutar “gtk3-widget-factory”) Juego de la vida main gui object drawing area Timer Ejercicio • Dispone de una documentación detallada y de calidad (https://developer.gnome.org/gtk3/stable/). Además de numerosos tutoriales y ejemplos desarrollados por su gran comunidad. Bucle principal • Toda la lógica de la interfaz se realiza dentro de un bucle Portada • • • • Sobre GTK Bucle principal Señales Callbacks Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio 1 2 3 4 5 #i n c l u d e <g t k / g t k . h> i n t main ( i n t a r g c , c h a r ∗∗ a r g v ) { GtkWidget ∗ window ; 6 g t k _ i n i t (& a r g c , &a r g v ) ; window = gtk_window_new (GTK_WINDOW_TOPLEVEL) ; g t k _ w i d g e t _ s h o w _ a l l ( window ) ; gtk_main ( ) ; 7 8 9 10 11 12 13 infinito gtk_main(); Antes del bucle la interfaz no es funcional Después del bucle no existe interfaz No podemos modificar el interior del bucle ¿Cómo interacciono con la interfaz gráfica? } return 0; Señales Portada Sobre GTK Bucle principal Señales Callbacks Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio • Una señal puede verse como un mensaje que nos envía GTK cada vez que ocurre un evento • Un evento puede ser cualquier acción del usuario: un click en un botón, mover el ratón sobre una ventana, arrastar algún objeto, . . . . También hay eventos internos de la interfaz gráfica: se acaba de crear/destruir la ventana, se va a redibujar algún objeto, . . . • En GTK se implementan mediante callbacks Callbacks Portada Sobre GTK Bucle principal Señales Callbacks Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio • Un callback es una función que pasamos a alguna librería, para que ella se encargue de llamarla cuando convenga • GTK guarda una lista de punteros a funciones por cada posible evento • Como usuarios de GTK, conectamos una o más funciones de callback a una señal (pulsar un botón). Cuando ocurra el evento oportuno, GTK se encargará de llamarlas a todas Glade y builder Portada • Crear una interfaz compleja directamente en un lenguaje de programación es complejo y poco versátil Sobre GTK • GTK puede crear e inicializar todos los objetos necesarios a Bucle principal partir de un XML con la descripción de la interfaz Señales • Glade es un programa que nos ayuda a construir este XML Callbacks de manera gráfica Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio Ejemplo simple Portada Sobre GTK Bucle principal Señales Callbacks Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #i n c l u d e < s t d i o . h> #i n c l u d e < s t d l i b . h> #i n c l u d e <g t k / g t k . h> #d e f i n e BUILDER_FILE " b u i l d e r . u i " i n t main ( i n t a r g c , c h a r ∗∗ a r g v ) { GtkBuilder ∗ builder ; G t k W i d g e t ∗ window ; GtkWidget ∗ b u t t o n 1 ; GtkWidget ∗ b u t t o n 2 ; g t k _ i n i t (& a r g c , &a r g v ) ; /∗ b u i l d e r ∗/ b u i l d e r = gtk_builder_new ( ) ; i f ( ! g t k _ b u i l d e r _ a d d _ f r o m _ f i l e ( b u i l d e r , BUILDER_FILE , NULL ) ) { f p r i n t f ( s t d e r r , " Can ' t open f i l e \"% s \"\ n " , BUILDER_FILE ) ; e x i t ( EXIT_FAILURE ) ; } /∗ o b j e c t s ∗/ window = GTK_WIDGET( g t k _ b u i l d e r _ g e t _ o b j e c t ( b u i l d e r , " window " ) ) ; b u t t o n 1 = GTK_WIDGET( g t k _ b u i l d e r _ g e t _ o b j e c t ( b u i l d e r , " b u t t o n 1 " ) ) ; b u t t o n 2 = GTK_WIDGET( g t k _ b u i l d e r _ g e t _ o b j e c t ( b u i l d e r , " b u t t o n 2 " ) ) ; Ejemplo simple Portada Sobre GTK 27 28 29 /∗ s i g n a l s ∗/ g _ s i g n a l _ c o n n e c t ( window , " d e s t r o y " , G_CALLBACK( g t k _ m a i n _ q u i t ) , NULL ) ; g _ s i g n a l _ c o n n e c t ( b u t t o n 1 , " c l i c k e d " , G_CALLBACK( b u t t o n _ c b ) , " H o l a " ); g _ s i g n a l _ c o n n e c t ( b u t t o n 2 , " c l i c k e d " , G_CALLBACK( b u t t o n _ c b ) , " A d i o s ") ; g t k _ b u i l d e r _ c o n n e c t _ s i g n a l s ( b u i l d e r , NULL ) ; Bucle principal 30 Señales 31 Callbacks 32 33 34 g t k _ w i d g e t _ s h o w _ a l l ( window ) ; 35 gtk_main ( ) ; 36 37 return 0; 38 } 39 40 s t a t i c v o i d b u t t o n _ c b ( G t k W i d g e t ∗ w i d g e t , 41 { 42 p r i n t f ( "%s mundo ! \ n " , s t r ) ; 43 } Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio const char ∗ s t r ) gcc -g $(pkg-config --cflags gtk+-3.0 --libs gtk+-3.0)main.c Juego de la vida Portada Sobre GTK Bucle principal Señales Callbacks Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio Juego de la vida main Portada Sobre GTK Bucle principal Señales Callbacks Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio 1 i n t main ( i n t a r g c , c h a r ∗∗ a r g v ) 2 { 3 s t r u c t w o r l d ∗w ; 4 s t r u c t g u i ∗g ; 5 6 g t k _ i n i t (& a r g c , &a r g v ) ; 7 8 w = w o r l d _ a l l o c (WORLD_X, WORLD_Y) ; 9 i f ( ! w) { 10 p e r r o r ( " Can ' t a l l o c a t e w o r l d " ) ; 11 e x i t ( EXIT_FAILURE ) ; 12 } 13 w o r l d _ i n i t (w) ; 14 15 g = g u i _ a l l o c ( " b u i l d e r . u i " , w , WINDOW_X, WINDOW_Y) ; 16 i f (! g) { 17 p e r r o r ( " Can ' t c r e a t e g u i " ) ; 18 e x i t ( EXIT_FAILURE ) ; 19 } 20 21 gtk_main ( ) ; 22 23 w o r l d _ f r e e (w) ; 24 gui_free (g) ; 25 26 return 0; 27 } gui object Portada Sobre GTK Bucle principal Señales Callbacks Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio 1 struct gui { 2 GtkBuilder ∗ builder ; 3 4 G t k W i d g e t ∗ window ; 5 GtkWidget ∗ btn_quit ; 6 GtkWidget ∗ btn_step ; 7 GtkWidget ∗ btn_pause ; 8 GtkGrid ∗ grid ; 9 GtkWidget ∗ drawing_area ; 10 11 struct { 12 double x ; 13 double y ; 14 } cell_size ; 15 16 bool run ; 17 18 s t r u c t world ∗ world ; 19 } ; drawing area Portada Sobre GTK Bucle principal Señales Callbacks Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 /∗ d r a w i n g a r e a ∗/ g−>d r a w i n g _ a r e a = gtk_drawing_area_new ( ) ; // Tamanyo d e l l a v e n t a n a de d i b u j a d o g t k _ w i d g e t _ s e t _ s i z e _ r e q u e s t ( g−>d r a w i n g _ a r e a , ws_x , ws_y ) ; // N e c e s a r i o p a r a c a p t u r a r l o s c l i c k d e l r a t o n g t k _ w i d g e t _ s e t _ e v e n t s ( g−>d r a w i n g _ a r e a , g t k _ w i d g e t _ g e t _ e v e n t s ( g−>d r a w i n g _ a r e a ) | GDK_BUTTON_PRESS_MASK) ; // Any ad imos e l n u e v o w i d g e t a l g r i d g t k _ g r i d _ a t t a c h _ n e x t _ t o ( g−>g r i d , g−>d r a w i n g _ a r e a , g−>b t n _ q u i t , GTK_POS_BOTTOM, 1 , 1 ) ; s t a t i c g b o o l e a n draw_cb ( G t k W i d g e t ∗ w i d g e t , g) { /∗ C l e a r s c r e e n ∗/ cairo_set_source_rgb ( cr , 0 , 0 , 0) ; cairo_paint ( cr ) ; cairo_t ∗ cr , struct gui ∗ /∗ TODO ∗ D i b u j a e l mundo s a b i e n d o que e l c o d i g o de a b a j o d i b u j a un rectangulo ∗ b l a n c o en l a s c o o r d e n a d a s ( 2 , 3 ) de 5 x4 p i x e l e s de tamanyo ∗/ cairo_set_source_rgb ( cr , 1 , 1 , 1) ; c a i r o _ r e c t a n g l e ( cr , 2 , 3 , 5 , 4) ; c a i r o _ f i l l ( cr ) ; 9 10 11 12 13 14 15 return 16 } false ; Timer Portada Sobre GTK Bucle principal Señales Callbacks Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio 1 s t a t i c v o i d pause_cb ( G t k W i d g e t ∗ w i d g e t , s t r u c t g u i ∗ g ) 2 { 3 i f ( ! g−>r u n ) 4 g_timeout_add (VEL_MS, t i m e r _ c b , g ) ; // Co nec t am os l a nuevo 5 g−>r u n = ! g−>r u n ; 6 } 1 s t a t i c gboolean timer_cb ( g p o i n t e r g u i ) 2 { 3 s t r u c t g u i ∗g = ( s t r u c t g u i ∗) g u i ; 4 s t e p _ c b ( NULL , g ) ; 5 /∗ S i d e v u e l v e f a l s e , l a s e n y a l s e d e s c o n e c t a y e l vuelve 6 ∗ a llamar 7 ∗/ 8 r e t u r n g−>r u n ; 9 } s e n y a l de c a l l b a c k no s e Ejercicio Portada Sobre GTK Bucle principal Señales Callbacks Glade y builder Ejemplo simple Juego de la vida main gui object drawing area Timer Ejercicio 1 Estudia detenidamente el código proporcionado 2 Crea el fichero “builder.ui” con Glade. Presta atención al identificador de cada widget 3 Completa el código de “gui.c”. Las zonas a completar están marcadas con un TODO