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.
Related
Context and Question
For reasons, I need to fork my code and update a variable on both forks. The variable is stored in memory via mmap so it is accessible to all processes. On one child process, I increment the variable. How do I tell the GTK application to refresh/update/redraw from the child process?
MWE
/*
* Update GTK label from variable stored in mmap
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <gtk/gtk.h>
static void activate (GtkApplication *app, gpointer localval) {
GtkWidget *window;
// Button Containers
GtkWidget *button_box_quit;
// Buttons
GtkWidget *exit_button;
// Text
GtkWidget *text_status;
// Define Window, dynamic size for screen.
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "test");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
// Define Button Boxes.
button_box_quit = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
// Define Exit Button, put it in a box, put box in window
exit_button = gtk_button_new_with_label ("Exit");
gtk_container_add(GTK_CONTAINER (button_box_quit), exit_button);
gtk_container_add(GTK_CONTAINER (window), button_box_quit);
// Connect signals to buttons
g_signal_connect_swapped (exit_button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
// Define text status
char msg[32]={0};
// The "print" line
g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)localval);
text_status = gtk_label_new(msg);
gtk_container_add(GTK_CONTAINER (button_box_quit), text_status);
//Activate!
g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)localval);
gtk_label_set_text(GTK_LABEL(text_status), msg);
gtk_widget_show_all (window);
}
int main (int argc, char **argv) {
GtkApplication *app;
int status;
int *VAL = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
int *ABORT = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
int pid = fork();
if (pid == 0) {
while(!*ABORT) {
printf("%d\n", *VAL);
// Increments here should be reflected outside this PID.
*VAL = *VAL + 1;
usleep(1000000);
}
exit(0);
} else {
app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
// The passing line
g_signal_connect (app, "activate", G_CALLBACK (activate), VAL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
*ABORT = 1;
}
*ABORT = 1;
return status;
}
What happens at runtime
When the MWE is run, the terminal dutifully prints the value each time it updates. However, the GTK window forever says "val: 1". We can tell the value stored in mmap is accessible to the GTK process by adding usleep(3000000) in the activate process just before gtk_widget_show_all. In this variant, the window will forever show "val: 4".
The Question Reiterated
How do I make the output on the GTK window match the terminal?
That's because activate is called only once (when the window is loaded/activated) but nothing is refreshing the label once loaded, I did some changes in the code (using a global, very ugly but simple to ilustrate the problem), the "Exit Button" is now a "Refresh Button". Press it and you will see the changes in VAL.
/*
* Update GTK label from variable stored in mmap
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <gtk/gtk.h>
static GtkWidget *text_status;
static void refresh(GtkWidget *widget, gpointer data)
{
(void)widget;
char msg[32]={0};
g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)data);
gtk_label_set_text(GTK_LABEL(text_status), msg);
}
static void activate (GtkApplication *app, gpointer localval) {
GtkWidget *window;
// Button Containers
GtkWidget *button_box_quit;
// Buttons
GtkWidget *refresh_button;
// Text
// Define Window, dynamic size for screen.
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "test");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
// Define Button Boxes.
button_box_quit = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
// Define Exit Button, put it in a box, put box in window
refresh_button = gtk_button_new_with_label ("Refresh");
gtk_container_add(GTK_CONTAINER (button_box_quit), refresh_button);
gtk_container_add(GTK_CONTAINER (window), button_box_quit);
// Connect signals to buttons
g_signal_connect(refresh_button, "clicked", G_CALLBACK (refresh), localval);
// Define text status
char msg[32]={0};
// The "print" line
g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)localval);
text_status = gtk_label_new(msg);
gtk_container_add(GTK_CONTAINER (button_box_quit), text_status);
//Activate!
g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)localval);
gtk_label_set_text(GTK_LABEL(text_status), msg);
gtk_widget_show_all (window);
}
int main (int argc, char **argv) {
GtkApplication *app;
int status;
int *VAL = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
int *ABORT = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
int pid = fork();
if (pid == 0) {
while(!*ABORT) {
printf("%d\n", *VAL);
// Increments here should be reflected outside this PID.
*VAL = *VAL + 1;
usleep(1000000);
}
exit(0);
} else {
app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
// The passing line
g_signal_connect (app, "activate", G_CALLBACK (activate), VAL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
*ABORT = 1;
}
*ABORT = 1;
return status;
}
If you want to refresh the label without pressing a button, you can use g_timeout_add and set a function to be called at regular intervals refreshing VAL.
g_timeout_add solution
To allow for an automatic update of the main loop from the application we can use g_timeout_add as #David Ranieri pointed out. However, the API of GTK3 requires we pass the refresh function slightly differently to g_timeout_add.
Modifying the OP MWE and #David Ranieri's answer:
/*
* Update GTK label from variable stored in mmap
* Timeout Method
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <gtk/gtk.h>
static GtkWidget *text_status;
static gboolean refresh(gpointer data) {
char msg[32]={0};
g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)data);
gtk_label_set_text(GTK_LABEL(text_status), msg);
return TRUE;
}
static void activate (GtkApplication *app, gpointer localval) {
GtkWidget *window;
// Button Containers
GtkWidget *button_box_quit;
// Buttons
GtkWidget *exit_button;
// Define Window, dynamic size for screen.
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "test");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
// Define Button Boxes.
button_box_quit = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
// Define Exit Button, put it in a box, put box in window
exit_button = gtk_button_new_with_label ("Exit");
gtk_container_add(GTK_CONTAINER (button_box_quit), exit_button);
gtk_container_add(GTK_CONTAINER (window), button_box_quit);
// Connect signals to buttons
g_signal_connect_swapped (exit_button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
// Define text status
text_status = gtk_label_new(NULL);
gtk_container_add(GTK_CONTAINER (button_box_quit), text_status);
// Define timeout
g_timeout_add(500, G_SOURCE_FUNC(refresh), localval);
// Activate!
refresh(localval);
gtk_widget_show_all (window);
}
int main (int argc, char **argv) {
GtkApplication *app;
int status;
int *VAL = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
int *ABORT = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
int pid = fork();
if (pid == 0) {
while(!*ABORT) {
printf("%d\n", *VAL);
// Increments here should be reflected outside this PID.
*VAL = *VAL + 1;
usleep(1000000);
}
exit(0);
} else {
app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
// The passing line
g_signal_connect (app, "activate", G_CALLBACK (activate), VAL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
*ABORT = 1;
}
*ABORT = 1;
return status;
}
The important differences:
We no longer pass the empty widget to refresh like we do when using a callback.
GTK3 must be explicitly told that refresh is a G_SOURCE_FUNC.
This follows from my previous question, I am trying to set the mouse cursor to cross hair when hovering over a GtkDrawingArea. I am trying to apply the answer from ebassi to the following code from zetcode. So far I have got:
#include <cairo.h>
#include <gtk/gtk.h>
static void do_drawing(cairo_t *);
struct {
int count;
double coordx[100];
double coordy[100];
} glob;
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
do_drawing(cr);
return FALSE;
}
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width(cr, 0.5);
int i, j;
for (i = 0; i <= glob.count - 1; i++ ) {
for (j = 0; j <= glob.count - 1; j++ ) {
cairo_move_to(cr, glob.coordx[i], glob.coordy[i]);
cairo_line_to(cr, glob.coordx[j], glob.coordy[j]);
}
}
glob.count = 0;
cairo_stroke(cr);
}
static gboolean
on_crossing (GtkWidget *darea, GdkEventCrossing *event)
{
switch (gdk_event_get_event_type (event))
{
case GDK_ENTER_NOTIFY:
printf("Yey!");
break;
case GDK_LEAVE_NOTIFY:
printf("Whooo!");
break;
}
}
static gboolean clicked(GtkWidget *widget, GdkEventButton *event,
gpointer user_data)
{
if (event->button == 1) {
glob.coordx[glob.count] = event->x;
glob.coordy[glob.count++] = event->y;
}
if (event->button == 3) {
gtk_widget_queue_draw(widget);
}
return TRUE;
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
glob.count = 0;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(window), darea);
gtk_widget_set_has_window (GTK_WIDGET (darea), TRUE);
int crossing_mask = GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
gtk_widget_add_events (GTK_WIDGET (darea), crossing_mask);
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
g_signal_connect (darea, "enter-notify-event", G_CALLBACK (on_crossing), NULL);
g_signal_connect (darea, "leave-notify-event", G_CALLBACK (on_crossing), NULL);
g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(window, "button-press-event",
G_CALLBACK(clicked), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_window_set_title(GTK_WINDOW(window), "Lines");
gtk_widget_show_all(window);
gtk_main();
return 0;
}
However, it is not compiling, gcc 5.3.0 tells the error:
undefined reference to `gdk_event_get_event_type'
What am i doing wrong?
I am working on a Win10 machine, using MinGW. I have not tried yet to compile this in a GNU/Linux system.
"Undefined reference" means that you're using a version of GTK+ that does not have gdk_event_get_event_type(). That function was introduced in GTK+ 3.10, as the documentation specifies; 3.10.0 was released in September 2013, so it means you have a version that is at least 4 years older than that, and very much unsupported.
The latest version of GTK+ is, at the time I'm writing this answer, 3.22.12, released in April 2017.
Please, follow the instructions on the GTK+ website on how to install GTK+ on Windows.
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`
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]);
}
}
I've done this simple app in Gtk, just to test things out... I come from swing so redefining a draw event function is normal for me... Anyway seems not to work:
#include <gtk-2.0/gtk/gtk.h>
#include <gtk-2.0/gdk-pixbuf/gdk-pixbuf.h>
#include <stdlib.h>
#include<string.h>
#include<stdio.h>
#include <iostream>
GdkPixbuf *imm;
void destroy(GtkWidget *widget, gpointer data) {
gtk_main_quit();
}
gboolean on_expose_event(GtkWidget *widget, GdkEventExpose *event,
gpointer data) {
gdk_draw_pixbuf((GdkDrawable*) widget, widget->style->white_gc, imm, 0, 0,
0, 0, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
return FALSE;
}
int main(int argc, char** argv) {
char* filename = new char[1000];
GError *error = NULL;
GtkWidget *window;
gtk_set_locale();
gtk_init(&argc, &argv);
if (argv[1] == NULL) {
std::cout << "Err.";
return -1;
}
strcpy(filename, argv[1]);
imm = gdk_pixbuf_new_from_file(filename, &error);
if (!imm) {
std::cout << "err closing";
return 0;
}
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_size_request((GtkWidget*) window, 500, 350);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL);
g_signal_connect(window, "expose-event",
G_CALLBACK(on_expose_event), NULL);
gtk_container_set_border_width(GTK_CONTAINER (window), 10);
gtk_widget_show(window);
gtk_main();
return 0;
}
..in fact at runtinme says (on line gdk_draw_pixbuf(....)):
(cConvolve:5011): Gdk-CRITICAL **: gdk_draw_pixbuf: assertion `GDK_IS_DRAWABLE (drawable)' failed
is it because the pixbuf is not good??? Or is it because I can't draw to the window like this?
It's because you cast GtkWidget to GdkDrawable, whereas GtkWidget doesn't inherit from GdkDrawable. Use
gdk_draw_pixbuf(GDK_DRAWABLE(gtk_widget_get_window(widget)), blah blah...);
Anyway in normal GTK use you don't have to do any drawing in expose handlers. To display an image, just use the GtkImage widget:
GtkImage *image = gtk_image_new_from_file(filename);
gtk_container_add(GTK_CONTAINER(window), image);
Widgets don't inherit from GdkDrawable, you need to get the drawable from the widget's window.
Thanks. But my purpose was to try drawing on the window bg... anyway, even if I dont' get anymore asserts, it doesn't work:
gboolean on_expose_event(GtkWidget *widget, GdkEventExpose *event,
gpointer data) {
int w, h;
w = gdk_pixbuf_get_width(imm);
h = gdk_pixbuf_get_height(imm);
gdk_draw_pixbuf((GdkDrawable*) widget->window, widget->style->fg_gc[ GTK_STATE_NORMAL ], imm, 0, 0,
0, 0, w, h, GDK_RGB_DITHER_NORMAL, 0, 0);
return FALSE;
}