Programación de GUI en C

De Wiki do Ciclo ASIR do IES de Rodeira

Índice

Programación Orientada a Eventos

Para facer programas que funcionen en entornos de ventanas normalmente se recurre á programación orientada a eventos ou sucesos. Este tipo de programación consiste en construir o interface de usuario e esperar unha interacción co mesmo (un evento), cando esa interacción se produce se executa a función que se encargará de tratar o evento.

Na programación orientada a eventos os programas son prácticamente un conxunto de funcións de tratamento de eventos encargadas de facer que a aplicación responda do xeito adecuado ás distintas accións do usuario.

O comentido dos programas que funcionan en entornos orientados a eventos é recoller a acción do usuario (evento) e executar a función de tratamento correspondente mentras non se reciba o evento de finalización de programa.

O sistema Windows

Ficheiro:WindowsXP.gif
Microsoft Windows XP

O entorno orientado a eventos máis coñecido é Microsoft Windows. Este entorno ten o sistema gráfico de ventanas incluído no núcleo do sistema operativo (forma parte do propio sistema operativo) e proporciona ás aplicacións unha API de programación coñecida como WinAPI para crear os distintos obxectos gráficos, instalar as funcións de tratamento de eventos ... etc. Hoxe en día non se emplea esta API directamente para crear as interfaces de usuario, se non que se utilizan sistemas de desenvolvemento rápido de aplicacións (RAD) e visuais, como pode ser Visual C++ ou Borland C++ que empregan librerías de maior nivel. Como veremos existen algunhas librerías das comúnmente utilizadas no mundo Unix, como Qt, GTK+ ou wxwidgets que teñen unha versión para Windows, o que nos permitirá facer programas portables entre os dous sistemas.



O sistema X-Window

Ficheiro:Motif.jpg
Xestor de Ventanas Motif

O sistema XWindow é o entorno de ventanas tradicional nos sistemas Unix. É unha aplicación que funciona en modo usuario (é unha aplicación máis que funciona por enriba do sistema operativo) e a súa característica máis salientable é que sigue o esquema cliente/servidor.

Ficheiro:Olwm.gif
Xestor de Ventanas OpenLook

No entorno XWindow as aplicacións son programas cliente que lle solicitan ó servidor (o sistema X-Window) que realice as distintas operacións sobre a pantalla (debuxar unha ventana, un botón, ...). Esto permite que a aplicación poda estar executándose nun ordenador remoto e a saída podemos obtela no ordenador local, e incluso dende unha mesma estación de traballo executar aplicacións en distintas máquinas obtendo a saída de todas elas na mesma estación de traballo que está a executar o servidor XWindow.

Tamén ofrece a posibilidade de ter ordenadores con poucos recursos (a partir dun 486 con 32Mb de RAM) como terminal gráfica, e executar os programas nun servidor central, con todo o que implica en canto a aforro en mantemento e inversión en hardware.

Ficheiro:Fluxbox.jpg
Xestor de Ventanas fluxbox

A API de programación empregada por XWindow é moito máis complexa que a de Windows sobre todo debido a esta arquitectura cliente/servidor. A comunicación entre os clientes X (os programas) e o servidor X empregan un protocolo chamado protocolo X, para manexar este protocolo se emprega a librería Xlib.

Ficheiro:Blackbox.jpg
Xestor de Ventanas blackbox

Poderíamos decir que Xlib é a linguaxe ensamblador de X, xa que proporciona soamente funcións moi simples o que fai a programación difícil e laboriosa. Para solucionar este problema se empregan librerías de maior nivel, como Xt Intrinsics.

Normalmente os clientes X non se comunican directamente co servidor X, se non que o fan a través dun Xestor de Ventanas.

O xestor de ventanas encárgase de crear as ventanas (e o resto dos obxectos gráficos) e de xestionar a súa apariencia, mentras que o servidor X encárgase de debuxar. Polo tanto, a apariencia das aplicacións depende do xestor de ventanas que estemos a utilizar.

Ficheiro:Fvwm.gif
Xestor de Ventanas fvwm

Os xestores de ventanas normalmente proporcionan tamén unhas librerías que nos facilitan enormemente o traballo de escribir aplicacións. Entre os xestores de ventanas máis coñecidos (por ser os primeiros) atópanse OpenLook (olwm) e Motif (mwm).

En Unix existe unha gran variedade de xestores de ventanas, con consumo de recursos moi variados. Deste xeito teremos xestores que necesitan máquinas con moita memoria e potencia e xestores que necesitan moi pouco.

Ficheiro:Afterstep.jpg
Xestor de Ventanas AfterStep

Hoxe en día a tendencia é utilizar unha terceira capa no acceso ó servidor X: O entorno de escritorio.

O servidor de ventanas encárgase do aspecto e creación das ventanas, e o entorno de escritorio trata de unificar e comunicar entre sí as distintas aplicacións que están a utilizar o servidor X, facilitando cousas como o cortar e pegar ou o uso dunhas aplicacións por parte de outras. Unha das facilidades aportadas polos xestores de escritorio é, por exemplo, a posibilidade de colocar iconas para lanzar as aplicacións en calquera posición da pantalla (agora denominada escritorio).

Ficheiro:Kde.png
Escritorio KDE

Os entornos de escritorio precisan de moitos máis recursos que un simple xestor de ventanas, (aínda que algúns son bastante modestos en canto a consumo de memoria e procesador), o que fai que en máquinas con pouca memoria ou procesadores lentos sexa moito mellor executar únicamente un xestor de ventanas.

Os entornos de escritorio máis comúnmente utilizados son KDE e Gnome. Ambos se atopan nunha etapa de acelerado desenvolvemento, incorporando contínuamente novas características.

Ficheiro:Gnome.jpg
Escritorio Gnome

As librerías empregadas por estos entornos de escritorio proporcionan ás aplicacións un xeito moi rápido e simple de crear entornos de usuario, o que nos permite olvidarnos completamente de Xlib ou Xt Intrinsics:

Ficheiro:Cde.jpg
Escritorio CDE

KDE emprega a librería Qt escrita en C++ o que implica o uso necesariamente de C++ cando se escriben aplicacións Qt.

Gnome emprega a librería GTK+. Esta librería está escrita en C utilizando técnicas de orientación a obxecto, o que implica que se pode programar en C. GTK+ ofrece ademáis a posibilidade de programar en moitas outras linguaxes de programación.

Ficheiro:Xfce.jpg
Escritorio Xfce

Ambas librerías teñen una versión que funciona en Microsoft Windows, o que proporciona ademáis a posibilidade de compilar sen apenas cambios as aplicacións para o seu funcionamento en entorno Unix ou Windows.

Outros entornos de escritorio coñecidos e cunhas necesidades de procesador e memoria máis pequenas son CDE e, sobre todo xfce.

Xfce é un entorno de escritorio (en realidade é casi máis un entorno de ventanas) feito coa librería GTK+ extremadamente lixeiro e cun aspecto gráfico bastante notable.

Programación Visual

A programación en entornos de ventanas orientados a eventos pode facerse do xeito tradicional, cun editor de textos e escribindo as chamadas ó API do sistema para crear os distintos obxectos gráficos (ventanas, botóns, listas de texto, debuxos ...), pero hoxe en día se utilizan ferramentas RAD (Rapid Application Development) para o desenvolvemento rápido de aplicacións.

As ferramentas RAD que se utilizan son normalmente entornos IDE (Integrated Development Environment) de desenvolvemento que integran o editor de textos, o compilador, o depurador e o deseñador de interfaces.

Os deseñadores de interfaces son ferramentas que permiten deseñar o GUI (Graphic User Interface) do usuario dun xeito visual. Disporemos dunha caixa de ferramentas donde poderemos seleccionar os distintos obxectos que poden formar parte do interface do usuario, e os iremos colocando no interfaz no sitio que consideremos axeitado.

Estes deseñadores permitirán tamén indicar qué funcións se executarán en resposta os distintos eventos que desexamos controlar (pulsación de botóns, movemento do cursor, cambio de tamaño da ventana ... ) e no caso dos IDE incluso escribir as funcións.

As ferramentas RAD máis coñecidas e utilizadas para programar con C son, nos entornos Windows, Microsoft Visual Studio C++ e Borland C++ Builder, e nos entornos Linux KDevelop, QT Designer e Glade. Tanto Glade como QT Designer teñen unha versión que traballa baixo Windows, o que fai posible o desenvolvemento de aplicacións multiplataforma.

Microsoft Visual C++ e Borland C++ Builder

Ficheiro:Visual.jpg
Microsoft Visual C++: Deseño do GUI

O Microsoft Visual C++ englóbase na ferramenta de programación Microsoft Visual Studio .NET que permite desenvolver programas en outras linguaxes como C#, J# ou Visual Basic. Ten integradas todas as utilidades necesarias para realizar aplicacións baixo Windows (editor de texto, depurador, deseñador de interfaces...).

Ficheiro:Visual1.jpg
Microsoft Visual C++: Edición de código

Visual Studio .NET ten asistentes que nos permitirán elexir entre distintos tipos de aplicacións a desenvolver, de xeito que se faga automáticamente a construcción do esqueleto da aplicación.

Este entorno de desenvolvemento é probablemente o máis utilizado baixo Windows, aínda que históricamente os entornos de programación de Borland sempre gozaran de moita popularidade e unha ben merecida fama.

Hoxe en día o programa RAD de Borland para traballar en C chámase Borland C++ Builder. A súa orixe está noutra ferramenta para programar en PASCAL chamada Delphi de gran éxito, e garda gran compatibilidade con ela.

Ficheiro:Borland.jpg
Borland C++ Builder: Deseño do GUI

Ten todas as ferramentas integradas nun único programa, e ademáis unha serie de obxectos gráficos propios (compoñentes) que podemos incluír nos nosos programas. Fai algún tempo chegouse a distribuír unha versión para Linux chamada Kylix.

Nos dous entornos se xenerará automáticamente código C para o inicio da aplicación e o funcionamento do interface deseñado. Posteriormente é necesario ir ampliando e modificando ese código automático.

Na imaxe de mostra do Microsoft Visual C++ de edición de código pode observarse como é o bucle de tratamento de eventos xenérico dos sistemas Windows.

Ficheiro:Borland1.jpg
Borland C++ Builder: Edición e código

Tanto Visual Studio como Borland Builder son ferramentas moi potentes e axeitadas para o desenvolvemento de aplicacións baixo Windows, pero teñen dous inconvintes:

  • Son aplicacións propietarias, e polo tanto atan ó programador a compañía que vende o producto. Si a compañía decide retirar ou deixar de manter o producto o programador quedaría completamente desasistido.
  • Son ferramentas exclusivamente para Windows, o que impide o desenvolvemento de aplicacións multiplataforma.

A continuación falaremos de outras dúas ferramentas que, ademáis de ser software libre (o que garante ó programador a independencia do distribuidor do producto) son multiplataforma, xa que dispoñen tanto de versión para Linux como para Windows.

KDevelop, Qt Designer e a librería Qt

Ficheiro:Kdevelop.jpg
KDevelop: Edición do Código

A librería Qt foi creada pola empresa TrollTech e empregada na construcción do entorno de escritorio KDE. A súa licencia é GPL, pero si se queren desenvolver aplicacións propietarias é posible (e necesario) adquirir unha licencia.

Qt está construída en C++, de modo que o natural é programar en C++, aínda que existe a posibilidade de facelo en outras linguaxes.

O entorno de desenvolvemento chámase KDevelop e é un entorno moi completo que ten incluído o editor de textos, o depurador ... etc. O deseñador de interfaces é unha ferramenta independente chamada QT Designer que está integrada no propio IDE.

Ficheiro:Kdevelop1.jpg
QT Designer: Deseño do GUI

KDevelop ten un asistente para crear distintos tipos de aplicacións, e xenerará automáticamente tanto o código inicial da aplicación como o interface de usuario típica para o caso elexido.

Qt Designer non é so un deseñador de interfaces, senon que tamén é unha ferramenta visual de creación de aplicacións, integrando tamén un pequeno editor de texto e xerando o código fonte da aplicación a partir do deseño visual realizado.

A librería Qt e QT Designer teñen versión para Linux, OS/X, Windows e PDA. Existe unha versión de entorno escritorio para pequenos dispositivos (como os PDA ou teléfonos móviles) feita en Qt chamada QTopía.

A súa condición de software libre, o funcionar en distintas plataformas e o seu inmellorable aspecto gráfico xunto coa facilidade e rapidez no desenvolvemento de aplicacións fan de QT Designer e de KDevelop dúas ferramentas moi recomendables.

Trolltech facilita unha completa documentación para programar utilizando Qt e Qt-Designer.

Programación con QT Designer

Aquí podes ver un exemplo paso a paso de aplicación feita con Qt Designer

Glade e a librería GTK+

A librería GTK+ (Gimp ToolKit) foi escrita inicialmente para escribir o programa de deseño gráfico The Gimp, e logo adoptada para a elaboración do entorno de escritorio Gnome.

GTK+ está desenvolvida na linguaxe C utilizando técnicas de orientación de obxectos, proporciona múltiples elementos gráficos para as aplicacións (chamados Widgets) e o control dos eventos de usuario (chamados sinais) mediante un interfaz simple que permite asociar unha función a cada posible evento.

A librería GTK+ está escrita sobre outras dúas librerías:

  • GDK (Graphics Drawing Kit): Proporciona as funcións básicas para o tratamento de eventos e o debuxo dos Widgets por parte de GTK+.
  • GLib : E unha librería de propósito xeral proporcionando tipos de datos específicos e funcións para o manexo de listas, ficheiros, táboas hash, XML, ... etc.

As aplicacións GTK+ siguen o esquema seguinte: <c> int main(int argc,char *argv[]) {

 // Inicialización
 gtk_init(&argc,&argv);
 
 // Creación e inicialización dos Widgets (Ventanas e os seus obxectos incluidos)
 widget=gtk_..._new(...);
 // Conexión das funcións de tratamentos de eventos
 gtk_signal_connect(...);
 // Visualización da ventana co seu contido
 gtk_widget_show_all(...);
 // Bucle principal de tratamento de eventos
 gtk_main();
 return 0;

} </c> Básicamente, un programa con GTK+ consiste na creación co interface (que como veremos pode ser realizado coa axuda da libraría libglade) e na realización das distintas funcións de xestión de eventos do usuario ou funcións de tratamento das sinais.

A colocación dos obxectos no interior dun contenedor (un obxecto que pode conter outro, como por exemplo unha ventana) realízase mediante os administradores de deseño. Estes administradores de deseño son similares ós empregados na linguaxe Java. Estes administradores permiten que o contido das ventanas se recoloque automáticamente cando éstas cambian de tamaño:

  • Caixas horizontais (hbox): O espacio para os obxectos está dividido nunha serie de celdas dispostas horizontalmente.
  • Caixas verticais (vbox): O espacio para os obxectos está dividido nunha serie de celdas dispostas verticalmente.
  • Táboas (table): Os espacio para os obxectos está dividido nunha serie de filas e columnas.
  • Posicións Fixas (fixed): Os elementos poden colocarse libremente donde se queira nunha posición fixa. O cambio de tamaño da ventana non modifica a posición dos elementos.

En Internet está dispoñible a documentación da API de programación e un tutorial con GTK+, GLib e GDK.

Multitarefa con GTK+

As aplicacións con multitarefa teñen o problema do acceso a un recurso compartido: A pantalla gráfica. Si facemos un fork nuha aplicación GTK cada un dos procesos será unha 'copia' exacta do outro. Esto quere decir que cada proceso pode ter información distinta almacenada nun obxecto gráfico que en principio parecería común (por exemplo un campo de edición de texto). Outro problema é que varios procesos poden querer modificar o contido da pantalla ó mesmo tempo, e incluso poñer á vez distintos valores no mesmo sitio.

Esto fai evidente que é necesario controlar o acceso á pantalla por parte de múltiples procesos ou fíos en execución. No caso dos procesos pode bastar con asegurarse de que só un dos procesos accede ós obxectos gráficos.

<c> g_idle_add g_idle_add_full g_timeout_add g_thread_init g_thread_create G_LOCK G_UNLOCK gdk_threads_init gdk_threads_enter gdk_threads_leave gdk_input_add XFlush gdk_flush </c>


<c> /* O seguinte programa amosa nunha ventana o resultado da execución dun comando ó mesmo tempo que o comando se está a executar. Para facer eso, se comunica cunha tubería (pipe) os dous procesos:

        o encargado de executar o comando e o encargado de visualizar a saída.

Para que o programa que visualice a saida continúe refrescando o interface gráfico (respondendo ós eventos) é necesario que a lectura se realice ó mesmo tempo que o tratamento de eventos, así que facemos unha lectura en cada iteración do bucle de tratamento (gtk_main()) instalando unha función idle que lee da tubería e pon o leido na caixa de texto do interface.

  • /
  1. include <stdio.h>
  2. include <string.h>
  3. include <stdlib.h>
  4. include <unistd.h>
  5. include <sys/types.h>
  6. include <sys/stat.h>
  7. include <fcntl.h>
  8. include <gtk/gtk.h>
  9. include <gdk/gdkkeysyms.h>
  10. include <glade/glade.h>

GladeXML *__xml; GtkTextBuffer *__tbuf; GtkWidget *__btn; int __f[2];

void on_Executar_clicked(GtkButton *btn,gpointer data); void pai(void); void fillo(char *txt);

gboolean leepipe(gpointer kk) {

 char buffer[128];
 int n;
 gboolean ret=FALSE;
 n=read(__f[0],buffer,127);
 buffer[n]=0;
 if (n>0)
 {
    gtk_text_buffer_insert_at_cursor(__tbuf,buffer,strlen(buffer));
    ret=TRUE;
 }
 else
 {
    gtk_widget_set_sensitive(__btn,TRUE);
    close(__f[0]);
    wait(NULL);
 }
 return ret;

}

void pai(void) {

 GtkWidget *txt;
 char buffer[2048];
 int n;
 close(__f[1]);
 __btn=glade_xml_get_widget(__xml,"Executar");
 gtk_widget_set_sensitive(__btn,FALSE);
 txt=glade_xml_get_widget(__xml,"vista");
 __tbuf=gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
 gtk_text_buffer_set_text(__tbuf,"",0);
 g_idle_add(leepipe,NULL);

}

void fillo(char *txt) {

 close(__f[0]);
 close(STDOUT_FILENO);
 dup(__f[1]);
 system(txt);
 close(__f[1]);
 _exit(0);

}

void on_Executar_clicked(GtkButton *btn,gpointer data) {

 int resul;
 pid_t pid;
 GtkWidget *txt;
 char *texto;
 resul=pipe(__f);
 if (resul!=-1)
 {
   txt=glade_xml_get_widget(__xml,"entrada");
   texto=gtk_entry_get_text(GTK_ENTRY(txt));
   pid=fork();
   switch(pid)
   {
     case 0: fillo(texto);
             break;
     case -1: //erro
              close(__f[0]);
              close(__f[1]);
              gtk_main_quit();
              break;
     default: pai();
              break;
   }
 }

}

int main(int argc, char *argv[]) {

  gtk_init(&argc, &argv);
  __xml = glade_xml_new("exec.glade", NULL, NULL);
  glade_xml_signal_autoconnect(__xml);
  gtk_main();
  return 0;

}

</c>

Programación con Glade

Ficheiro:Glade.jpg
Deseñador de Interfaces Glade

Glade é unha ferramenta que permite deseñar o interface gráfico de usuario (GUI) dunha aplicación de xeito visual. O resultado pode obterse ben en forma de código fonte, ou nunha descrición do interface en formato XML. Este ficheiro XML pode ser cargado e interpretado dinámicamente facendo uso da librería libglade.

O código fonte xerado de modo automático por Glade non é demasiado claro, e ademáis tendo a posibilidade de crear o interface de xeito dinámico con libglade a partir do ficheiro XML non ten demasiado sentido. A súa posible utilidade está nada máis que na persoalización en algúns casos pouco comúns.

O xeito recomendado de traballar co Glade é a xeración do ficherio XML (con extensión .glade) que será cargado na aplicación posteriormente coa función de libglade glade_xml_new.

En Glade bótase de menos unha opción de vista previa do interface, e máis cando pode conseguirse fácilmente:

<c>

  1. include <gtk/gtk.h>
  2. include <glade/glade.h>

int main(int argc,char *argv[]) {

  gtk_init(&argc,&argv);
  
  __gui=glade_xml_new(argv[1],NULL,NULL);
  glade_xml_signal_autoconnect(__gui);
  gtk_main();

}

</c>

Co programa anterior (vistaprevia.c) bastaría poñer vistaprevia ficheiro.glade para ver o aspecto final na pantalla do GUI definido en ficheiro.glade.

Deste xeito a función main nos programas feitos con libglade será a seguinte:

<c> int main(int argc,char *argv[]) {

  gtk_init(&argc,&argv);
  
  __gui=glade_xml_new(/*"ficheiro.glade da aplicación"*/,NULL,NULL);
  glade_xml_signal_autoconnect(__gui);
  /*
  Posible inicialización de datos (carga inicial de datos no GUI, por exemplo*/
  */
  gtk_main();

} </c>

A librería libGlade

A librería libGlade permite cargar un ficheiro XML coa descrición do GUI da aplicación, instalar as funcións de tratamento de sinais e obter os distintos obxectos que compoñen o interface do usuario a partir do seu nome.

O ficheiro XML se xerará normalmente cunha aplicación de deseño visual, como glade ou gazpacho.

Deste xeito, unha aplicación con libGlade limítase a inicialización da aplicación, carga do interface e instalación de sinais e á escritura das funcións de tratamento para os distintos eventos como se pode ver nos exemplos do apartado anterior.

As funcións máis interesantes ofrecidas pola librería libglade son as seguintes:

<c> /* Carga o interface do ficheiro XML. - Si se indica root so cargará o obxecto root e os seus fillos. NULL cargará ficheiroXML completo. - domain normalmente será NULL. Úsase para a traducción da aplicación a outros idiomas mediante a utilidade gettext.

  • /

GladeXML *glade_xml_new(char *ficheiroXML,char *root,char *domain);

/* Conecta as sinais indicadas no XML ás funcións tamén especificadas no XML.

  • /

void glade_xml_signal_autoconnect (GladeXML *xml);

/* Obtén o obxecto gráfico (widget) do ficheiro XML a partir do seu nome Posteriormente poderemos manipula-lo coas funcións propias do obxecto recuperado.

  • /

GtkWidget *glade_xml_get_widget(GladeXML *xml,char *nomeObxecto);

</c>

Programación Multiplataforma con libGlade

Modelo:Programación Multiplataforma con Glade

Exemplos

  • Saúdo
  • Ordear Lista
  • Cálculo Media
  • Números Romanos