Re: Proper way to provide gtk+ app with asynchronous data?



Paul Pogonyshev wrote:
Russell Shaw wrote:

I'm trying to figure out how to wake up the main gtk gui thread when
new data is available in a GAsyncQueue (command_queue), so that i can
display the data in a gtk widget:

Here is an excerpt from Quarry (http://home.gna.org/quarry/) that
does exactly what you described...

Thanks so much Paul.P, Todd.F, been wondering for months;)

-----------------------------------------------------------------------

#include<glib.h>
#include<glib/gprintf.h>
#include<gtk/gtk.h>
#include<gtk/gtksignal.h>

static gboolean thread_events_callback(gpointer data);
static gboolean thread_events_prepare(GSource *source, gint *timeout);
static gboolean thread_events_check(GSource *source);
static gboolean thread_events_dispatch(GSource *source, GSourceFunc callback, gpointer user_data);


static GtkWidget *label;

static GSource  *thread_events_source;
GAsyncQueue     *thread_events_queue;

typedef void (*ThreadEventCallback)(GtkWidget *label, gint result);
typedef struct _ThreadEventData ThreadEventData;

struct _ThreadEventData{
    ThreadEventCallback callback;
    GtkWidget *label;
    gint result;
};

static GSourceFuncs thread_events_functions={
    thread_events_prepare,
    thread_events_check,
    thread_events_dispatch,
    NULL,
};

gboolean
on_destroy(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
    gtk_main_quit();
    g_printf("quit\n");
    return FALSE;
}

static void
label_cb(GtkWidget *label, gint result)
{
    gchar str[10];
    g_snprintf(str,10,"i:%d\n",result);
    gtk_label_set_text(GTK_LABEL(label),str);
//    gdk_threads_enter();
//    gtk_main_iteration_do(FALSE);
//    gdk_threads_leave();
}

static gpointer
command_thread(gpointer data)
{
    ThreadEventData *event_data;

    gint i=0;
    for(;;){
        i++;
        g_usleep(G_USEC_PER_SEC*0.1);

        event_data=g_malloc(sizeof(ThreadEventData));
        event_data->callback=label_cb;
        event_data->label=label;
        event_data->result=i;

        g_async_queue_push(thread_events_queue, event_data);
        g_main_context_wakeup(NULL);
    }
    return NULL;
}

static gboolean
thread_events_callback(gpointer data)
{
    ThreadEventData *thread_completion_data=(ThreadEventData*)data;
    thread_completion_data->callback(
            thread_completion_data->label,
            thread_completion_data->result);
    return TRUE;
}

static gboolean
thread_events_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
    gpointer data=g_async_queue_pop(thread_events_queue);
    gboolean result=callback(data);

    g_free(data);
    return result;
}

static gboolean
thread_events_prepare(GSource *source, gint *timeout)
{
    *timeout=-1;
    return g_async_queue_length(thread_events_queue)>0;
}

static gboolean
thread_events_check(GSource *source)
{
    return g_async_queue_length(thread_events_queue)>0;
}

int
main(int argc, char *argv[])
{
    g_thread_init(NULL);
    gdk_threads_init();
    gtk_init(&argc,&argv);

    GtkWidget *window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
    label=gtk_label_new("");

    gtk_container_add(GTK_CONTAINER(window),label);

    gtk_widget_show_all(window);

    g_signal_connect(window,"delete-event",G_CALLBACK(on_destroy),NULL);
    g_signal_connect(window,"destroy-event",G_CALLBACK(on_destroy),NULL);

    thread_events_queue=g_async_queue_new();
    g_thread_create(command_thread,NULL,FALSE,NULL);

    thread_events_source=
             g_source_new(&thread_events_functions,sizeof(GSource));

    g_source_set_callback(
                          thread_events_source,
                          thread_events_callback,
                          NULL,
                          NULL
                         );

    g_source_attach(thread_events_source,NULL);

    gtk_main();

    g_source_destroy(thread_events_source);
    g_async_queue_unref(thread_events_queue);
    return 0;
}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]