====== Catturare l'output di una pipe e stamparlo dentro una textview ====== Autore: **//Fabio Di Matteo//** \\ Ultima revisione: **// 05/05/2023 - 11:09 //** // // {{:programmazione:gtk:glib_get_output.png?500|}} ===== Versione GLib ===== **main.c** #include GObject *myEntry, *myButton, *myTextView, *myProgressBar, *myScrolledWindow; GtkTextBuffer *buffer; GtkBuilder *xml; void on_mainWindow_delete_event(GtkWidget *widget, gpointer data) { g_print("Ciao ciao...\n"); gtk_main_quit(); } void scrollToEnd(GtkWidget *widget, gpointer data) { GtkAdjustment * adj=gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(myScrolledWindow)); gdouble upper=gtk_adjustment_get_upper(GTK_ADJUSTMENT(adj)); gtk_adjustment_set_value (GTK_ADJUSTMENT(adj), upper); } //Semplice funzione che aggiorna la textview gboolean appendTextView(gchar* out) { gtk_text_buffer_insert_at_cursor (buffer,out, -1); gtk_text_view_set_buffer (GTK_TEXT_VIEW(myTextView),buffer); return FALSE; } //Callback che viene chiamata ogni qualvolta c'è dello standard output da mostrare static gboolean cb_out_watch( GIOChannel *channel, GIOCondition cond, gpointer user_data ) { gchar *string; gsize size; if( cond == G_IO_HUP ) { g_io_channel_unref( channel ); return( FALSE ); } //Leggo la riga corrente dallo stdout e la salvo in "string" g_io_channel_read_line( channel, &string, &size, NULL, NULL ); //Formatto e stampo a video "string" e dopo libero string dalla memoria gchar* s=g_strdup_printf("%s",string); //g_idle_add ((GSourceFunc) appendTextView, s ); //mostro nel textview g_idle_add_full (G_PRIORITY_HIGH_IDLE, (GSourceFunc) appendTextView, s, NULL); //mostro nel textview ad alta priorita' printf("%s", s); g_free( string ); return( TRUE ); } //Callback che viene chiamata ogni qualvolta c'è dello standard error da mostrare static gboolean cb_err_watch( GIOChannel *channel, GIOCondition cond, gpointer user_data ) { gchar *string; gsize size; if( cond == G_IO_HUP ) { g_io_channel_unref( channel ); return( FALSE ); } g_io_channel_read_line( channel, &string, &size, NULL, NULL ); //Formatto e stampo a video "string" e dopo libero string dalla memoria gchar* s=g_strdup_printf("Errore: %s",string); g_idle_add ((GSourceFunc) appendTextView, s ); //mostro nel textview printf("%s", s); g_free( string ); return( TRUE ); } void *runCommandPipe(gchar* cmd) { GPid pid; //il processid del processo figlio gchar *workingDir ="/usr/bin/" ; //la directory corrente dove si trova l'eseguibile gchar **launch = g_strsplit (cmd," ",0); //crea un array di stringhe a partire da una stringa di char gint in, //file descriptor per l'input (non usato) out, //file descriptor per l'stdoutput err; //file descriptor per lo stderr GIOChannel *out_ch, //canale per catturare l'output *err_ch; //canale per catturare gli errori gboolean ret; // se g_spawn_async_with_pipes ha successo è TRUE GError **error; //array per catturare gli eventuali errori all'avvio di g_spawn_async_with_pipes /* Avvia il processo figlio in background */ ret = g_spawn_async_with_pipes( workingDir, launch, NULL, G_SPAWN_DEFAULT, NULL, NULL, &pid, &in, &out, &err, error ); if( ! ret ) { g_error( "Pipe fallita." ); } //Creiamo i canali che servono per leggere i dati dalla pipe. out_ch = g_io_channel_unix_new( out ); err_ch = g_io_channel_unix_new( err ); // Creiamo i watch per ogni canale creato // in pratica ogni volta che la pipe restituisce dell'output dallo stdout // oppure dallo stderr vengono esseguite le relative funzioni callbacks g_io_add_watch( out_ch, G_IO_IN | G_IO_HUP, (GIOFunc)cb_out_watch, NULL ); g_io_add_watch( err_ch, G_IO_IN | G_IO_HUP, (GIOFunc)cb_err_watch, NULL ); if (error != NULL) { printf("Errore nella pipe\n"); } } void myButton_clicked_cb(GtkWidget *widget, gpointer data) { //Pulisco tutto il textBuffer gtk_text_buffer_set_text (GTK_TEXT_BUFFER(buffer),"",0); gchar* cmd= g_strdup_printf("%s", gtk_entry_get_text(GTK_ENTRY(data))); g_print("Comando-> %s\n",cmd); //Un thread avvia la funzione che eseguira' la pipe GThread *myThread; myThread= g_thread_new(NULL,(GThreadFunc)runCommandPipe,cmd); } void mainWindowInit() { GError* error = NULL; gchar* glade_file = g_build_filename("gui.ui", NULL); GObject *mainWindow ; buffer=gtk_text_buffer_new (NULL); xml = gtk_builder_new (); if (!gtk_builder_add_from_file (xml, glade_file, &error)) { g_warning ("Couldn\'t load builder file: %s", error->message); g_error_free (error); } mainWindow=gtk_builder_get_object (xml,"mainWindow" ); myScrolledWindow=gtk_builder_get_object (xml,"myScrolledWindow" ); g_signal_connect (myScrolledWindow, "size-allocate", G_CALLBACK (scrollToEnd), NULL); myButton=gtk_builder_get_object (xml,"myButton" ); myEntry=gtk_builder_get_object(xml,"myEntry"); myTextView=gtk_builder_get_object(xml,"myTextView"); myProgressBar=gtk_builder_get_object(xml, "myProgressBar"); gtk_entry_set_text(GTK_ENTRY(myEntry),"rsync -avz --info=progress2 --dry-run /usr/bin/ /home/fabio/Desktop/tt/"); g_signal_connect(myButton,"clicked",G_CALLBACK(myButton_clicked_cb),myEntry); g_signal_connect(mainWindow,"delete-event",G_CALLBACK(on_mainWindow_delete_event),NULL); g_object_unref( G_OBJECT( xml ) ); } int main (int argc, char **argv) { gtk_init (&argc, &argv); mainWindowInit(); gtk_main (); return 0; } **makefile** all: gcc main.c -o simple `pkg-config --cflags --libs gtk+-3.0 gthread-2.0 gmodule-2.0` **gui.glade** 500 300 True False True False vertical True False 508 True True False True 0 gtk-execute True True True True True False True 1 False True 0 400 True True in True True False True 1 15 True False Pronto. True False True 2 ===== Versione Posix ===== #include GObject *myEntry, *myButton, *myTextView ; GtkTextBuffer *buffer; void on_mainWindow_delete_event(GtkWidget *widget, gpointer data) { g_print("Ciao ciao..."); gtk_main_quit(); } gboolean updateTextView(gchar* out) { gtk_text_buffer_insert_at_cursor (buffer,out, -1); gtk_text_view_set_buffer (GTK_TEXT_VIEW(myTextView),buffer); return FALSE; } void *runCommandPipe(gchar* cmd) { gchar output[1000]; FILE *pipe = popen(cmd, "r" ); if (pipe == NULL ) { printf("Errore nella pipe"); } sleep(1); while(!feof(pipe) ) { if( fgets( output, 1000, pipe ) != NULL ) { printf("Il mio output: %s\n", output ); g_idle_add ((GSourceFunc) updateTextView, output ); } } pclose(pipe); } void myButton_clicked_cb(GtkWidget *widget, gpointer data) { gchar* cmd= g_strdup_printf("%s", gtk_entry_get_text(GTK_ENTRY(data))); g_print("Comando-> %s\n",cmd); //Run command GThread *myThread; myThread= g_thread_new(NULL,(GThreadFunc)runCommandPipe,cmd); } void mainWindowInit() { GError* error = NULL; gchar* glade_file = g_build_filename("gui.glade", NULL); GtkBuilder *xml; GObject *mainWindow ; buffer=gtk_text_buffer_new (NULL); xml = gtk_builder_new (); if (!gtk_builder_add_from_file (xml, glade_file, &error)) { g_warning ("Couldn\'t load builder file: %s", error->message); g_error_free (error); } mainWindow=gtk_builder_get_object (xml,"mainWindow" ); myButton=gtk_builder_get_object (xml,"myButton" ); myEntry=gtk_builder_get_object(xml,"myEntry"); myTextView=gtk_builder_get_object(xml,"myTextView"); g_signal_connect(myButton,"clicked",G_CALLBACK(myButton_clicked_cb),myEntry); g_signal_connect(mainWindow,"delete-event",G_CALLBACK(on_mainWindow_delete_event),NULL); g_object_unref( G_OBJECT( xml ) ); } int main (int argc, char **argv) { gtk_init (&argc, &argv); mainWindowInit(); gtk_main (); return 0; } ====== Catturare la percentuale di un download (per esempio) ====== while(!feof(pipe) ) { if( fgets( output, 100, pipe ) != NULL ) { printf("Il mio output: %s\n", output ); if (sscanf(output, "%*[^0-9%]%lf", &progress) ) { // ho catturato tutto l'output e anche una percentuale di un download // nelle variabili output e progress } } } pclose(pipe);