Reputation: 20140
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?
Upvotes: 3
Views: 2069
Reputation: 11454
One solution is to create one GtkImage
for each position where there will be a led. Don't use gtk_image_new_from_file
, as it will load each time the image file. Instead:
gdk_pixbuf_new_from_file
for each of your 2 image filesgtk_image_new
to create each image widget, and immediately
initialize them with the right pixel buffer using
gtk_image_set_from_pixbuf
GtkImage
, and change the image displayed with gtk_image_set_from_pixbuf
This ensures low memory consumption: you only have 2 pixel buffers that are allocated (and reference counted from the GtkImage instances), and you only create one GtkImage per led (instead of destroying/creating one each time you change the image displayed).
EDIT: here's an improved of Sean Bright's submission, in which I fixed a few mistakes.
#include <gtk/gtk.h>
#define MAX_LEDS_PER_LINE 50
#define NUM_LEDS 2500
static GdkPixbuf * led_on;
static GdkPixbuf * led_off;
static gboolean click_handler(GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
gboolean *is_led_on = user_data;
GList * children = gtk_container_get_children (GTK_CONTAINER (widget));
*is_led_on = ! *is_led_on; /* invert led state */
gtk_image_set_from_pixbuf (GTK_IMAGE(children->data), (*is_led_on) ? led_on : led_off);
g_list_free (children);
return TRUE; /* stop event propagation */
}
int main(int argc, char** argv)
{
GtkWidget *window, *table;
gboolean leds[NUM_LEDS];
int i = 0;
gtk_init(&argc, &argv);
/* 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);
/* Load leds on/off images */
led_on = gdk_pixbuf_new_from_file ("on.png", NULL);
led_off = gdk_pixbuf_new_from_file ("off.png", NULL);
/* Create the container */
int n_rows = (NUM_LEDS / MAX_LEDS_PER_LINE) + 1;
int n_cols = (NUM_LEDS / MAX_LEDS_PER_LINE) + 1;
table = gtk_table_new (n_rows, n_cols, FALSE);
/* Create the leds */
for (i = 0; i < NUM_LEDS; i++)
{
leds[i] = FALSE; /* FALSE means OFF, TRUE means ON */
/*
* A GtkImage doesn't have a window, so we need to put it inside
* a GtkEventBox so we can capture events.
*/
GtkWidget *image = gtk_image_new ();
gtk_image_set_from_pixbuf (GTK_IMAGE(image), led_off);
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), image);
int row = i / MAX_LEDS_PER_LINE;
int col = i % MAX_LEDS_PER_LINE;
gtk_table_attach (GTK_TABLE(table),
eb,
row, row + 1,
col, col + 1,
0,
0,
0,
0);
}
gtk_container_add(GTK_CONTAINER(window), table);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Here, I implemented my first remarks, and improved a few things:
click_handler
callbackclick_handler
callbackUpvotes: 2
Reputation: 120704
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;
}
Upvotes: 4
Reputation: 1305
I am not sure of this but you should place your 2 images in the same position on the screen and then show the one you need and hide the one you don't need. I don't know if it is a good way to do that but I am pretty sure that you are not allocing stuff using this way.
gtk_widget_hide(your_image2);
gtk_widget_show(your_image1);
Hope it helps.
Regards,
Upvotes: 0