Questa vuole essere una piccola introduzione allo sviluppo in C++ utilizzando Glade3, per la creazione dell’interfaccia utente, e le librerie Gtk
Per questo programmino, ci faremo aiutare dagli autotools, quindi creiamo una directory, ad esempio “ex1”:
mkdir ex1
ed al suo interno creiamo un file configure.ac come il seguente:
AC_INIT(src/main.cc)
AM_INIT_AUTOMAKE(ex1,0.1)
AC_CONFIG_HEADER(config.h)
AC_PROG_CC
AC_PROG_CXX
AC_PROG_INSTALL
AC_PROG_LIBTOOL
PKG_CHECK_MODULES([GTKMM], [gtkmm-2.4 >= 2.8.0])
PKG_CHECK_MODULES([GLADEMM], [libglademm-2.4])
AC_SUBST(GTKMM_CFLAGS)
AC_SUBST(GTKMM_LIBS)
AC_SUBST(GLADEMM_CFLAGS)
AC_SUBST(GLADEMM_LIBS)
AC_OUTPUT(Makefile src/Makefile)
sempre in questa cartella, creiamo il file Makefile.am, al cui interno mettiamo:
SUBDIRS = src
così da avere il codice separato dagli altri files e quindi creiamola:
mkdir src
cd src
e dentro inseriamo il file Makefile.am
bin_PROGRAMS=ex1
ex1dir=$(datadir)
ex1_SOURCES=main.cc wmain.h wmain.cc
ex1_DATA=wmain.glade
EXTRA_DIST = wmain.glade
INCLUDES = $(GTKMM_CFLAGS) $(GLADEMM_CFLAGS) -DGLADEDIR=\""$(datadir)\""
LIBS = $(GTKMM_LIBS) $(GLADEMM_LIBS)
Siamo quindi pronti a scrivere il codice, iniziamo dal main.cc:
#include <iostream> #include "wmain.h" #include "config.h" int main(int argc, char *argv[]) { Gtk::Main kit(argc, argv); Glib::RefPtr<Gnome::Glade::Xml> refXml; Glib::ustring glade_filename = Glib::ustring(GLADEDIR) + "/wmain.glade"; try { refXml = Gnome::Glade::Xml::create(glade_filename); } catch(const Gnome::Glade::XmlError& ex) { std::cerr << ex.what() << std::endl; return 1; } wMain* W = 0; refXml->get_widget_derived("wMain", W); if(W) { kit.run(*W); } delete W; return 0; }
questo provvederà a caricare il file .glade (vediamo dopo come crearlo), estrarre il widget wMain, “associarlo” ad una nostra classe ed a lanciare l’applicazione
Vediamo come creare la nostra classe, iniziamo dall’header wmain.h:
#ifndef WMAIN_H #define WMAIN_H #include <gtkmm.h> #include <gtkmm/button.h> #include <libglademm.h> #include <iostream> using namespace std; class wMain : public Gtk::Window { public: wMain(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade); virtual ~wMain(); virtual void btnEsci(); virtual void btnOk(); private: Gtk::Entry *txtNome; }; #endif
Quindi passiamo al wmain.cc:
#include "wmain.h" wMain::wMain(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade) : Gtk::Window(cobject) { Gtk::Button *btn; /** btnEsci */ refGlade->get_widget("btnEsci", btn); if (btn) { btn->signal_clicked().connect( sigc::mem_fun(*this, &wMain::btnEsci) ); } else { cerr << "btnEsci non trovato!" << endl; } /** btnOk */ refGlade->get_widget("btnOk", btn); if (btn) { btn->signal_clicked().connect( sigc::mem_fun(*this, &wMain::btnOk) ); } else { cerr << "btnOk non trovato!" << endl; } refGlade->get_widget("txtNome", txtNome); } wMain::~wMain() { } void wMain::btnEsci() { cout << "Esci" << endl; Gtk::Main::instance()->quit(); } void wMain::btnOk() { Glib::ustring txt; txt="Testo ["+txtNome->get_text()+"]"; Gtk::MessageDialog dlg(*this, txt, false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, true); dlg.run(); }
praticamente abbiamo definito la classe wMain derivata dalla Gtk::Window, nel costruttore abbiamo quindi “cercato” i due tasti “btnEsci” e “btnOk”, collegando l’evento “clicked” ai nostri metodi wMain::btnEsci e wMain::btnOk, e abbiamo memorizzato il puntatore alla GtkEntry txtNome.
Nel metodo wMain::btnOk, andiamo ad estrarre il valore inserito nella entry e visualizzarlo tramite un message box, mentre in wMain::btnEsci lanciamo il Gtk::Main::instance()->quit() che termina l’applicazione.
Ora possiamo creare l’interfaccia grafica usando Glade3, quindi da terminale e dentro la cartella src lanciamo:
glade-3
Quello che ci comparirà sarà:
dalla tavolozza (dock a sinistra), premiamo il tastino “Finestra”
sempre dalla tavolozza, selezioniamo il contenitore “Casella verticale” ed impostamo il numero di elementi a 2
per ogni elemento (il box creato) inseriamo una “Casella orizzontale” di 2 elementi
Nel box in alto a sinistra inseriamo una GtkLabel selezionando dalla tavolozza, nel riquadro “Controlli e visualizzazione” il tastino “Etichetta”
Nel box in alto a destra invece inseriamo una GtkEntry (tastino “Entrata testo” sempre in “Controlli e visualizzazione”)
Nei box sotto inseriamo due GtkButton (tastino “Pulsante”)
impostando nelle proprietà la tipologia come “Stock” (vedi barra evidenziata)
la prima con “Pulsante stock” in “Applica”, l’altra con “Chiudi”
Ora non resta che assegnare i nomi come nella seguente figura
Salviamo quindi il file nella cartella “src” col nome “wmain.glade” e, tornando nella cartella principale del progetto diamo i comandi:
touch NEWS README AUTHORS ChangeLog
touch stamp-h
aclocal
autoheader
libtoolize -f
automake -ac --foreign
autoconf
Se tutto è andato bene, diamo un
./configure
make
e per lanciare il nostro programmino
cd src
./ex1
Buon divertimento! 😉
Pacchetti da installare
Ci sono una serie di pacchetti che occorre avere prima di procedere con lo sviluppo:
sudo apt-get install build-essential automake1.9 pkg-config libglademm-2.4-dev libgtkmm-2.4-dev
potrebbero mancarne alcuni, in quel caso fatemi sapere cosa mi sono dimenticato 😉
Link utili
Sicuramente vi saranno di aiuto
http://www.gtkmm.org/docs/gtkmm-2.4/docs/tutorial/html/index.html
http://sourceware.org/autobook/autobook/autobook_toc.html
gran bel post davvero! davvero interessantissimo! complimenti!
io ho iniziato da poco a cimentarmi allo sviluppo con C++ & GTK+ & GLADE.
spero seguiranno altri tuoi post del genere… saresti di grande aiuto.
grazie.
Grazie! 😀
Appena ho un po di tempo, scrivo qualcosa per GLADE+GNOME e sempre in C++ con gli autotools 😉
bel post davvero. Mi servirebbe una mano per iniziare ad usare anjuta2.2.0 e glade3.
La mia principale difficoltà consiste nell’intercettare i signals provenienti dagli oggetti inseriti nella main window. Anjuta nel main di un programma di esempio (classico Hello world) ha una procedura che carica il file .xml generato in glade. Questo passaggio mi è chiaro ma non capisco come interagire con gli oggetti fatti in glade
grazie
ciao
Non ho mai usato Anjuta, comunque penso che questo link ti possa essere utile http://www.gtkmm.org/docs/gtkmm-2.4/docs/tutorial/html/apb.html#id2576849 😉
quasi risolto. Solo che mi da un errore che non riesco ancora a capire. Sono un po’ duro!
error: request for member ‘on_okbutton_clicked’ in ‘okbutton’, which is of non-class type ‘Gtk::Button*’
la riga incriminata è:
okbutton.signal_clicked().connect(sigc::ptr_fun(&on_okbutton_clicked));
il pezzo di codice dove creo il pulsante è:
…….
Gtk::Window* main_win = 0;
Gtk::Button* okbutton;
refXml->get_widget(“okbutton”, okbutton);
refXml->get_widget(“main_window”, main_win);
if (main_win)
{
if (okbutton) {
okbutton.signal_clicked().connect(sigc::ptr_fun(&on_okbutton_clicked));
} else {
//cerr << “btnEsci non trovato!” << endl;
}
kit.run(*main_win);
}
return 0;
………….
grazie per l’aiuto
ciao
Prova così
okbutton->signal_clicked().connect(sigc::ptr_fun(&on_okbutton_clicked));
😉
era proprio quello. Ho ancora addosso gli influssi del java!
Grazie tante!
ci sentiamo
ciao
aiuto!
mi scrive qst
configure.ac:2: error: m4_defn: undefined macro: _m4_divert_diversion
configure.ac:2: the top level
autom4te: /usr/bin/m4 failed with exit status: 1
aclocal: autom4te failed with exit status: 1
quando faccio aclocal
come posso fare per sistemare?
Forse hai una versione vecchia di autoconf… ho fatto una ricerca su Google ed ho trovato questo http://www.gnu.org/software/autoconf/manual/autoconf-2.57/html_node/autoconf_163.html vedi se fa al caso tuo 😉
Esiste anche una traduzione della guida ufficiale delle gtkmm, magari puo’ esservi utile.
http://itagtkmm.blogspot.com/
Ciao,
ho un problema per far avviare il programma.
Mi compila tutto ma quando vado a dare il comando ./ex1 mi da questo errore:
(ex1:12767): libglademm-CRITICAL **: Glade::Xml::get_cwidget(): glade_xml_get_widget() failed for widget name=wMain
Mi puoi aiutare??!??
Grazie mille
Ps. complimenti per l’articolo
MMM… ma da Glade, la finestra principale (gtkWindow per intenderci) si chiama wMain? 😉
Si si, si chiama wMain.
E ti dirò di più, per vedere se funzionasse tutto, ho fatto un copia e incolla del tuo codice, ma il risultato è sempre negativo. Sempre quell’errore.
Ho rieseguito nuovamente la guida, e la compilazione non ha dato problemi, l’unica cosa che penso possa essere, è il nome della finestra creata con Glade.
Dalla cartella src, lancia questo comando
grep GtkWin wmain.glade
devi avere id=”wMain” 😉
Ok perfetto, risolto….
Grazie mille… 😉
complimenti per il POST…
leggendolo, in pochissimo tempo sono stato operativo sviluppando per la prima volta in gtk+autoconf!!!!
Veramente tutto chiaro… semplice e completo.
Vista la tua abilià, potresti estendere il tutto in una miniguida….
P.S. La sto consigliando a tutti gli amici developer… anche quelli che già sviluppano in GTK+ perche comunque questa lettura fa bene a anche a loro:-)
Seems like a great first-step intro! Is there any chance you could translate this into German or English?
Scusami, dove posso scaricare questo programma (Glade 3)?
PERFETTO!
grazie per questo tutorial
@Alex: Anche se in netto ritardo: http://glade.gnome.org/ 😉
ciao, bell’articolo complimenti…
volevo scrivere una piccola applicazione in gtk ma le mie conoscenze di c++ sono praticamente zero.
in pratica l’esempio che hai fatto si adatterebbe molto a quello che vorrei fare.
in pratica vorrei utilizzare il testo inserito nella text box come parametro da utilizzare nel lanciare un applicazione:
esempio: uno inserisce come testo un id di un video. vffpprrsjkl
e alla pressione del bottone ok dovrebbe aprirmi vlc http:youtube? ecc
ho provato con system () ma mi va in crash.
mi puoi aiutare?
mmm… strano, ho provato e funziona… per sicurezza ti riporto la modifica che ho eseguito magari ti potrà essere di aiuto
void wMain::btnOk() {
Glib::ustring txt;
txt="Testo ["+txtNome->get_text()+"]";
Gtk::MessageDialog dlg(*this, txt, false, Gtk::MESSAGE_INFO,
Gtk::BUTTONS_OK, true);
dlg.run();
system(txtNome->get_text().c_str());
}
😉
void wMain::btnOk() {
Glib::ustring txt;
system(txtNome->get_text().c_str());
}
ok la parte necessaria è questa senza che appaia la finestra modale.
solo che così funziona da runcommand, invece io cercavo di “parsare” solo il parametro.ovvero creare già una costante del tipo:
VLC= vlc 127.0.0.1:60606/ //comando da eseguire
e integrare questa costante in system()
in modo che il risultato sia
vlc 127.0.0.1:60606/txtNome->get_text().c_str()
ovvero quello che scrivo nella textentry.
ho fatto varie prove ma mi da sempre errore di sintassi nel make: mi dice che vlc non è dichiarata.
ne approfitto per chiedere un altra cosa: per eseguire un programma si usa
system() e per chiuderlo?? system(kiallall nomeprogramma) ??
grazie
Dovresti fare così
void wMain::btnOk() {
Glib::ustring txt;
txt="vlc 127.0.0.1:60606/"+txtNome->get_text();
system(txt.c_str());
}
per terminarlo potresti fare come hai detto tu, ma rischi di chiudere altri VLC non avviati da te… ma non capisco perchè dovresti chiuderlo… eventualmente lancialo in background
txt="vlc 127.0.0.1:60606/"+txtNome->get_text()+ " &";
PS – Se vuoi un buon manuale di C++ ti consiglio di vedere questo http://www.bo.cnr.it/corsi-di-informatica/corsoCstandard/Lezioni/01Indice.html e http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html 😉
grandissimo, tutto ok!!!
perfetta anche quella & per il background, infatti il mio primo problema era che quando avviavo un applicazione quest’ultima mi teneva in stallo il bottone fino a crasharmi la finestra. così facendo invece va tutto ok! grazie mille.
lo so che rompo le scatole… ma ho un altro problema
una volta testato il tutto decido di installarlo con il classico make install:
il binario mi viene salvato un usr/bin correttamente, ma il file glade non viene installato con il risultato che quando vado a lanciare l’eseguibile mi da l’errore che non trova il glade associato. :S
cosa devo modificare affinche le librerie vengano installate in usr/local/lib ?
volevo dire /usr/local/share
Ho modificato la guida sopra, praticamente nel src/Makefile.am occorre aggiungere:
ex1dir=$(datadir)
INCLUDES = $(GTKMM_CFLAGS) $(GLADEMM_CFLAGS) -DGLADEDIR=\""$(datadir)\""
e nel main.cc, includere il “config.h” ed inserire nel main
Glib::ustring glade_filename = Glib::ustring(GLADEDIR) + "/wmain.glade";
...
refXml = Gnome::Glade::Xml::create(glade_filename);
😉
sei un grande non c’è che dire….
non trovando documentazione a riguardo avevo optato per una soluzione grossolana.
cambiare il path dell’xml e poi per l’installazione avevo fatto uno scriptino in bash.
ciao, ho girato tutto il web per trovare la soluzione al mio problema, ma non c’è verso.
allora ho pensato che magari tu hai la soluzione.
il problema è la classe ComboBox
Glade ti permette di aggiungere gli items direttamente da interfaccia grafica.
nelle referenze di gtkmm ci sono varie classi relative al Combobox, ma non funziona nessuna. come è possibile?
avrei bisogno praticamente di un equivalente a “get_text()” per la combobox.
ma la cosa strana è che anche se provo get_active(), l’output è sempre nullo
non ho nemmeno un valore booleano. hai qualche idea?
Penso questo sia il link che stavi cercando
http://www.gtkmm.org/docs/gtkmm-2.4/docs/tutorial/html/chapter-combobox.html
c’è anche un esempio su come utilizzarlo 😉
già visto… avevo letto che la classe ComboBoxtext non può essere utilizzata con Glade in quanto Glade utilizza una Treemodel propria. Per questi scopi si usa la classe ComboBox
“Note that you can not use this class with Gnome::Glade::Xml::get_widget_derived() to wrap a GtkComboBox added in the Glade user interface designer, because Glade adds its own TreeModel instead of using the TreeModel from this class. You could use a normal Gtk::ComboBox instead, though you can not use Glade to add rows to a TreeModel that is defined in your C++ code.”
Il link che mi hai mandato l’avevo già visto e testato. in quell’esempio viene utilizzata la classe ComboBoxText e non va per le ragioni sopracitate. Se la cambio con Combobox mi dice che la funzione append_text() non è supportata da Combobox… insomma un delirio.
Devi utilizzare il metodo “get_value”, ti faccio un esempio partendo sempre dal mio codice. Definisci, nella classe wMain, un nuovo puntatore privato:
Gtk::ComboBox *myCombo;
quindi nel costruttore lo fa puntare alla combobox definita con glade:
refGlade->get_widget("myCombo", myCombo);
ora, in wMain::btnOk():
void wMain::btnOk() {
Glib::ustring txt;
Gtk::TreeModel::iterator iter = myCombo->get_active();
if (iter) {
Gtk::TreeModel::Row row = *iter;
Glib::ustring txtCombo;
row.get_value(0, txtCombo);
txt="Combo ["+txtCombo+"] ";
} else {
// Nessuna selezione
}
txt+="Testo ["+txtNome->get_text()+"]";
Gtk::MessageDialog dlg(*this, txt, false, Gtk::MESSAGE_INFO,
Gtk::BUTTONS_OK, true);
dlg.run();
}
😉
che dire… funziona. non mi funzionava perchè non dichiaravo il treemodel.
senza treemodel invece funziona il get_row_number() e attraverso uno switch
avevo risolto il problema ( ma con 50 righe di codice in piu 🙂 ) molto meglio come hai fatto tu.
un altra perplessità… a progetto finito e tutto funzionante ho creato un pacchetto da compilare in tar.gz e un .deb con checkinstall.
quando provo a installarlo su un altro computer il .deb non da problemi, mentre il pacchetto da compilare non funziona:
si ferma subito sul ./configure e crea un log con gli errori… a quanto pare gli errori sono dovuti alla mancanza di definizione di alcuni parametri come il nome del pacchetto, versione ecc… credo sia dovuto all’ aclocal.. succede anche a te?
Si, è normale, se devi compilare ti servono alcuni pacchetti, anche se non devi apportare modifiche al codice, vedi “Pacchetti da installare” nella parte finale della guida 😉
si ma quelli sono pacchetti develop.
possibile che chi vuole usare un semplice software sebba scaricare tutti quei pacchetti?
se installi i -dev di libglade e libgtkmm ti installa 30 mega di dipendenze ( gcc dev libpango, libcairo e tantissimi altri). questo scoraggia chi volesse utilizzare il software.
mentre se con il deb creato con checkinstall provato su una ubuntu nuova installata su macchina virtuale necessita solo di libglademm-2.4-1c2a
😦
E’ proprio per questo motivo che esistono i pacchetti deb, rpm, tgz, … 🙂
ahh ok, volevo solo una conferma. Temevo di aver sbagliato qualcosa.
Ciao nel main.cc ho dovuto sostituire la riga 10:
da
Glib::ustring glade_filename = Glib::ustring(GLADEDIR) + “/wmain.glade”;
A
Glib::ustring glade_filename = “wmain.glade”;
Altrimenti mi dava :
(ex1:28710): libglade-WARNING **: could not find glade file ‘/usr/local/share/wmain.glade’
Failed to load glade file `/usr/local/share/wmain.glade’
(Non sò perchè cerca li file…cmq vabè sarà poco pulito ma molto pratico)
Il mio problema e che dopo compilato se lo lancio mi restituisce il seguente errore
(ex1:12666): libglademm-CRITICAL **: Glade::Xml::get_cwidget(): glade_xml_get_widget() failed for widget name=wMain
Ti segnala l’errore perché non trova nel file .glade il riferimento al widget “wMain”… controlla se non hai messo un nome diverso 😉
Per il discorso del warning invece… se togli il riferimento a GLADEDIR, una volta installato il programma, non riuscirà a trovare il file .glade perché lo andrà a cercare nella cartella corrente e non in quella specificata in fase di installazione…
Se può essere utile posto anche la mia sull’argomento, dove descrivo come inglobare i files glade all’interno dell’eseguibile senza bisogno di distribuirli nel pacchetto
C++ / Glade files come object
Ciao