Barra laterale

programmazione:gtk:catturare_output_di_una_pipe

Catturare l'output di una pipe e stamparlo dentro una textview

Autore: Fabio Di Matteo
Ultima revisione: 05/05/2023 - 11:09

Versione GLib

main.c

#include <gtk/gtk.h>
 
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

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="mainWindow">
    <property name="width_request">500</property>
    <property name="height_request">300</property>
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <signal name="delete-event" handler="on_mainWindow_delete_event" swapped="no"/>
    <child type="titlebar">
      <placeholder/>
    </child>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkEntry" id="myEntry">
                <property name="width_request">508</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="myButton">
                <property name="label">gtk-execute</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
                <property name="always_show_image">True</property>
                <signal name="clicked" handler="myButton_clicked_cb" object="myEntry" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow" id="myScrolledWindow">
            <property name="height_request">400</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="shadow_type">in</property>
            <child>
              <object class="GtkTextView" id="myTextView">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkProgressBar" id="myProgressBar">
            <property name="height_request">15</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="text" translatable="yes">Pronto.</property>
            <property name="show_text">True</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

Versione Posix

#include <gtk/gtk.h>
 
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);

programmazione/gtk/catturare_output_di_una_pipe.txt · Ultima modifica: 05/05/2023 - 11:10 da Fabio Di Matteo