Poesía Binaria

Interfaces Gráficos en Linux con gtkmm 1 [Hola Mundo]

Aunque a veces, cuando nos dedicamos a programar en Linux, recurrimos a la consola (ya que muchos de nosotros tenemos siempre una abierta), a veces es interesante crear un Interfaz Gráfico de Usuario (en inglés GUI, Graphical User Interface), para ello, si no queremos complicarnos demasiado tenemos dos opciones: Gtk+ y Qt.

Bien, vamos con Gtk+, está muy extendido y escrita en C, aunque aquí hablaré de una interfaz de Gtk+ para C++ llamada gtkmm, que nos proporciona las clases necesarias para jugar con la potencia de Gtk+ de una forma un poco más amigable (ya que podemos crear un botón, una etiqueta o una ventana como objetos de C++, hará todo un poco más intuitivo).

Una primera prueba que me gusta hacer es poner todo el código junto, sin mucha organización, para crear un «hola mundo», para ver más o menos a qué nos enfrentamos y cómo, si vamos a tener que escribir mucho y esas cosas:

[ holamundo.cpp]

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
27
28
29
30
#include <gtkmm.h>

using namespace Gtk;

int main(int argc, char *argv[])
{
  // Inicialización GTK
  Main entorno(argc, argv);

  // Declaración de objetos
  Window ventana;
  Label etiqueta;

  // Características de la ventana
  ventana.set_title("Hola Mundo!");
  ventana.set_border_width(5);
  ventana.set_default_size(400, 200);

  // Etiqueta
  etiqueta.set_text("Hola Mundo!!");
  ventana.add(etiqueta);

  // Mostrar todo
  ventana.show_all_children();

  // Ejecutar GUI
  entorno.run(ventana);

  return 0;
}

Para compilar debemos hacer lo siguiente:

$ g++ -o holamundo holamundo.cpp `pkg-config –cflags gtkmm-2.4` `pkg-config –libs gtkmm-2.4`

Eso sí, debemos sustituir el 2.4 por la versión de gtkmm que tengamos instalada. Para ver cuál es, podemos hacer lo siguiente:

$ pkg-config –list-all | grep gtkmm

Bien, una vez hecho esto, vamos a intentar adquirir una metodología de programación que nos permita reaprovechar código, y tenerlo todo organizado, vamos a crear archivos .h y .cpp:

[ hworld.h ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef _HWORLD_H
#define _HWORLD_H

#include <gtkmm.h>

using namespace Gtk;

class HolaMundo : public Window
{
 public:
  HolaMundo();
  ~HolaMundo();

  Label etiqueta;
};

#endif

[ hworld.cpp ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "hworld.h"

HolaMundo::HolaMundo()
{
  this->set_title("Hola Mundo!");
  this->set_border_width(5);
  this->set_default_size(400, 200);

  etiqueta.set_text("Hola Mundo!!");
  this->add(etiqueta);

  this->show_all_children();
}

HolaMundo::~HolaMundo()
{
}

[ hellomain.cpp ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <gtkmm.h>
#include "hworld.h"

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

  // Creamos la ventana
  HolaMundo hmundo;

  // Ejecutamos
  entorno.run(hmundo);

  return 0;
}

Ahora compilamos con:

$ g++ -o hellom hellomain.cpp hworld.cpp `pkg-config –cflags gtkmm-2.4` `pkg-config –libs gtkmm-2.4`

Hará lo mismo, ¡vaya tontería! pero lo tendremos todo mucho más organizado, hemos introducido todo lo referente a la ventana en una misma clase.

Una pequeña nota: escribo arriba using namespace Gtk para no repetir todo el rato Gtk::[Tipo] , Gtk::[Método], ya que todo lo que estoy utilizando hasta ahora pertenece al espacio Gtk.

Ahora vamos a hacer algo más complicado, vamos a utilizar algunos elementos más y vamos a introducir algún botón, para que se desempeñe alguna acción:
[ hworld.h ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef _HWORLD_H
#define _HWORLD_H

#include <gtkmm.h>

using namespace Gtk;

class HolaMundo : public Window
{
 public:
  HolaMundo();
  ~HolaMundo();


  void click_salir();
  void click_mensaje();

  VBox cajaV;
  HButtonBox botonera;
  Label etiqueta;
  Button *botonSalir, *botonMensaje;
};

#endif

Creamos un objeto derivado de Gtk::Window en el que incluimos los elementos que habrá en la ventana:

Definimos dos métodos para los eventos click_salir() y click_mensaje().

Los botones los he declarado como punteros para demostrar cómo podemos trabajar con objetos de este tipo.

[ hworld.cpp ]

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include "hworld.h"
#include <sstream>      // Para hacer string<<int

HolaMundo::HolaMundo()
{
  // Configuro la ventana
  set_title("Hola Mundo!");
  set_border_width(5);
  set_default_size(400, 200);

  // Configuro la etiqueta
  etiqueta.set_text("Hola Mundo!!");

  // Configuro la botonera
  botonSalir=new Button(Stock::QUIT);
  botonMensaje=new Button("Mensaje");

  botonSalir->signal_clicked().connect(sigc::mem_fun(*this, &HolaMundo::click_salir));
  botonMensaje->signal_clicked().connect(sigc::mem_fun(*this, &HolaMundo::click_mensaje));

  botonera.pack_start(*botonSalir, PACK_SHRINK);
  botonera.pack_start(*botonMensaje, PACK_SHRINK);

  // Configuro la división
  add(cajaV);
  cajaV.pack_start(etiqueta, PACK_EXPAND_WIDGET); // Esto ocupará el máximo tamaño
  cajaV.pack_start(botonera, PACK_SHRINK);

  show_all_children();
}

HolaMundo::~HolaMundo()
{
  delete botonSalir;
  delete botonMensaje;
}

void HolaMundo::click_mensaje()
{
  int res;
  std::stringstream ss;
  MessageDialog dialog("Esto es un mensaje emergente");
  res=dialog.run();

  if (res==RESPONSE_OK)
    etiqueta.set_text("Has dicho OK");
  else if (res==RESPONSE_DELETE_EVENT)
    etiqueta.set_text("Has cancelado el diálogo");
  else
    {
      ss<<"Has respondido otra cosa: "<<res;
      etiqueta.set_text(ss.str().data());
    }
}

void HolaMundo::click_salir()
{
  hide();
}

Vemos cómo en el constructor hemos configurado todos los elementos de la ventana, vemos cómo en la botonera y la cajaV, para añadir objetos se ha utilizado el método pack_start, las propiedades Gtk::PACK_SHRINK (encogerá el objeto contenedor) y Gtk::PACK_EXPAND_WIDGET (lo expanderá)

Pero la cajaV la añadiremos a la ventana con el método add.

Los botones los creamos con new (recordamos que eran punteros), tenemos muchos botones de Stock predefinidos, y podemos crear uno con esta propiedad (todos están en Gtk::Stock::XXXXX), aunque también podemos crearlos directamente con un texto.

Para conectar los eventos (de los botones por ejemplo) usamos connect(), como parámetro podemos incluir una función (con sigc::ptr_fun(&funcion)) o un método de clase (con sigc::mem_fun(*objeto, &Clase::metodo)), será la función o método que llamaremos cuando se genere el evento (en este ejemplo será el click sobre el botón).

Además, incluyo una caja de diálogo en la que luego distinguimos si se cierra por el botón aceptar (Gtk::RESPONSE_OK), o cerrando la ventana (Gtk::RESPONSE_DELETE_EVENT) modificando el texto del Label.

[ hellomain.cpp ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <gtkmm.h>
#include "hworld.h"

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

  // Creamos la ventana
  HolaMundo hmundo;

  // Ejecutamos
  entorno.run(hmundo);

  return 0;
}

Como vemos, el programa principal es exactamente igual que el anterior, como la compilación del proyecto:

g++ -o hellom hellomain.cpp hworld.cpp `pkg-config –cflags gtkmm-2.4` `pkg-config –libs gtkmm-2.4`

También podría interesarte....