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