Glade3 e Gtk… un primo programmino in C++


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

41 pensieri su “Glade3 e Gtk… un primo programmino in C++

  1. alexwizard ha detto:

    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.

  2. ilpaso ha detto:

    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

  3. ilpaso ha detto:

    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

  4. giuseppe ha detto:

    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?

  5. Marco ha detto:

    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

  6. Marco ha detto:

    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.

  7. 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” 😉

  8. teo_icKs ha detto:

    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:-)

  9. max ha detto:

    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?

  10. 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());
    }

    😉

  11. max ha detto:

    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

  12. 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 😉

  13. max ha detto:

    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.

  14. max ha detto:

    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 ?

  15. 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);

    😉

  16. max ha detto:

    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.

  17. max ha detto:

    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?

  18. max ha detto:

    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.

  19. 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();
    }

    😉

  20. max ha detto:

    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?

  21. 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 😉

  22. max ha detto:

    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
    😦

  23. gianpix ha detto:

    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

  24. 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…

Scrivi una risposta a max Cancella risposta