cgtkgtk2

Changing image on mouse click


I have been able to do the following:

  for (int i = 0; i < NUM_LEDS; ++i) {
    ledoff = gtk_image_new_from_file("./ledoff.png");
      leds[i].pos=ledpos[i];
      gtk_layout_put(GTK_LAYOUT(layout), ledoff, leds[i].pos.x, leds[i].pos.y);
      leds[i].status=OFF;
  }

Basically, this loads a bunch of "ledoff" images onto some window.

What I need is to change the image ledoff to ledon every time I click on leds[i].pos.x, leds[i].pos.y. At the beginning I thought it was just a matter of loading a new image and replacing the previous one, but then since this will be done thousands of times, I thought I was "malloc'ing" one new file everytime I do gtk_image_new_from_file! Is this true? Or am I just replacing the file and not adding a new one?


Solution

  • Here is a working example that creates a 50x50 "LED" array in a window and allows you to toggle their status by clicking on them. It's not really that efficient, and as I pointed out in comments you're better of drawing the images yourself right onto the GtkLayout, but this will at least serve as a proof of concept.

    Edit: I've updated the sample code to take into account liberforce's suggestions, which make things cleaner and more memory efficient.

    #include <gtk/gtk.h>
    
    #define ICON_WIDTH 16
    #define ICON_HEIGHT 16
    #define NUM_LEDS 2500
    
    typedef enum {
        ON,
        OFF
    } led_status;
    
    typedef struct {
        GtkWidget *img;
        struct {
            gint x;
            gint y;
        } pos;
        led_status status;
    } led;
    
    static led leds[NUM_LEDS];
    static GdkPixbuf *led_on;
    static GdkPixbuf *led_off;
    
    static gboolean click_handler(GtkWidget *widget,
                                  GdkEvent *event,
                                  gpointer user_data)
    {
        led *info = user_data;
    
        if (info->status == ON) {
            gtk_image_set_from_pixbuf(GTK_IMAGE(info->img), led_off);
            info->status = OFF;
        } else {
            gtk_image_set_from_pixbuf(GTK_IMAGE(info->img), led_on);
            info->status = ON;
        }
    
        return TRUE;
    }
    
    int main(int argc, char** argv)
    {
        GtkWidget *window, *layout;
        int i = 0, x, y;
    
        gtk_init(&argc, &argv);
    
        /* Load our images (ignoring errors - as any good sample code would) */
        led_on  = gdk_pixbuf_new_from_file("led-on.png", NULL);
        led_off = gdk_pixbuf_new_from_file("led-off.png", NULL);
    
        /* Initialize our array */
        for (x = 0; x < 50; x++) {
            for (y = 0; y < 50; y++) {
                leds[i].img = gtk_image_new();
                leds[i].pos.x = x * ICON_WIDTH;
                leds[i].pos.y = y * ICON_HEIGHT;
                leds[i].status = OFF;
    
                /* Initialize our image from the pixbuf we've already loaded */
                gtk_image_set_from_pixbuf(GTK_IMAGE(leds[i].img), led_off);
                i++;
            }
        }
    
        /* Create a window */
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_window_set_title(GTK_WINDOW(window), "LEDs");
        gtk_signal_connect(GTK_OBJECT(window),
                           "destroy",
                           G_CALLBACK(gtk_main_quit),
                           NULL);
    
        /* Create the widget */
        layout = gtk_layout_new(NULL, NULL);
    
        for (i = 0; i < NUM_LEDS; i++) {
            /*
             * A GtkImage doesn't have a window, so we need to put it inside
             * a GtkEventBox so we can capture events.
             */
            GtkWidget *eb = gtk_event_box_new();
            g_signal_connect(G_OBJECT(eb),
                             "button_press_event",
                             G_CALLBACK(click_handler),
                             &leds[i]);
            gtk_container_add(GTK_CONTAINER(eb), leds[i].img);
            gtk_layout_put(GTK_LAYOUT(layout), eb, leds[i].pos.x, leds[i].pos.y);
        }
    
        gtk_container_add(GTK_CONTAINER(window), layout);
        gtk_widget_show_all(window);
    
        gtk_main();
    
        return 0;
    }