Hi,

thanks a lot for those hints. The interesting part is that your program
below shows the same behaviour: I don't see any output until the 
embedded application (geotoad in this case) has ended. This is
rather annoying as the program runs quite some seconds and the
user may think something is broken. Perhaps geotoad misses
some "flushs", but it does not show the problem when being run
from the xterminal/whatever.

Just for the context: I am working on gpxview 
(http://maemo.org/downloads/product/OS2008/gpxview/) a geocaching
application written for hildon/maemo (but also runnable on plain gtk).
And i want to embed geotoad (http://code.google.com/p/geotoad/) to
gather/spider live geocaching info while on the go.

Thanks again,
  Till


Am Freitag 20 November 2009 schrieb Dov Grobgeld:
> Here's my solution to the problem. It runs the external command in an
> external thread. This is probably an overkill for the problem at hand, on
> the other hand it is a good demo for how to create a worker thread and
> capture its output in the GUI. This currently does not work with windows at
> it is using popen() and g_io_channel_unix_new() but it should be trivial to
> fix and is left as an exercise for the reader.
> 
> Compile with:
> 
> gcc -o stdout-to-textview `pkg-config --cflags --libs gtk+-2.0 gthread-2.0`
> stdout-to-textview.c
> 
> //======================================================================
> //  stdout-to-textview.c
> //
> //  An example how to place stdout from a an external process
> //  into a text view buffer by running the process in a separate
> //  thread.
> //
> //  This program is released under the LGPL v3.0.
> //
> //  Dov Grobgeld <dov.grobg...@gmail.com>
> //  Fri Nov 20 09:22:39 2009
> //----------------------------------------------------------------------
> 
> #include <stdio.h>
> #include <stdlib.h>
> #include <gtk/gtk.h>
> 
> // This structure contains all the thread info for the job.
> typedef struct {
>     GMutex *update_mutex;
>     GCond *update_cond;
>     GMutex *mutex_to_run;
> 
>     gchar *cmd;
>     gchar *info;
> }  JobData;
> 
> // Sorry, out of laziness I made the widgets global.
> GtkWidget *w_text_view = NULL;
> GtkWidget *w_entry_cmd = NULL;
> GMutex *mutex_one_job_at_a_time = NULL;
> 
> // Create the data for a job
> JobData *job_data_new(const char *cmd,
>                       GMutex *mutex_to_run)
> {
>     JobData *job_data = g_new0(JobData, 1);
> 
>     job_data->cmd = g_strdup(cmd);
>     job_data->update_mutex = g_mutex_new();
>     job_data->update_cond = g_cond_new();
>     job_data->mutex_to_run = mutex_to_run;
> 
>     return job_data;
> }
> 
> // free the data from a job
> void job_data_free(JobData *job_data)
> {
>     g_free(job_data->cmd);
>     g_mutex_free(job_data->update_mutex);
>     g_cond_free(job_data->update_cond);
>     g_free(job_data);
> }
> 
> 
> // This function receives a requst from a worker thread asking to
> // update the gui with the required info.
> gboolean cb_update_job(JobData *job_data)
> {
>     if (job_data->info) {
>         GtkTextBuffer *text_buffer =
> gtk_text_view_get_buffer(GTK_TEXT_VIEW(w_text_view));
>         GtkTextIter end_iter;
>         gtk_text_buffer_get_end_iter(text_buffer,
>                                      &end_iter);
>         gtk_text_buffer_insert(text_buffer,
>                                &end_iter,
>                                job_data->info,
>                                -1);
>         gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW(w_text_view),
>                                       &end_iter, 0.0, TRUE, 0.0, 0.5);
>         g_free(job_data->info);
>         job_data->info = NULL;
>     }
> 
>     // Indicate that the update is done
>     g_mutex_lock(job_data->update_mutex);
>     g_cond_signal(job_data->update_cond);
>     g_mutex_unlock(job_data->update_mutex);
> 
>     return FALSE;
> }
> 
> // A helper function run in the job thread receiving the string that
> // should be displayed in the textview.
> void job_add_to_text_viewer(JobData *job_data,
>                             const char *info)
> {
>     job_data->info = g_strdup(info);
> 
>     // Lock mutex to make sure that we will receive the condition signal
>     g_mutex_lock(job_data->update_mutex);
>     g_idle_add((GSourceFunc)cb_update_job, job_data);
> 
>     // Wait for cb_update_job to tell me that the update is done
>     g_cond_wait(job_data->update_cond,
>                 job_data->update_mutex);
>     g_mutex_unlock(job_data->update_mutex);
> }
> 
> // The thread entry point. It will do the job, send the data to the
> // GUI and self destruct when it is done.
> static gpointer thread_worker(JobData *job_data)
> {
>     FILE *fh = popen(job_data->cmd,"r");
>     printf("thread_worker running %s\n", job_data->cmd);
>     GIOChannel *gh = g_io_channel_unix_new(fileno(fh));
>     GIOStatus status;
>     GError *error = NULL;
>     gsize length;
>     gsize terminator_pos;
>     gchar *str_return;
> 
>     while( (status = g_io_channel_read_line(gh,
>                                             &str_return,
>                                             &length,
>                                             &terminator_pos,
>                                             &error)) == G_IO_STATUS_NORMAL)
> {
>         job_add_to_text_viewer(job_data,
>                                str_return);
>         g_free(str_return);
>     }
> 
>     g_io_channel_unref(gh);
>     pclose(fh);
>     job_add_to_text_viewer(job_data,
>                            "Job done!");
> 
>     g_mutex_unlock(job_data->mutex_to_run);
>     g_thread_exit(NULL);
>     if (job_data)
>         job_data_free(job_data);
> 
>     return NULL;
> }
> 
> // Callback for the run button
> void cb_clicked_run(GtkWidget *widget,
>                     gpointer  user_data)
> {
>     const gchar *cmd = gtk_entry_get_text(GTK_ENTRY(w_entry_cmd));
>     GError *error = NULL;
>     printf("Run %s\n", cmd);
> 
>     // create a thread that will run the external command
>     // tbd...
>     JobData *job_data = job_data_new(cmd, mutex_one_job_at_a_time);
>     g_thread_create((GThreadFunc)thread_worker, job_data, FALSE, &error);
>     if (error) {
>         printf("%s\n", error->message);
>         g_error_free(error);
>     }
> }
> 
> void create_gui()
> {
>     GtkWidget *w_top = gtk_window_new(GTK_WINDOW_TOPLEVEL);
>     GtkWidget *w_vbox = gtk_vbox_new(FALSE, 0);
>     gtk_container_add(GTK_CONTAINER(w_top), w_vbox);
> 
>     GtkWidget *w_scrolled_win = gtk_scrolled_window_new(NULL, NULL);
>     gtk_widget_set_size_request(w_scrolled_win, 500, 400);
>     gtk_box_pack_start(GTK_BOX(w_vbox), w_scrolled_win,
>                        TRUE, TRUE, 0);
>     w_text_view = gtk_text_view_new();
>     gtk_container_add(GTK_CONTAINER(w_scrolled_win),
>                       w_text_view);
> 
>     // An hbox with an entry for the command to run
>     GtkWidget *w_hbox = gtk_hbox_new(FALSE, 0);
>     gtk_box_pack_start(GTK_BOX(w_vbox), w_hbox,
>                        FALSE, FALSE, 0);
>     gtk_box_pack_start(GTK_BOX(w_hbox), gtk_label_new("Command:"),
>                        FALSE, FALSE, 0);
>     w_entry_cmd = gtk_entry_new();
>     gtk_entry_set_text(GTK_ENTRY(w_entry_cmd),
>                        "perl -e '$|++; for $i (0..10) { print \"$i\\n\";
> sleep 1 }'");
>     gtk_box_pack_start(GTK_BOX(w_hbox), w_entry_cmd,
>                        TRUE, TRUE, 0);
>     GtkWidget *w_button_run = gtk_button_new_with_label("Run");
>     gtk_box_pack_start(GTK_BOX(w_hbox), w_button_run,
>                        FALSE, FALSE, 0);
>     g_signal_connect(w_button_run, "clicked",
>                      G_CALLBACK(cb_clicked_run), NULL);
> 
>     // Finally quit button
>     GtkWidget *w_button_quit = gtk_button_new_with_label("Quit");
>     gtk_box_pack_start(GTK_BOX(w_vbox), w_button_quit,
>                        FALSE, FALSE, 0);
>     g_signal_connect(w_button_quit, "clicked",
>                      G_CALLBACK(gtk_main_quit), NULL);
> 
> 
>     gtk_widget_show_all(w_top);
> }
> 
> int main(int argc, char **argv)
> {
>     // init threads
>     g_thread_init(NULL);
>     gtk_init(&argc, &argv);
> 
>     // Only allow running one command at a time
>     mutex_one_job_at_a_time = g_mutex_new();
> 
>     create_gui();
> 
>     gtk_main();
>     exit(0);
> }
> 
> 
> On Fri, Nov 20, 2009 at 10:38, Emmanuel Rodriguez <
> emmanuel.rodrig...@gmail.com> wrote:
> 
> > On Thu, Nov 19, 2009 at 10:11 PM, Till Harbaum / Lists <li...@harbaum.org
> > >wrote:
> >
> > > Hi,
> > >
> > > i am trying to run a text mode application in the background of my gtk
> > one
> > > and display its output in a textview. I know this is supposed to be done
> > > using g_spawn_async_with_pipes and then link to the output via
> > > g_io_add_watch. I even got something that sort of works, but the output
> > is
> > > very much delayed and comes in chunks and worse, the CPU load is at max
> > > while and after i run my code.
> > >
> > > Are there any examples for doing this? There must be many programs doing
> > > something similar to run e.g. some little helper program or similar.
> > >
> > > Can you consider using the VteTerminal[1] widget? This is the same
> > terminal
> > widget used in gnome-terminal and in Ubuntu/Debian graphical frontends to
> > dpkg. It lets you embed a ternimal that you can bind to any program, you're
> > not forced to bind it to a shell.
> >
> > VteTerminal will take care of monitoring your process and grabbing all
> > output for you. The only drawback is that the widget doesn't work on win32.
> >
> > [1] http://library.gnome.org/devel/vte/unstable/VteTerminal.html
> >
> > Emmanuel Rodriguez
> > _______________________________________________
> > gtk-app-devel-list mailing list
> > gtk-app-devel-list@gnome.org
> > http://mail.gnome.org/mailman/listinfo/gtk-app-devel-list
> >
> 

_______________________________________________
gtk-app-devel-list mailing list
gtk-app-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-app-devel-list

Reply via email to