glib critical error (gthread) and gtk spinner not showing up - c

I've a weird problem with distros which are !(Arch Linux).Actually I'm using Arch Linux with GNOME 3.16 and I've tried my software software with Fedora 22 alpha (GNOME 3.16), Ubuntu and Xubuntu 14.10/15.04 but I always receive the same error:GLib-CRITICAL **: Source ID XXX was not found when attempting to remove it
I know that the problem is with g_thread but I'm not able to understand why I'm not receiving that warning with Arch but only with other distros.Moreover, the gtk_spinner widgets are showing up only on Arch. On Fedora they aren't showing up and I'm not getting any errors...
gpointer (*hash_func[NUM_OF_HASH_ENTRY])(gpointer) = {compute_md5, compute_gost94, compute_sha1, compute_sha2, compute_sha3, compute_sha2, compute_sha3, compute_sha2, compute_sha3, compute_whirlpool};
struct hash_vars
{
gint n_bit; //number of hash bit (256, 384, 512)
gboolean gth_created[NUM_OF_HASH_ENTRY];
gchar *filename;
GHashTable *hash_table;
GtkWidget *hash_entry[NUM_OF_HASH_ENTRY]; //md5, gost, sha1, sha256, sha3-256, sha384, sha3_384, sha512, sha3-512, whir
GtkWidget *hash_check[NUM_OF_HASH_ENTRY]; //md5, gost, sha1, sha256, sha3-256, sha384, sha3_384, sha512, sha3-512, whir
GtkWidget *hash_spinner[NUM_OF_HASH_ENTRY];
gchar *key[NUM_OF_HASH_ENTRY];
struct threads_list
{
GThread *gth[NUM_OF_HASH_ENTRY];
} threads;
};
struct hash_vars hash_var;
/* other things */
for (i = 0; i < NUM_OF_HASH_ENTRY; i++)
{
gtk_widget_set_name (GTK_WIDGET (hash_var.hash_check[i]), bt_names[i]);
hash_var.gth_created[i] = FALSE;
g_signal_connect (hash_var.hash_check[i], "clicked", G_CALLBACK (create_thread), &hash_var);
}
result = gtk_dialog_run (GTK_DIALOG (dialog));
switch (result)
{
case GTK_RESPONSE_REJECT:
for (i = 0; i < NUM_OF_HASH_ENTRY; i++)
{
if(hash_var.gth_created[i])
g_thread_join (hash_var.threads.gth[i]);
}
g_free (hash_var.filename);
g_hash_table_destroy (hash_var.hash_table);
gtk_widget_destroy (dialog);
break;
}
}
gpointer
create_thread ( GtkWidget *bt,
gpointer user_data)
{
gint i;
struct hash_vars *hash_var = user_data;
const gchar *name = gtk_widget_get_name (bt);
for (i = 0; i < NUM_OF_HASH_ENTRY; i++)
{
if (g_strcmp0 (name, bt_names[i]) == 0)
{ hash_var->gth_created[i] = TRUE;
hash_var->threads.gth[i] = g_thread_new (NULL, (GThreadFunc)hash_func[i], hash_var);
}
}
}
and this is one of the hash functions:
gpointer
compute_md5 (gpointer user_data)
{
struct hash_vars *hash_var = user_data;
if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (hash_var->hash_check[0])))
{
gtk_entry_set_text (GTK_ENTRY (hash_var->hash_entry[0]), "");
goto fine;
}
else if (g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (hash_var->hash_entry[0])), -1) == 32)
goto fine;
gpointer ptr = g_hash_table_lookup (hash_var->hash_table, hash_var->key[0]);
if (ptr != NULL)
{
gtk_entry_set_text (GTK_ENTRY (hash_var->hash_entry[0]), (gchar *)g_hash_table_lookup (hash_var->hash_table, hash_var->key[0]));
goto fine;
}
gtk_spinner_start (GTK_SPINNER (hash_var->hash_spinner[0]));
/* computing md5 */
fine:
gtk_spinner_stop (GTK_SPINNER (hash_var->hash_spinner[0]));
g_thread_exit(NULL);
}
So, to summarize, my problems are:
I receive a GLib-Critical error only on distro !(Arch Linux)
gtk_spinner are showing up only on Arch
All suggestions are welcome :)

"GTK+, however, is not thread safe. You should only use GTK+ and GDK from the thread gtk_init() and gtk_main() were called on. This is usually referred to as the “main thread”."
— https://developer.gnome.org/gdk3/stable/gdk3-Threads.html#gdk3-Threads.description
Basically, any time you want to interact with GTK+ you should do it using an idle callback. That link explains how.
As for why it works on some platforms but not others, it's probably a race condition.

Related

Non-standard device event input for GTK

I am using GTK as the user interface to a piece of hardware (a 3D printer). The hardware can create events as a result of user interactions with the machine (not the GUI). For example, they can remove the build plate which trips a switch thus sending a signal to my program that this has happened, but how do I get gtk_main() to recognize that this event has happened?
In other words, how do I get gtk_main() to watch for non-standard device input events?
You can use custom actions provided by GtkApplication.
In any case you must implement by yourself the monitoring code, either by polling your hardware status or by leveraging more advanced techniques (if possible).
/* gcc -o monitor monitor.c $(pkg-config --cflags --libs gtk+-3.0) */
#include <gtk/gtk.h>
static void
startup(GtkApplication *application)
{
GAction *action;
/* plate-removed is a custom action */
action = G_ACTION(g_simple_action_new("plate-removed", NULL));
g_action_map_add_action(G_ACTION_MAP(application), action);
g_object_unref(action);
/* You can connect your callbacks to the GAction::activate signal */
action = g_action_map_lookup_action(G_ACTION_MAP(application), "plate-removed");
g_signal_connect_swapped(action, "activate",
G_CALLBACK(g_print), "Plate has been removed\n");
}
static gpointer
worker_thread(gpointer user_data)
{
GApplication *application = G_APPLICATION(user_data);
for (;;) {
g_usleep(g_random_int_range(500000, 5000000));
/* Event occurred: emit the signal */
g_action_group_activate_action(G_ACTION_GROUP(application),
"plate-removed", NULL);
}
return NULL;
}
static gboolean
polling_loop(gpointer user_data)
{
GApplication *application = G_APPLICATION(user_data);
if (g_random_int_range(1, 20) == 8) {
/* Event occurred: emit the signal */
g_action_group_activate_action(G_ACTION_GROUP(application),
"plate-removed", NULL);
}
return G_SOURCE_CONTINUE;
}
static void
activate(GtkApplication *application)
{
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_application_add_window(application, GTK_WINDOW(window));
gtk_widget_show_all(window);
/* You can use whatever you want to monitor your hardware, in particular
* a polling loop or a worker thread */
g_timeout_add(50, polling_loop, application);
/* g_thread_new("WorkerThread", worker_thread, application); */
}
int
main(int argc, char *argv[])
{
GtkApplication *application;
int retval;
application = gtk_application_new(NULL, G_APPLICATION_FLAGS_NONE);
g_signal_connect(application, "startup", G_CALLBACK(startup), NULL);
g_signal_connect(application, "activate", G_CALLBACK(activate), NULL);
retval = g_application_run(G_APPLICATION(application), argc, argv);
g_object_unref(application);
return retval;
}

Drawing lines with GTK+ and Cairo without removing what is already drawn

Currently I am writing a program in C, on a linux system (Raspberry Pi to be exact) which should draw to a GTK window using Cairo. I've been following the tutorial at: http://zetcode.com/gfx/cairo/ . But it is way to vague with it's explanations at certain points.
It does not explain two points that I really need:
I can't figure out a way to draw to the window with a proper function call.
It removes what is already drawn.
I need a piece of code that does some simple things, in a very Object-Oriented manner:
Draw lines to a GTK window with a function call, given X and Y for both starting and end point;
Do not remove what is previously drawn;
All initializations of variables and the window should be outside the main function.
So basically something similar to this:
#include <cairo.h>
#include <gtk/gtk.h>
void drawLine(int xStart, int yStart, int yEnd, int xEnd) {
//Drawing code here.
}
void initializeCairo() {
//Insert cairo initialization.
}
void initializeGTK() {
//Insert GTK initialization.
}
/*If needed a general initializer for both cairo and GTK*/
void initialize() {
//Insert general initialization.
}
int main (int argc, char *archv[]) {
intializeGTK();
initializeCairo();
if(doSomething) {
drawLine(10, 10, 20, 20);
}
}
If it could be explained what a method does (in proper English please, not a reference to the documentation), that'd be absolutely great.
Also please include the gcc build command used.
Thanks in advance!
The answers from andlabs are fine. Here is in addition a short (although not entirely elegant) example. It will "kind of remember" the last NUM lines - creation/resize/activation/deactivation of the window will trigger a "draw" of the content. A Next button click will add a new line to the output. Check also the command-line output for an update of
the array values that are drawn.
#include <gtk/gtk.h>
#include <glib/gprintf.h>
#include <cairo.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#define NUM 3
typedef struct {
GtkApplication *app;
GtkWidget *window;
GtkWidget *button;
GtkWidget *da;
cairo_t* cr;
gboolean redraw;
gint xsize;
gint ysize;
} appWidgets;
gboolean drawEvent (GSimpleAction *action, GVariant *parameter, gpointer data);
void nextCallback (GtkWidget *widget, gpointer data);
void nextCallback (GtkWidget *widget, gpointer data)
{
appWidgets *w = (appWidgets*) data;
static gint cnt = 0;
static gdouble x[NUM], y[NUM], u[NUM], v[NUM];
// determine the next coordinates for a line
if (w->redraw == FALSE) {
x[cnt] = g_random_double();
y[cnt] = g_random_double();
u[cnt] = g_random_double();
v[cnt] = g_random_double();
}
w->cr = gdk_cairo_create (gtk_widget_get_window (w->da));
// map (0,0)...(xsize,ysize) to (0,0)...(1,1)
cairo_translate (w->cr, 0, 0);
cairo_scale (w->cr, w->xsize, w->ysize);
// set linewidth
cairo_set_line_width (w->cr, 0.005);
// draw the lines
for (int k = 0; k < NUM; k++) {
cairo_move_to (w->cr, x[k], y[k]);
cairo_line_to (w->cr, u[k], v[k]);
cairo_stroke (w->cr);
g_print("k=%d:(%1.2lf,%1.2lf).(%1.2lf,%1.2lf) ",
k, x[k], y[k], u[k], v[k]);
}
g_print("\n");
cairo_destroy (w->cr);
if (w->redraw == FALSE) {
cnt++;
if (cnt == NUM)
cnt = 0;
}
}
gboolean drawEvent (GSimpleAction *action, GVariant *parameter, gpointer data)
{
appWidgets *w = (appWidgets*) data;
w->xsize = gtk_widget_get_allocated_width (w->da);
w->ysize = gtk_widget_get_allocated_height (w->da);
w->redraw = TRUE;
nextCallback (NULL, w);
w->redraw = FALSE;
return TRUE;
}
void activate (GtkApplication *app, gpointer data)
{
GtkWidget *box;
appWidgets *w = (appWidgets*) data;
w->window = gtk_application_window_new (w->app);
gtk_window_set_application (GTK_WINDOW (w->window), GTK_APPLICATION (w->app));
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (w->window), box);
w->da = gtk_drawing_area_new();
gtk_widget_set_size_request (w->da, 400, 400);
gtk_box_pack_start (GTK_BOX (box), w->da, TRUE, TRUE, 0);
g_signal_connect (w->da, "draw", G_CALLBACK (drawEvent), (gpointer) w);
w->button = gtk_button_new_with_label ("Next");
g_signal_connect (G_OBJECT (w->button), "clicked", G_CALLBACK (nextCallback),
(gpointer) w);
gtk_box_pack_start (GTK_BOX (box), w->button, FALSE, TRUE, 0);
gtk_widget_show_all (GTK_WIDGET (w->window));
w->redraw = FALSE;
}
int main (int argc, char *argv[])
{
gint status;
appWidgets *w = g_malloc (sizeof (appWidgets));
w->app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect (w->app, "activate", G_CALLBACK (activate), (gpointer) w);
status = g_application_run (G_APPLICATION (w->app), argc, argv);
g_object_unref (w->app);
g_free (w);
w = NULL;
return status;
}
Build the program as usual:
gcc example.c -o example `pkg-config --cflags --libs gtk+-3.0`

gtk_label_set_text() segmentation fault

Here is my code.
#include <gtk/gtk.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#define MAX_LENGTH 8
#define WIDGET_NUM 3
typedef struct Widget
{
GtkWidget *window;
GtkWidget *button[WIDGET_NUM];
GtkWidget *entry[WIDGET_NUM];
GtkWidget *label[WIDGET_NUM];
GtkWidget *grid;
pthread_t pid[WIDGET_NUM];
int button_num;
}Widget;
void num_2_time(int num, char *buf)
{
int h = num / 3600;
int m = num % 3600 / 60;
int s = num % 60;
sprintf(buf, "%d:%d:%d", h, m, s);
}
void *wait_4_waking(void *arg)
{
Widget *window = (Widget*)arg;
int input_num, window_num = window->button_num;
const char *text;
char buf[MAX_LENGTH * 2];
text = gtk_entry_get_text(GTK_ENTRY(window->entry[window_num]));
input_num = atoi(text);
gtk_entry_set_text(GTK_ENTRY(window->entry[window_num]), "");
while (input_num >= 0)
{
num_2_time(input_num, buf);
//Segmentation fault
gtk_label_set_text(GTK_LABEL(window->label[window_num]), buf);
sleep(1);
input_num--;
}
return NULL;
}
void button_clicked_0(GtkWidget *widget, gpointer data)
{
Widget *window = (Widget*)data;
window->button_num = 0;
printf("%u\n", window->pid[0]);
if (window->pid[0] > 0)
{
pthread_cancel(window->pid[0]);
}
pthread_create(window->pid, NULL, wait_4_waking, data);
}
void button_clicked_1(GtkWidget *widget, gpointer data)
{
Widget *window = (Widget*)data;
window->button_num = 1;
if (window->pid[1] > 0)
{
pthread_cancel(window->pid[1]);
}
pthread_create(window->pid+1, NULL, wait_4_waking, data);
}
void button_clicked_2(GtkWidget *widget, gpointer data)
{
Widget *window = (Widget*)data;
window->button_num = 2;
if (window->pid[2] > 0)
{
pthread_cancel(window->pid[2]);
}
pthread_create(window->pid+2, NULL, wait_4_waking, data);
}
int main (int argc, char **argv)
{
Widget window;
int i;
void (*button_clicked[WIDGET_NUM])(GtkWidget*, gpointer) = {
button_clicked_0, button_clicked_1, button_clicked_2
};
gtk_init(&argc, &argv);
window.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window.window), "Window");
gtk_window_set_default_size (GTK_WINDOW (window.window), 400, 200);
window.grid = gtk_grid_new();
for (i = 0; i < WIDGET_NUM; i++)
{
window.entry[i] = gtk_entry_new();
window.label[i] = gtk_label_new("0:0:0");
window.button[i] = gtk_button_new_with_label("Go!");
window.pid[i] = 0;
gtk_entry_set_max_length(GTK_ENTRY(window.entry[i]), MAX_LENGTH);
gtk_grid_attach(GTK_GRID(window.grid), window.entry[i], 0, i, 1, 1);
gtk_grid_attach(GTK_GRID(window.grid), window.button[i], 1, i, 1, 1);
gtk_grid_attach(GTK_GRID(window.grid), window.label[i], 2, i, 1, 1);
}
gtk_container_add (GTK_CONTAINER (window.window), window.grid);
for (i = 0; i < WIDGET_NUM; i++)
{
g_signal_connect (window.button[i], "clicked", G_CALLBACK (button_clicked[i]), (gpointer)&window);
}
g_signal_connect (window.window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all (window.window);
gtk_main();
return 0;
}
Building command:
gcc -o 1 test2.c `pkg-config --libs --cflags gtk+-3.0` -pthread -g
This error occurred when I clicked The button, but it didn't occur all the time. Before it occurred, I get an information below.
(1:18144): Pango-CRITICAL **: pango_layout_get_pixel_extents: assertion 'PANGO_IS_LAYOUT (layout)' failed
(1:18144): Pango-CRITICAL **: pango_layout_get_iter: assertion 'PANGO_IS_LAYOUT (layout)' failed
And then segmentation fault.
I checked its information by dmesg command. And I got this.
[ 703.437988] 1[6358]: segfault at 10 ip 00007f9b07ac3f91 sp 00007ffe3bd53790 error 4 in libpango-1.0.so.0.3800.1[7f9b07aa0000+48000]
How can I do for this error?
You are calling a Gtk+ widget function from another thread. Gtk+ is not thread safe so don't do that.
Your best option is to avoid threads: design your code so that the main loop is never blocked for long periods. Usually when I see threading problems, the use of threads is unnecessary and the whole mess could have been avoided by a cleaner design.
If the code can't be designed in a better way, then you'll need to use e.g. g_main_context_invoke() in your other thread to invoke a function in the main thread: that function can then modify the Gtk+ widget state safely. Be careful not to make mistakes with lifetimes of pointers that you share between threads.

How can I compile my C program with the polkit library?

Today, I tried to use polkit - an application-level toolkit for defining and handling the policy that allows unprivileged processes to speak to privileged processes. In order to understand it better, I tried to download this code:
#include <polkit/polkit.h>
static gboolean
on_tensec_timeout (gpointer user_data)
{
GMainLoop *loop = user_data;
g_print ("Ten seconds has passed. Now exiting.\n");
g_main_loop_quit (loop);
return FALSE;
}
static void
check_authorization_cb (PolkitAuthority *authority,
GAsyncResult *res,
gpointer user_data)
{
GMainLoop *loop = user_data;
PolkitAuthorizationResult *result;
GError *error;
error = NULL;
result = polkit_authority_check_authorization_finish (authority, res, &error);
if (error != NULL)
{
g_print ("Error checking authorization: %s\n", error->message);
g_error_free (error);
}
else
{
const gchar *result_str;
if (polkit_authorization_result_get_is_authorized (result))
{
result_str = "authorized";
}
else if (polkit_authorization_result_get_is_challenge (result))
{
result_str = "challenge";
}
else
{
result_str = "not authorized";
}
g_print ("Authorization result: %s\n", result_str);
}
g_print ("Authorization check has been cancelled and the dialog should now be hidden.\n"
"This process will exit in ten seconds.\n");
g_timeout_add (10000, on_tensec_timeout, loop);
}
static gboolean
do_cancel (GCancellable *cancellable)
{
g_print ("Timer has expired; cancelling authorization check\n");
g_cancellable_cancel (cancellable);
return FALSE;
}
int
main (int argc, char *argv[])
{
pid_t parent_pid;
const gchar *action_id;
GMainLoop *loop;
PolkitSubject *subject;
PolkitAuthority *authority;
GCancellable *cancellable;
g_type_init ();
if (argc != 2)
{
g_printerr ("usage: %s <action_id>\n", argv[0]);
return 1;
}
action_id = argv[1];
loop = g_main_loop_new (NULL, FALSE);
authority = polkit_authority_get_sync (NULL, NULL);
/* Typically mechanisms will use a PolkitSystemBusName since most
* clients communicate with the mechanism via D-Bus. However for
* this simple example we use the process id of the calling process.
*
* Note that if the parent was reaped we have to be careful not to
* check if init(1) is authorized (it always is).
*/
parent_pid = getppid ();
if (parent_pid == 1)
{
g_printerr ("Parent process was reaped by init(1)\n");
return 1;
}
subject = polkit_unix_process_new (parent_pid);
cancellable = g_cancellable_new ();
g_print ("Will cancel authorization check in 10 seconds\n");
/* Set up a 10 second timer to cancel the check */
g_timeout_add (10 * 1000,
(GSourceFunc) do_cancel,
cancellable);
polkit_authority_check_authorization (authority,
subject,
action_id,
NULL, /* PolkitDetails */
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
cancellable,
(GAsyncReadyCallback) check_authorization_cb,
loop);
g_main_loop_run (loop);
g_object_unref (authority);
g_object_unref (subject);
g_object_unref (cancellable);
g_main_loop_unref (loop);
return 0;
}
But when I tried to compile it with gcc, I received this output:
ciao.c:35:27: fatal error: polkit/polkit.h: File o directory non esistente
compilation terminated.
The path polkit/polkit.h is not in the system include path. Make sure you've downloaded and installed PolKit so that the headers and libraries are available.
I solved my problem adding this
pkg-config --cflags glib-2.0 polkit-gobject-1
and this
pkg-config --libs glib-2.0 polkit-gobject-1
to gcc compilation's command in this way
gcc `pkg-config --cflags glib-2.0 polkit-gobject-1` -o polkit PolicyKit/example.c `pkg-config --libs glib-2.0`

How to start and stop GIF animated GtkImage in GTK+

I have a GTK+ application written in C that loads a matrix of animated GIF files. These GtkImages automatically run the animation when they are loaded and then stop when the animation is completed. How would I restart the animation of each GtkImage containing the GIF and are signals generated when the animation is complete?
Thank you.
EDIT :
Would the use of gdk_pixbuf_animation_get_iter() described here make this possible?
Full code is provided below.
/*
* Compile me with:
* gcc -o reels reels.c $(pkg-config --cflags --libs gtk+-2.0 gmodule-2.0)
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
/* GTK */
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
/**** prototypes ****/
static void destroy (GtkWidget*, gpointer);
GdkPixbuf *create_pixbuf(const gchar * filename);
GtkWidget *SetupWindow(gchar *data, const gchar *filename);
static void destroy (GtkWidget *window, gpointer data);
void btnSpin_clicked(GtkWidget *button, gpointer data);
void btnExit_clicked(GtkWidget *button, gpointer data);
/********************/
GtkWidget *images[3][5];
static void destroy (GtkWidget *window, gpointer data)
{
gtk_main_quit ();
}
void btnSpin_clicked(GtkWidget *button, gpointer data)
{
printf("Spin Button pressed.\n");
return;
}
void btnExit_clicked(GtkWidget *button, gpointer data)
{
gtk_main_quit();
return;
}
GtkWidget *SetupWindow(gchar *data, const gchar *filename)
{
GdkPixmap *background;
GdkPixbuf *pixbuf;
GdkScreen *ourscreen;
GdkColormap *colormap;
GtkStyle *style;
GdkColor fg;
GdkColor bg;
GError *error = NULL;
GdkRectangle *rect;
GtkWidget *window;
pixbuf = gdk_pixbuf_new_from_file (filename,&error);
if (error != NULL) {
if (error->domain == GDK_PIXBUF_ERROR) {
g_print ("Pixbuf Related Error:\n");
}
if (error->domain == G_FILE_ERROR) {
g_print ("File Error: Check file permissions and state:\n");
}
g_printerr ("%s\n", error[0].message);
}
gdk_pixbuf_render_pixmap_and_mask (pixbuf, &background, NULL, 0);
style = gtk_style_new ();
style->bg_pixmap[0] = background;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), data);
// gtk_window_maximize(GTK_WINDOW(window));
gtk_window_set_modal (GTK_WINDOW (window),TRUE);
gtk_window_set_default_size(GTK_WINDOW(window),628,530);
gtk_widget_set_style (GTK_WIDGET(window), GTK_STYLE(style));
gtk_window_set_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER_ALWAYS);
gtk_container_set_border_width(GTK_CONTAINER(window), 0);
//gtk_window_set_resizable(GTK_WINDOW(window), (gboolean) FALSE);
gtk_window_set_decorated( GTK_WINDOW(window), FALSE );
return(window);
}
int main (int argc, char *argv[])
{
GdkPixbufAnimation *animation;
GtkWidget *image;
int x,y;
GdkPixbuf *pixBuf;
GdkPixmap *pixMap;
gchar filename[20];
GtkWidget *btnSpin, *btnExit;
GtkWidget *frame; /* for absolute positionining of widgets */
GtkWidget *window;
int posx, posy;
gtk_init (&argc, &argv);
window = SetupWindow("Demo", "background.gif");
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
frame = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), frame);
btnSpin = gtk_button_new_with_label("Spin");
gtk_widget_set_size_request(btnSpin, 80, 35);
gtk_fixed_put(GTK_FIXED(frame), btnSpin, 229, 485);
g_signal_connect(G_OBJECT( btnSpin ), "clicked", G_CALLBACK(btnSpin_clicked), NULL );
btnExit = gtk_button_new_with_label("Exit");
gtk_widget_set_size_request(btnExit, 80, 35);
gtk_fixed_put(GTK_FIXED(frame), btnExit, 320, 485);
g_signal_connect(G_OBJECT( btnExit ), "clicked", G_CALLBACK(btnExit_clicked), NULL );
/* setup animated gifs */
for( y = 0; y < 3; y++ )
{
posy = (y*120) + 20;
for( x = 0; x < 5; x++ )
{
posx = (x*120) + 20;
/* set each Image widget to spin GIF */
sprintf( filename,"%d-%d.gif", y+1,x+1 );
images[y][x] = gtk_image_new_from_file(filename);
gtk_fixed_put(GTK_FIXED(frame), images[y][x], posx, posy);
}
}
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
After taking a look at the GtkImage source code, I am afraid there is no signal that is generated when the animation is completed. However, you should be able to restart the animation by calling gtk_image_set_from_file() or gtk_image_set_from_animation().
To completely solve your initial issue, I propose you create a subclass of GtkImage. It should behave exactly like GtkImage, except that animation_timeout should send a signal if delay < 0 around line 1315.
Information about creating a subclass of a GObject (note that GtkImage is a GObject) can be found here.
Based on user1202136's answer the following code snippets show the solution. I post this in case others find it useful.
The basic idea is to use GdkPixbufAnimation, gdk_pixbuf_animation_new_from_file and gtk_image_set_from_animation
The following code snippet(s) show how to do it.
GtkWidget *images[3][5];
GdkPixbufAnimation *animations[3][5];
/* Initial setup of animated gifs */
for( y = 0; y < 3; y++ )
{
for( x = 0; x < 5; x++ )
{
/* set each Image widget to spin GIF */
sprintf( filename,"%d-%d.gif", y+1,x+1 );
images[y][x] = gtk_image_new();
animations[y][x] = gdk_pixbuf_animation_new_from_file ( filename , &error);
gtk_image_set_from_animation (GTK_IMAGE(images[y][x]), animations[y][x]);
gtk_fixed_put(GTK_FIXED(frame), images[y][x], (x*120) + 20, (y*120) + 20);
}
}
/* now to restart the animations use the images and animations array already stored in memory,
no need to re-read the animations from disk so this happens quickly
*/
/* restart animated gifs */
for( y = 0; y < 3; y++ )
{
for( x = 0; x < 5; x++ )
{
gtk_image_set_from_animation(GTK_IMAGE(images[y][x]), animations[y][x]);
}
}

Resources