====== Creazione automatica dello scheletro per applicazioni gtkbuilder ====== Autore: **//Fabio Di Matteo//** \\ Ultima revisione: **//19/02/2018 - 15:34//** \\ \\ Sviluppare con le librerie gtk puo' risultare tedioso nel caso in cui ogni volta ci si deve creare a mano il sorgente base, i vari script per la generazione del makefile e la struttura delle directory del progetto. Vedremo in questo articolo di creare uno script che possa automatizzare il tutto. \\ Utilizzeremo il tool CMake per generare il makefile multipiattaforma.\\ ===== Utilizzo dello script ===== L'utilizzo dello script e' immediato, basta infatti impartire da console i seguenti comandi: ./auto-gtk ===== Versione con Meson , file delle risorse e supporto all'installazione ===== #!/bin/sh AUTHOR="Fabio Di Matteo" VERSION="0.1.2" #Controllo se e' stato fornito il nome del progetto if [ "$1" = "" ];then echo "E' necessario fornire il nome del progetto come parametro." exit 1 ; fi #Se la directory del progetto esiste gia' esco. mkdir $1 2>/dev/null if [ "$?" != 0 ];then echo "Il progetto esiste." exit 1 ; fi cd $1 echo "Genero il main.c ..." echo "#include GtkBuilder *xml; int c=0; static void count_clicks (GtkWidget *widget,gpointer data) { c++; char* txt=g_strdup_printf(\" %d clicked!\", c); gtk_label_set_text(GTK_LABEL(data),txt); } int main (int argc, char *argv[]) { gtk_init (&argc, &argv); GError* error = NULL; xml = gtk_builder_new (); if (!gtk_builder_add_from_resource (xml, \"/org/myapp/res/gui.ui\", &error)) { g_warning (\"Error on load builder file: %s\", error->message); g_error_free (error); } GObject* window1=gtk_builder_get_object (xml,\"window1\"); GObject* button1=gtk_builder_get_object (xml,\"button1\"); GObject* label1=gtk_builder_get_object (xml,\"label1\"); g_signal_connect (button1, \"clicked\", G_CALLBACK (count_clicks), label1); g_signal_connect (window1, \"destroy\", G_CALLBACK (gtk_main_quit), NULL); gtk_window_present(GTK_WINDOW(window1)); gtk_main (); return 0; } " > main.c echo "Genero il file delle risorse..." echo " gui.ui " >resource.xml echo "Genero il file dell'interfaccia Glade..." echo ' window1 True False Test True False True True False label1 0 0 button True False True 0 1 ' >gui.ui echo "Genero il meson.build" echo " project('main', 'c') gnome = import('gnome') myresources = gnome.compile_resources( 'my-resource', 'resource.xml', source_dir: '.', c_name: 'myresource' ) extra_args= ['-rdynamic'] gtkdep = dependency('gtk+-3.0') gthread = dependency('gthread-2.0') gmodule = dependency('gmodule-2.0') executable('main', myresources,'main.c', dependencies :[gtkdep, gthread, gmodule ], c_args : extra_args, install : false, install_dir : '/usr/bin') ">meson.build echo "Per configurare il progetto : meson setup " echo "Per compilare il progetto: cd ; ninja" echo "Per installare il progetto: sudo ninja install" ==== Versione CMake (senza file risorse) ==== per creare la directory del progetto con dentro tutte le sottodirectory e il file CMakeLists.txt . drwxr-xr-x 2 fabio fabio 4096 2009-12-22 15:32 bin -rw-r--r-- 1 fabio fabio 1050 2009-12-22 15:32 CMakeLists.txt -rwxr-xr-x 1 fabio fabio 18 2009-12-22 15:32 configure drwxr-xr-x 3 fabio fabio 4096 2009-12-22 15:32 share drwxr-xr-x 2 fabio fabio 4096 2009-12-22 15:32 src dentro **bin** verra posizionato l'eseguibile ; \\ in **src** sta il sorgente; \\ mentre in **share** c'e' una directory con lo stesso nome del progetto che verra copiata in fase di installazione in ''$PREFIX/share/''. In questa directory e' contenuto il file xml di Glade ; ===== Compilazione e installazione del progetto ===== Quando vorremo compilare e installare il progetto basteranno i seguenti comandi: cmake . make sudo make install oppure ./configure make sudo make install Qualora si vogliano cambiare le opzioni di configurazione e' sufficente lanciare il comando ccmake . se il comando ccmake non e' installato lo si puo' installare in ubuntu con ''sudo apt-get install cmake-curses-gui'' ===== Lo script ===== #!/bin/sh AUTHOR="Fabio Di Matteo" VERSION="0.1.0" #Controllo se e' stato fornito il nome del progetto if [ "$1" = "" ];then echo "E' necessario fornire il nome del progetto come parametro." exit 1 ; fi #Se la directory del progetto esiste gia' esco. mkdir $1 2>/dev/null if [ "$?" != 0 ];then echo "Il progetto esiste." exit 1 ; fi cd $1 mkdir bin mkdir share && mkdir share/$1 echo "Genero il CMakeLists.txt ...\n" echo "cmake_minimum_required(VERSION 2.6)" > CMakeLists.txt echo "PROJECT($1)" >> CMakeLists.txt echo "SET (AUTHOR \"$AUTHOR\" INTERNAL \"Author\")" >> CMakeLists.txt echo "SET (VERSION \"$VERSION\")" >> CMakeLists.txt echo "INCLUDE (FindPkgConfig)" >> CMakeLists.txt echo "IF (NOT PKG_CONFIG_FOUND)" >> CMakeLists.txt echo " MESSAGE (FATAL_ERROR \"pkg-config not found...\") " >> CMakeLists.txt echo "ENDIF (NOT PKG_CONFIG_FOUND)" >> CMakeLists.txt echo "pkg_check_modules (GTK REQUIRED gtk+-2.0>=2.6)" >> CMakeLists.txt echo "IF (NOT GTK_FOUND)" >> CMakeLists.txt echo " MESSAGE(FATAL_ERROR \"You don't seem to have gtk >= 2.6 development libraries installed...\")" >> CMakeLists.txt echo "ENDIF (NOT GTK_FOUND)" >> CMakeLists.txt echo "ADD_DEFINITIONS()" >> CMakeLists.txt echo "INCLUDE_DIRECTORIES (. \${GTK_INCLUDE_DIRS})" >> CMakeLists.txt echo "LINK_DIRECTORIES (\${GTK_LIBRARY_DIRS} )" >> CMakeLists.txt echo "LINK_LIBRARIES (\${GTK_LIBRARIES} )" >> CMakeLists.txt echo "#Indichiamo dove sara' messo l'eseguibile" >>CMakeLists.txt echo "ADD_EXECUTABLE( bin/$1 src/main.c)" >> CMakeLists.txt echo "#Se se si ha bisogno di eventuali dipendenze fornite da noi stessi" >> CMakeLists.txt echo "#ADD_DEPENDENCIES ($1 src/main.c)" >> CMakeLists.txt echo "#Variabili per le directory di installazione" >> CMakeLists.txt echo "#ADD_DEFINITIONS (-DVERSION=\\\"\${VERSION}\\\")" >> CMakeLists.txt echo "#ADD_DEFINITIONS (-DDATADIR=\\\"\${CMAKE_INSTALL_PREFIX}/share\\\")" >> CMakeLists.txt echo "#Copia file per l'installazione" >> CMakeLists.txt echo "#ADD_SUBDIRECTORY (src)" >> CMakeLists.txt echo "INSTALL (TARGETS bin/$1 DESTINATION bin)" >> CMakeLists.txt echo "INSTALL (DIRECTORY share/$1 DESTINATION share)" >> CMakeLists.txt echo "#!/bin/sh" > configure echo "cmake ." >> configure chmod 755 configure echo "Genero il main.c" mkdir src && cd src echo "#include //includo le librerie gtk e glade. GtkBuilder *xml; //questo è il puntatore al file xml che contiene l'interfaccia GtkWidget *widget; //questa variabile serve per recuperare di volta in volta il // widget (ovvero l'oggetto ) che vogliamo usare void on_MainWindow_delete_event(GtkWidget *widget, gpointer user_data) //altra callback associata alla chiusura della { // finestra gtk_main_quit(); } int main (int argc, char *argv[]) { gtk_init (&argc, &argv); gchar* base; gchar* glade_file; if (g_find_program_in_path (\"$1\")==NULL){ /*Ricava il percorso dell'eseguibile, senza il nome del file*/ base = g_path_get_dirname(argv[0]); /*Concatena il percorso base al nome file gui.glade */ glade_file = g_build_filename (base,\"../share/$1\", \"gui.glade\", NULL); }else{ base = g_path_get_dirname(g_find_program_in_path (\"$1\")); glade_file = g_build_filename (base,\"../share/$1\", \"gui.glade\", NULL); } /*Infine carica come disolito il file dell'interfaccia */ GError* error = 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); } /* connette tutti gli eventi dei widget alle rispettive funzioni */ gtk_builder_connect_signals (xml, NULL); /* Avvia il ciclo principale delle gtAvvia il ciclo principale delle gtk*/ gtk_main (); return 0; }" > main.c #scrivo il file della GUI cd ../share/$1 echo " True " > gui.glade ===== Creazione di uno scheletro per applicazioni che includono il file della gui dentro l'eseguibile ===== ==== Versione GTK+2 ==== #!/bin/sh AUTHOR="Fabio Di Matteo" VERSION="0.1.0" #Controllo se e' stato fornito il nome del progetto if [ "$1" = "" ];then echo "E' necessario fornire il nome del progetto come parametro." exit 1 ; fi #Se la directory del progetto esiste gia' esco. mkdir $1 2>/dev/null if [ "$?" != 0 ];then echo "Il progetto esiste." exit 1 ; fi cd $1 echo "Genero il main.c ..." echo "#include //includo le librerie gtk e glade. GtkBuilder *xml; //questo è il puntatore al file xml che contiene l'interfaccia GtkWidget *widget; //questa variabile serve per recuperare di volta in volta il // widget (ovvero l'oggetto ) che vogliamo usare void on_MainWindow_delete_event(GtkWidget *widget, gpointer user_data) //altra callback associata alla chiusura della { // finestra gtk_main_quit(); } int main (int argc, char *argv[]) { gtk_init (&argc, &argv); GError* error = NULL; gchar* glade_file = g_build_filename(g_get_tmp_dir(),\"gui.xml\", NULL);; // Copio il file dalla gui dalle risorse a una cartella temporanea // (le gtk+2 non possono caricare direttamente il file). GFile* mySRC = g_file_new_for_uri(\"resource:///org/myapp/res/gui.xml\"); GFile* myDEST = g_file_new_for_path(glade_file); g_file_copy (mySRC, myDEST, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error); /*Infine carica come disolito il file dell'interfaccia */ 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); } /* connette tutti gli eventi dei widget alle rispettive funzioni */ gtk_builder_connect_signals (xml, NULL); /* Avvia il ciclo principale delle gtAvvia il ciclo principale delle gtk*/ gtk_main (); return 0; } " > main.c echo "Genero il file delle risorse..." echo " gui.xml " >resource.xml echo "Genero il file dell'interfaccia Glade..." echo " True False Test " >gui.xml echo "Genero il makefile..." echo " CPP = gcc OPTS = `pkg-config --cflags --libs gio-2.0 gtk+-2.0 gmodule-2.0` all: glib-compile-resources --generate-source resource.xml \$(CPP) resource.c main.c -o main \$(OPTS) clean: rm main" >makefile ==== Versione GTK+3 ==== #!/bin/sh AUTHOR="Fabio Di Matteo" VERSION="0.1.0" #Controllo se e' stato fornito il nome del progetto if [ "$1" = "" ];then echo "E' necessario fornire il nome del progetto come parametro." exit 1 ; fi #Se la directory del progetto esiste gia' esco. mkdir $1 2>/dev/null if [ "$?" != 0 ];then echo "Il progetto esiste." exit 1 ; fi cd $1 echo "Genero il main.c ..." echo "#include //includo le librerie gtk e glade. GtkBuilder *xml; //questo è il puntatore al file xml che contiene l'interfaccia GtkWidget *widget; //questa variabile serve per recuperare di volta in volta il // widget (ovvero l'oggetto ) che vogliamo usare void on_MainWindow_delete_event(GtkWidget *widget, gpointer user_data) //altra callback associata alla chiusura della { // finestra gtk_main_quit(); } int main (int argc, char *argv[]) { gtk_init (&argc, &argv); GError* error = NULL; gchar* glade_file = g_build_filename(g_get_tmp_dir(),\"gui.xml\", NULL);; /*Infine carica come disolito il file dell'interfaccia */ xml = gtk_builder_new (); if (!gtk_builder_add_from_resource (xml, \"/org/myapp/res/gui.xml\", &error)) { g_warning (\"Couldn\'t load builder file: %s\", error->message); g_error_free (error); } /* connette tutti gli eventi dei widget alle rispettive funzioni */ gtk_builder_connect_signals (xml, NULL); /* Avvia il ciclo principale delle gtAvvia il ciclo principale delle gtk*/ gtk_main (); return 0; } " > main.c echo "Genero il file delle risorse..." echo " gui.xml " >resource.xml echo "Genero il file dell'interfaccia Glade..." echo " True False Test " >gui.xml echo "Genero il makefile..." echo " CPP = gcc OPTS = \`pkg-config --cflags --libs gio-2.0 gtk+-3.0\` -rdynamic all: glib-compile-resources --generate-source resource.xml \$(CPP) resource.c main.c -o main \$(OPTS) clean: rm main" >makefile ==== Versione Vala ==== #!/bin/bash AUTHOR="Fabio Di Matteo" VERSION="0.1.0" #Controllo se e' stato fornito il nome del progetto if [ "$1" = "" ];then echo "E' necessario fornire il nome del progetto come parametro." exit 1 ; fi #Se la directory del progetto esiste gia' esco. mkdir $1 2>/dev/null if [ "$?" != 0 ];then echo "Il progetto esiste." exit 1 ; fi cd $1 echo "Make resorces folder..." mkdir res echo "Make main.vala..." echo " using Gtk; public class mainWindow : GLib.Object { public Button btn; public Window window; public Label myLabel; int c=0; public void on_button1_clicked () { c++; window.set_title(\"You have clicked \"+ c.to_string()+\" times\"); myLabel.set_text(\"You have clicked \"+ c.to_string()+\" times\"); } public mainWindow() { } public void show() { try { var builder = new Builder (); builder.add_from_resource(\"/app/res/gui.ui\"); btn=builder.get_object (\"button1\") as Button; myLabel=builder.get_object(\"label1\") as Label; btn.clicked.connect(on_button1_clicked); window = builder.get_object (\"window1\") as Window; window.destroy.connect(Gtk.main_quit); window.show_all (); } catch (Error e) { stderr.printf (\"Problem on gui loading: %s\n\", e.message); } } } int main (string[] args) { Gtk.init (ref args); mainWindow myWin = new mainWindow(); myWin.show(); Gtk.main (); return 0; } " > main.vala ; echo "Make meson.build" echo " project('$1', 'vala', 'c') gtk_dep = dependency('gtk+-3.0') r = run_command('compile_resources.sh') if r.returncode() != 0 error('Errors on compile resources') endif #check os os=build_machine.system() if os == 'linux' executable('$1', 'main.vala', 'resources.c', dependencies : [gtk_dep]) endif if os == 'windows' executable('$1', 'main.vala', 'resources.c', dependencies : [gtk_dep], link_args : ['-mwindows']) endif " > meson.build; echo "Make resources" echo " gui.ui " > res/resources.xml; echo " False True False vertical True False You have clicked 0 times False True 0 Cliccami True True True False True 1 " > res/gui.ui ; echo "#!/bin/bash cd res echo 'Building resources.c ...' glib-compile-resources --generate-source resources.xml mv resources.c ../ " > compile_resources.sh; chmod +x compile_resources.sh ==== Gtkmm ==== **auto-gtkmm** #!/bin/bash AUTHOR="Fabio Di Matteo" VERSION="0.1.0" #Controllo se e' stato fornito il nome del progetto if [ "$1" = "" ];then echo "E' necessario fornire il nome del progetto come parametro." exit 1 ; fi #Se la directory del progetto esiste gia' esco. mkdir $1 2>/dev/null if [ "$?" != 0 ];then echo "Il progetto esiste." exit 1 ; fi cd $1 echo "Make resorces folder..." mkdir res echo "Make main.cc..." echo " #include #include \"mainwindow.hpp\" int main(int argc, char *argv[]) { auto app = Gtk::Application::create(argc, argv, \"org.gtkmm.example\"); mainWindow mainw; return app->run(*mainw.window1); } " > main.cc ; echo "Make mainwindow.hpp..." echo " #ifndef MAINWINDOW_HPP #define MAINWINDOW_HPP #include class mainWindow { public: Glib::RefPtr builder; Gtk::Window *window1; Gtk::Button *button1; Gtk::Label *label1; mainWindow(); protected: void on_button_clicked(); private: /* add your private declarations */ }; #endif /* MAINWINDOW_HPP */ " > mainwindow.hpp ; echo "Make mainwindow.cpp..." echo " #include \"mainwindow.hpp\" #include mainWindow::mainWindow() { auto builder = Gtk::Builder::create(); try { builder->add_from_resource(\"/app/res/gui.ui\"); } catch(const Glib::FileError& ex) { std::cerr << \"FileError: \" << ex.what() << std::endl; } catch(const Glib::MarkupError& ex) { std::cerr << \"MarkupError: \" << ex.what() << std::endl; } catch(const Gtk::BuilderError& ex) { std::cerr << \"BuilderError: \" << ex.what() << std::endl; } builder->get_widget(\"window1\", window1); builder->get_widget(\"button1\", button1); builder->get_widget(\"label1\", label1); button1->signal_clicked().connect( sigc::mem_fun(*this, &mainWindow::on_button_clicked) ); } void mainWindow::on_button_clicked() { static int c; c++; label1->set_text(\"You have clicked \"+std::to_string(c)+\" times!\"); } " > mainwindow.cpp ; echo "Make meson.build" echo " project('GTKmm', 'cpp') gtkmm = dependency('gtkmm-3.0') r = run_command('compile_resources.sh') if r.returncode() != 0 error('Errors on compile resources') endif os=build_machine.system() if os == 'linux' executable('simple-meson', 'main.cc','resources.cpp','mainwindow.cpp', dependencies : gtkmm) endif if os == 'windows' executable('simple-meson', 'main.cc','resources.cpp','mainwindow.cpp', dependencies : gtkmm, link_args : ['-mwindows']) endif " > meson.build; echo "Make resources" echo " gui.ui " > res/resources.xml; echo " False True False vertical True False You have clicked 0 times False True 0 Cliccami True True True False True 1 " > res/gui.ui ; echo "#!/bin/bash cd res echo 'Building resources.c ...' glib-compile-resources --generate-source resources.xml mv resources.c ../resources.cpp " > compile_resources.sh; chmod +x compile_resources.sh