====== 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**
===== 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);