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>
#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; }