====== 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 '
' >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 "
TrueFalseTest" >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 "
TrueFalseTest" >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 "
FalseTrueFalseverticalTrueFalseYou have clicked 0 timesFalseTrue0CliccamiTrueTrueTrueFalseTrue1
" > 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 "
FalseTrueFalseverticalTrueFalseYou have clicked 0 timesFalseTrue0CliccamiTrueTrueTrueFalseTrue1
" > 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