Manual Completo de Programación con GTK+

1. Introducción a GTK+

GTK+ (GIMP Toolkit) es un toolkit multiplataforma para crear interfaces gráficas de usuario (GUI) originalmente desarrollado para el programa GIMP.

1.1 Características principales

1.2 Instalación

Linux (Debian/Ubuntu):

sudo apt-get install libgtk-3-dev

Linux (Fedora):

sudo dnf install gtk3-devel

macOS (Homebrew):

brew install gtk+3

Windows:

Descargar MSYS2 desde msys2.org y luego:

pacman -S mingw-w64-x86_64-gtk3

1.3 Compilación básica

// Compilar en Linux/macOS
gcc programa.c -o programa `pkg-config --cflags --libs gtk+-3.0`

// Compilar en Windows (MSYS2)
gcc programa.c -o programa.exe `pkg-config --cflags --libs gtk+-3.0`

2. Conceptos Básicos

2.1 Hola Mundo en GTK+

#include <gtk/gtk.h>

// Función callback para el botón
static void on_button_clicked(GtkWidget *widget, gpointer data) {
    g_print("¡Botón clickeado!\n");
}

int main(int argc, char *argv[]) {
    // Inicializar GTK
    gtk_init(&argc, &argv);
    
    // Crear ventana principal
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Hola GTK+");
    gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
    
    // Conectar señal "destroy" para cerrar la aplicación
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    
    // Crear un botón
    GtkWidget *button = gtk_button_new_with_label("Presióname");
    g_signal_connect(button, "clicked", G_CALLBACK(on_button_clicked), NULL);
    
    // Añadir botón a la ventana
    gtk_container_add(GTK_CONTAINER(window), button);
    
    // Mostrar todos los widgets
    gtk_widget_show_all(window);
    
    // Bucle principal de GTK
    gtk_main();
    
    return 0;
}

2.2 Jerarquía de objetos

GObject (base fundamental)
├── GInitiallyUnowned
│   └── GtkWidget (base para todos los widgets)
│       ├── GtkContainer (puede contener otros widgets)
│       │   ├── GtkBin (contiene un solo hijo)
│       │   │   ├── GtkWindow
│       │   │   ├── GtkButton
│       │   │   └── ...
│       │   ├── GtkBox (organización horizontal/vertical)
│       │   ├── GtkGrid (organización en cuadrícula)
│       │   └── ...
│       ├── GtkLabel (muestra texto)
│       ├── GtkEntry (entrada de texto)
│       └── ...
└── ...

2.3 Sistema de señales

GTK+ usa un sistema de señales y callbacks para manejar eventos:

// Conectar una señal a un callback
g_signal_connect(widget, "signal-name", G_CALLBACK(callback_function), user_data);

// Ejemplo:
g_signal_connect(button, "clicked", G_CALLBACK(on_button_clicked), NULL);

3. Widgets Básicos

3.1 GtkWindow (Ventana)

#include <gtk/gtk.h>

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    // Crear ventana principal
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Ventana GTK+");
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    
    // Configurar borde
    gtk_container_set_border_width(GTK_CONTAINER(window), 10);
    
    // Conectar señal de cierre
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    
    gtk_widget_show(window);
    gtk_main();
    
    return 0;
}

3.2 GtkButton (Botón)

#include <gtk/gtk.h>

void button_clicked(GtkWidget *widget, gpointer data) {
    g_print("Botón %s presionado\n", (char*)data);
}

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Botones GTK+");
    
    // Crear caja vertical
    GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
    gtk_container_add(GTK_CONTAINER(window), box);
    
    // Crear botones
    GtkWidget *button1 = gtk_button_new_with_label("Botón 1");
    GtkWidget *button2 = gtk_button_new_with_label("Botón 2");
    
    // Conectar señales
    g_signal_connect(button1, "clicked", G_CALLBACK(button_clicked), "Uno");
    g_signal_connect(button2, "clicked", G_CALLBACK(button_clicked), "Dos");
    
    // Añadir botones a la caja
    gtk_box_pack_start(GTK_BOX(box), button1, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(box), button2, TRUE, TRUE, 0);
    
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_show_all(window);
    gtk_main();
    
    return 0;
}

3.3 GtkLabel (Etiqueta)

#include <gtk/gtk.h>

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Etiquetas GTK+");
    
    // Crear etiqueta
    GtkWidget *label = gtk_label_new("¡Hola GTK+!");
    
    // Configurar etiqueta
    gtk_label_set_markup(GTK_LABEL(label), 
        "<span font='16' foreground='blue'>Texto formateado</span>");
    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
    
    gtk_container_add(GTK_CONTAINER(window), label);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_show_all(window);
    gtk_main();
    
    return 0;
}

3.4 GtkEntry (Campo de entrada)

#include <gtk/gtk.h>

void on_entry_activated(GtkEntry *entry, gpointer data) {
    const gchar *text = gtk_entry_get_text(entry);
    g_print("Texto ingresado: %s\n", text);
}

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Entrada GTK+");
    
    GtkWidget *entry = gtk_entry_new();
    gtk_entry_set_placeholder_text(GTK_ENTRY(entry), "Escribe algo...");
    gtk_entry_set_max_length(GTK_ENTRY(entry), 50);
    
    g_signal_connect(entry, "activate", G_CALLBACK(on_entry_activated), NULL);
    gtk_container_add(GTK_CONTAINER(window), entry);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_show_all(window);
    gtk_main();
    
    return 0;
}

4. Layouts y Contenedores

4.1 GtkBox (Caja vertical/horizontal)

#include <gtk/gtk.h>

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Cajas GTK+");
    gtk_container_set_border_width(GTK_CONTAINER(window), 10);
    
    // Crear caja vertical con espaciado de 5 píxeles
    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
    
    // Añadir widgets a la caja
    GtkWidget *label = gtk_label_new("Caja Vertical");
    GtkWidget *button1 = gtk_button_new_with_label("Botón 1");
    GtkWidget *button2 = gtk_button_new_with_label("Botón 2");
    
    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), button1, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), button2, TRUE, TRUE, 0);
    
    gtk_container_add(GTK_CONTAINER(window), vbox);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_show_all(window);
    gtk_main();
    
    return 0;
}

4.2 GtkGrid (Cuadrícula)

#include <gtk/gtk.h>

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Grid GTK+");
    gtk_container_set_border_width(GTK_CONTAINER(window), 10);
    
    // Crear grid
    GtkWidget *grid = gtk_grid_new();
    gtk_grid_set_row_spacing(GTK_GRID(grid), 5);
    gtk_grid_set_column_spacing(GTK_GRID(grid), 5);
    
    // Añadir widgets al grid
    GtkWidget *label = gtk_label_new("Cuadrícula:");
    gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 2, 1); // col 0, row 0, width 2, height 1
    
    GtkWidget *button1 = gtk_button_new_with_label("Botón 1");
    gtk_grid_attach(GTK_GRID(grid), button1, 0, 1, 1, 1);
    
    GtkWidget *button2 = gtk_button_new_with_label("Botón 2");
    gtk_grid_attach(GTK_GRID(grid), button2, 1, 1, 1, 1);
    
    GtkWidget *button3 = gtk_button_new_with_label("Gran Botón");
    gtk_grid_attach(GTK_GRID(grid), button3, 0, 2, 2, 1);
    
    gtk_container_add(GTK_CONTAINER(window), grid);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_show_all(window);
    gtk_main();
    
    return 0;
}

4.3 GtkNotebook (Pestañas)

#include <gtk/gtk.h>

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Pestañas GTK+");
    
    // Crear notebook (pestañas)
    GtkWidget *notebook = gtk_notebook_new();
    
    // Crear páginas
    GtkWidget *page1 = gtk_label_new("Contenido de la página 1");
    GtkWidget *page2 = gtk_label_new("Contenido de la página 2");
    
    // Añadir páginas al notebook
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page1, gtk_label_new("Pestaña 1"));
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page2, gtk_label_new("Pestaña 2"));
    
    gtk_container_add(GTK_CONTAINER(window), notebook);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_show_all(window);
    gtk_main();
    
    return 0;
}

5. Widgets Avanzados

5.1 GtkTreeView (Vista de árbol/listado)

#include <gtk/gtk.h>

enum {
    COL_NAME = 0,
    COL_AGE,
    NUM_COLS
};

static GtkTreeModel* create_model(void) {
    GtkListStore *store = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_INT);
    
    // Añadir datos
    GtkTreeIter iter;
    gtk_list_store_append(store, &iter);
    gtk_list_store_set(store, &iter, COL_NAME, "Juan Pérez", COL_AGE, 30, -1);
    
    gtk_list_store_append(store, &iter);
    gtk_list_store_set(store, &iter, COL_NAME, "María García", COL_AGE, 25, -1);
    
    gtk_list_store_append(store, &iter);
    gtk_list_store_set(store, &iter, COL_NAME, "Carlos López", COL_AGE, 35, -1);
    
    return GTK_TREE_MODEL(store);
}

static void add_columns(GtkTreeView *treeview) {
    GtkCellRenderer *renderer;
    GtkTreeViewColumn *column;
    
    // Columna Nombre
    renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes("Nombre", renderer, 
                                                    "text", COL_NAME, NULL);
    gtk_tree_view_append_column(treeview, column);
    
    // Columna Edad
    renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes("Edad", renderer, 
                                                    "text", COL_AGE, NULL);
    gtk_tree_view_append_column(treeview, column);
}

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "TreeView GTK+");
    
    // Crear TreeView con modelo
    GtkWidget *treeview = gtk_tree_view_new_with_model(create_model());
    add_columns(GTK_TREE_VIEW(treeview));
    
    // Añadir a ScrolledWindow
    GtkWidget *scrolled = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_container_add(GTK_CONTAINER(scrolled), treeview);
    
    gtk_container_add(GTK_CONTAINER(window), scrolled);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_show_all(window);
    gtk_main();
    
    return 0;
}

5.2 GtkComboBox (Lista desplegable)

#include <gtk/gtk.h>

void on_combo_changed(GtkComboBox *combo, gpointer data) {
    gint active = gtk_combo_box_get_active(combo);
    if (active >= 0) {
        gchar *text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
        g_print("Seleccionado: %s\n", text);
        g_free(text);
    }
}

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "ComboBox GTK+");
    gtk_container_set_border_width(GTK_CONTAINER(window), 10);
    
    // Crear ComboBoxText
    GtkWidget *combo = gtk_combo_box_text_new();
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "Opción 1");
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "Opción 2");
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "Opción 3");
    
    g_signal_connect(combo, "changed", G_CALLBACK(on_combo_changed), NULL);
    
    gtk_container_add(GTK_CONTAINER(window), combo);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_show_all(window);
    gtk_main();
    
    return 0;
}

5.3 GtkTextView (Editor de texto)

#include <gtk/gtk.h>

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Editor de Texto");
    gtk_window_set_default_size(GTK_WINDOW(window), 600, 400);
    
    // Crear TextView
    GtkWidget *textview = gtk_text_view_new();
    GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
    
    // Insertar texto inicial
    gtk_text_buffer_set_text(buffer, "Escribe aquí tu texto...\n", -1);
    
    // Configurar fuente
    PangoFontDescription *font_desc = pango_font_description_from_string("Monospace 12");
    gtk_widget_override_font(textview, font_desc);
    pango_font_description_free(font_desc);
    
    // Añadir a ScrolledWindow
    GtkWidget *scrolled = gtk_scrolled_window_new(NULL, NULL);
    gtk_container_add(GTK_CONTAINER(scrolled), textview);
    
    gtk_container_add(GTK_CONTAINER(window), scrolled);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_show_all(window);
    gtk_main();
    
    return 0;
}

6. Dibujo Personalizado

6.1 GtkDrawingArea (Área de dibujo)

#include <gtk/gtk.h>
#include <cairo.h>

gboolean on_draw(GtkWidget *widget, cairo_t *cr, gpointer data) {
    // Configurar color de fondo
    cairo_set_source_rgb(cr, 1, 1, 1); // Blanco
    cairo_paint(cr);
    
    // Dibujar un rectángulo
    cairo_set_source_rgb(cr, 0.8, 0.2, 0.2); // Rojo
    cairo_rectangle(cr, 50, 50, 100, 80);
    cairo_fill(cr);
    
    // Dibujar un círculo
    cairo_set_source_rgb(cr, 0.2, 0.2, 0.8); // Azul
    cairo_arc(cr, 200, 100, 40, 0, 2 * G_PI);
    cairo_fill(cr);
    
    // Dibujar texto
    cairo_set_source_rgb(cr, 0, 0, 0); // Negro
    cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
    cairo_set_font_size(cr, 16);
    cairo_move_to(cr, 50, 180);
    cairo_show_text(cr, "Dibujo con Cairo");
    
    return FALSE;
}

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Dibujo GTK+");
    
    // Crear área de dibujo
    GtkWidget *drawing_area = gtk_drawing_area_new();
    gtk_widget_set_size_request(drawing_area, 300, 200);
    
    // Conectar señal de dibujo
    g_signal_connect(drawing_area, "draw", G_CALLBACK(on_draw), NULL);
    
    gtk_container_add(GTK_CONTAINER(window), drawing_area);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_show_all(window);
    gtk_main();
    
    return 0;
}

6.2 Funciones básicas de Cairo

Función Descripción
cairo_set_source_rgb() Establece color (RGB)
cairo_set_source_rgba() Establece color con transparencia (RGBA)
cairo_move_to() Mueve el punto actual
cairo_line_to() Dibuja una línea
cairo_rectangle() Dibuja un rectángulo
cairo_arc() Dibuja un arco o círculo
cairo_fill() Rellena la figura actual
cairo_stroke() Dibuja el contorno de la figura

7. Aplicaciones Completas

7.1 Editor de texto con barra de menú

#include <gtk/gtk.h>
#include <gtk/gtkfilechooser.h>

GtkWidget *textview;
GtkTextBuffer *buffer;

void open_file(GtkWidget *widget, gpointer data) {
    GtkWidget *dialog = gtk_file_chooser_dialog_new("Abrir archivo",
                                                   GTK_WINDOW(data),
                                                   GTK_FILE_CHOOSER_ACTION_OPEN,
                                                   "_Cancelar", GTK_RESPONSE_CANCEL,
                                                   "_Abrir", GTK_RESPONSE_ACCEPT,
                                                   NULL);
    
    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
        char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
        gchar *contents;
        gsize length;
        
        if (g_file_get_contents(filename, &contents, &length, NULL)) {
            gtk_text_buffer_set_text(buffer, contents, length);
            g_free(contents);
        }
        
        g_free(filename);
    }
    
    gtk_widget_destroy(dialog);
}

void save_file(GtkWidget *widget, gpointer data) {
    GtkWidget *dialog = gtk_file_chooser_dialog_new("Guardar archivo",
                                                   GTK_WINDOW(data),
                                                   GTK_FILE_CHOOSER_ACTION_SAVE,
                                                   "_Cancelar", GTK_RESPONSE_CANCEL,
                                                   "_Guardar", GTK_RESPONSE_ACCEPT,
                                                   NULL);
    
    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
        char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
        GtkTextIter start, end;
        
        gtk_text_buffer_get_bounds(buffer, &start, &end);
        gchar *text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
        
        g_file_set_contents(filename, text, -1, NULL);
        g_free(text);
        g_free(filename);
    }
    
    gtk_widget_destroy(dialog);
}

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Editor de Texto");
    gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
    
    // Crear barra de menú
    GtkWidget *menubar = gtk_menu_bar_new();
    GtkWidget *file_menu = gtk_menu_new();
    GtkWidget *file_item = gtk_menu_item_new_with_label("Archivo");
    
    GtkWidget *open_item = gtk_menu_item_new_with_label("Abrir");
    GtkWidget *save_item = gtk_menu_item_new_with_label("Guardar");
    GtkWidget *exit_item = gtk_menu_item_new_with_label("Salir");
    
    gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), open_item);
    gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), save_item);
    gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), gtk_separator_menu_item_new());
    gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), exit_item);
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_item), file_menu);
    gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file_item);
    
    // Conectar señales del menú
    g_signal_connect(open_item, "activate", G_CALLBACK(open_file), window);
    g_signal_connect(save_item, "activate", G_CALLBACK(save_file), window);
    g_signal_connect(exit_item, "activate", G_CALLBACK(gtk_main_quit), NULL);
    
    // Crear editor de texto
    textview = gtk_text_view_new();
    buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
    
    // Configurar fuente monoespaciada
    PangoFontDescription *font_desc = pango_font_description_from_string("Monospace 12");
    gtk_widget_override_font(textview, font_desc);
    pango_font_description_free(font_desc);
    
    // Crear ScrolledWindow
    GtkWidget *scrolled = gtk_scrolled_window_new(NULL, NULL);
    gtk_container_add(GTK_CONTAINER(scrolled), textview);
    
    // Organizar widgets en caja vertical
    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0);
    
    gtk_container_add(GTK_CONTAINER(window), vbox);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_show_all(window);
    gtk_main();
    
    return 0;
}

7.2 Calculadora básica

#include <gtk/gtk.h>
#include <string.h>
#include <stdlib.h>

GtkWidget *display;
double current_value = 0;
char current_operation = 0;

void update_display() {
    char text[32];
    snprintf(text, sizeof(text), "%.10g", current_value);
    gtk_entry_set_text(GTK_ENTRY(display), text);
}

void on_digit_clicked(GtkButton *button, gpointer data) {
    const char *digit = gtk_button_get_label(button);
    const char *current = gtk_entry_get_text(GTK_ENTRY(display));
    
    if (strcmp(current, "0") == 0 && digit[0] != '.') {
        gtk_entry_set_text(GTK_ENTRY(display), digit);
    } else {
        char new_text[32];
        snprintf(new_text, sizeof(new_text), "%s%s", current, digit);
        gtk_entry_set_text(GTK_ENTRY(display), new_text);
    }
}

void on_operation_clicked(GtkButton *button, gpointer data) {
    const char *op = gtk_button_get_label(button);
    const char *current = gtk_entry_get_text(GTK_ENTRY(display));
    
    double operand = atof(current);
    
    if (current_operation) {
        switch(current_operation) {
            case '+': current_value += operand; break;
            case '-': current_value -= operand; break;
            case '*': current_value *= operand; break;
            case '/': current_value /= operand; break;
        }
    } else {
        current_value = operand;
    }
    
    current_operation = op[0];
    update_display();
    gtk_entry_set_text(GTK_ENTRY(display), "0");
}

void on_equals_clicked(GtkButton *button, gpointer data) {
    const char *current = gtk_entry_get_text(GTK_ENTRY(display));
    double operand = atof(current);
    
    if (current_operation) {
        switch(current_operation) {
            case '+': current_value += operand; break;
            case '-': current_value -= operand; break;
            case '*': current_value *= operand; break;
            case '/': current_value /= operand; break;
        }
        current_operation = 0;
        update_display();
    }
}

void on_clear_clicked(GtkButton *button, gpointer data) {
    current_value = 0;
    current_operation = 0;
    gtk_entry_set_text(GTK_ENTRY(display), "0");
}

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);
    
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Calculadora GTK+");
    gtk_container_set_border_width(GTK_CONTAINER(window), 10);
    
    // Crear display
    display = gtk_entry_new();
    gtk_entry_set_alignment(GTK_ENTRY(display), 1); // Alinear a la derecha
    gtk_entry_set_text(GTK_ENTRY(display), "0");
    gtk_editable_set_editable(GTK_EDITABLE(display), FALSE);
    
    // Crear botones
    const char *buttons[] = {
        "7", "8", "9", "/",
        "4", "5", "6", "*",
        "1", "2", "3", "-",
        "0", ".", "=", "+",
        "C"
    };
    
    // Crear grid para botones
    GtkWidget *grid = gtk_grid_new();
    gtk_grid_set_row_spacing(GTK_GRID(grid), 5);
    gtk_grid_set_column_spacing(GTK_GRID(grid), 5);
    
    // Añadir botones al grid
    for (int i = 0; i < 16; i++) {
        GtkWidget *button = gtk_button_new_with_label(buttons[i]);
        gtk_widget_set_size_request(button, 50, 50);
        
        if (i % 4 == 3) {
            g_signal_connect(button, "clicked", G_CALLBACK(on_operation_clicked), NULL);
        } else if (i == 14) {
            g_signal_connect(button, "clicked", G_CALLBACK(on_equals_clicked), NULL);
        } else {
            g_signal_connect(button, "clicked", G_CALLBACK(on_digit_clicked), NULL);
        }
        
        gtk_grid_attach(GTK_GRID(grid), button, i % 4, i / 4, 1, 1);
    }
    
    // Botón Clear
    GtkWidget *clear = gtk_button_new_with_label(buttons[16]);
    gtk_widget_set_size_request(clear, 50, 50);
    g_signal_connect(clear, "clicked", G_CALLBACK(on_clear_clicked), NULL);
    gtk_grid_attach(GTK_GRID(grid), clear, 0, 4, 4, 1);
    
    // Organizar widgets en caja vertical
    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
    gtk_box_pack_start(GTK_BOX(vbox), display, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), grid, TRUE, TRUE, 0);
    
    gtk_container_add(GTK_CONTAINER(window), vbox);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_show_all(window);
    gtk_main();
    
    return 0;
}

8. Recursos y Referencias

8.1 Documentación oficial

8.2 Tutoriales

8.3 Comunidades