Beispiel: Fensterinformationen aus einer Glade-Datei laden (gtkmm)



  • Moin allerseits,

    da hier ja bereits fleissig Beispiele fuer gtkmm geschrieben werden, will ich
    auch mal eins schreiben, da ich auch fuer mein Abschlussprojekt entsprechendes
    angewendet habe und euch das nicht vorenthalten will.

    Wie der Titel bereits erahnen laesst, kann man Fensterinformationen in einer
    sogenannten Glade-Datei speichern. Dies ist nichts weiter als eine XML-Datei,
    welche von dem Designer-Tool Glade erzeugt wird.

    Mit Glade kann ich also Fenster Designen und diese Abspeichern.

    Das schoene: Ich kann das Design jederzeit aendern, ohne die Anwendung neu
    uebersetzen zu muessen. Es werden keinerlei Informationen ueber das
    Fensterdesign im Quellcode codiert, sondern lediglich aus der Glade-Datei
    geladen.

    Zunaechst einmal muss man natuerlich ein Fenster mittels des Designer-Tools
    Glade erstellen und abspeichern.

    Ich erlaube mir einmal, die entsprechende Datei hier zu posten:

    example.glade:

    <?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
    <!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
    
    <glade-interface>
    
    <widget class="GtkWindow" id="MainWindow">
      <property name="width_request">340</property>
      <property name="visible">True</property>
      <property name="title" translatable="yes">Hallo Welt!</property>
      <property name="type">GTK_WINDOW_TOPLEVEL</property>
      <property name="window_position">GTK_WIN_POS_CENTER</property>
      <property name="modal">False</property>
      <property name="resizable">False</property>
      <property name="destroy_with_parent">False</property>
      <property name="decorated">True</property>
      <property name="skip_taskbar_hint">False</property>
      <property name="skip_pager_hint">False</property>
      <property name="type_hint">GDK_WINDOW_TYPE_HINT_DESKTOP</property>
      <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
    
      <child>
        <widget class="GtkVBox" id="vbox1">
          <property name="visible">True</property>
          <property name="homogeneous">False</property>
          <property name="spacing">0</property>
    
          <child>
    	<widget class="GtkLabel" id="halloWeltLabel">
    	  <property name="height_request">270</property>
    	  <property name="visible">True</property>
    	  <property name="label" translatable="yes">Hallo Welt!</property>
    	  <property name="use_underline">False</property>
    	  <property name="use_markup">False</property>
    	  <property name="justify">GTK_JUSTIFY_LEFT</property>
    	  <property name="wrap">False</property>
    	  <property name="selectable">False</property>
    	  <property name="xalign">0.5</property>
    	  <property name="yalign">0.5</property>
    	  <property name="xpad">0</property>
    	  <property name="ypad">0</property>
    	</widget>
    	<packing>
    	  <property name="padding">0</property>
    	  <property name="expand">False</property>
    	  <property name="fill">False</property>
    	</packing>
          </child>
    
          <child>
    	<widget class="GtkHButtonBox" id="hbuttonbox1">
    	  <property name="visible">True</property>
    	  <property name="layout_style">GTK_BUTTONBOX_DEFAULT_STYLE</property>
    	  <property name="spacing">0</property>
    
    	  <child>
    	    <widget class="GtkButton" id="cmdBeenden">
    	      <property name="visible">True</property>
    	      <property name="can_default">True</property>
    	      <property name="can_focus">True</property>
    	      <property name="label" translatable="yes">_Beenden</property>
    	      <property name="use_underline">True</property>
    	      <property name="relief">GTK_RELIEF_NORMAL</property>
    	      <property name="focus_on_click">True</property>
    	    </widget>
    	  </child>
    	</widget>
    	<packing>
    	  <property name="padding">0</property>
    	  <property name="expand">True</property>
    	  <property name="fill">True</property>
    	</packing>
          </child>
        </widget>
      </child>
    </widget>
    
    </glade-interface>
    

    Ihr koennt diese Datne in einer Datei abspeichern und mit Glade laden. Es
    handelt sich hierbei um ein Fenster (MainWindow), welches eine VBox mit zwei
    Elementen enthaelt. Bei dem ersten Element handelt es sich um ein Label mit
    der Aufschrift "Hallo Welt!" und bei dem zweiten Element handelt es sich um
    eine HButtonBox, also eine horizontale Button Box, welche einen Button enthaelt
    mit der Aufschrift "Beenden" enthaelt.

    Zunaechst einmal muessen wir auf das Fenster zugreifen koennen, wir definieren
    uns daher eine Klasse, welche von Gtk::Window erbt und diejenigen Elemente
    als Member enthaelt, mit denen wir interagieren wollen.

    Ich habe den Beispiel-Code bereits kommentiert, sodass ich mir eine extra
    Erklaerung an dieser Stelle spare, sie waere auch nichts weiter als eine
    Wiederholung der Kommentare.

    mainwindow.hpp:

    #ifndef mainwindowHPP
    #define mainwindowHPP
    #include <gtkmm.h>
    #include <libglademm.h>
    
    class MainWindow : public Gtk::Window {
        private:
            //zugriff auf glade-datei
            Glib::RefPtr<Gnome::Glade::Xml> &refXml;
    
            //der button zum beenden der anwendung
            Gtk::Button*    btnBeenden;
        public:
            /*
                wir wollen die fenster informationen aus einer glade-datei laden.
                mittels 'get_widget_derived' (s. main.cpp) kann auf eine abgeleitete
                klasse zugegriffen werden. zum instanziieren ruft 'get_widget_derived'
                einen CTor auf, welcher eben diese signatur haben muss. 'base' ist
                hierbei der c-typ (gtk+) und enthaelt bereits alle relervanten
                informationen ueber das fenster. 'refXml' ist hierbei wichtig, da
                wir damit die member der klasse aus der glade-datei herauslesen und
                den klassenmembern zuweisen koennen (siehe implementierung des ctor)
            */
            MainWindow(GtkWindow* base, Glib::RefPtr<Gnome::Glade::Xml> &refXml);
    
            //beendet die Anwendung
            virtual void cmdBeenden() {
                hide();
            }
    };
    
    #endif
    

    Natuerlich benoetigen wir auch noch die Implementierung des CTors.

    mainwindow.cpp:

    #include "mainwindow.hpp"
    using namespace Gtk;
    
    /*
        hier sieht man nun, warum wir 'refXml' benoetigen. ueber 'refXml' greifen
        wir nun auf die information fuer den beenden-button zu und lesen diese aus
        der glade-datei. wie einem fenster, wird auch fuer den button mittels 'get_widget'
        die information aus der glade-datei herausgelesen.
    */
    MainWindow::MainWindow(GtkWindow* base, Glib::RefPtr<Gnome::Glade::Xml> &refXml) 
        : Window(base), refXml(refXml) {
        //pointer auf die 'cmdBeenden'-instanz holen
        refXml->get_widget("cmdBeenden", btnBeenden);
        if(btnBeenden)
            //das clicked-signal mit der elementfunktion 'cmdBeenden' verbinden
            btnBeenden->signal_clicked().connect(mem_fun(*this, &MainWindow::cmdBeenden));
    }
    

    Letztlich benoetigen wir noch die main.cpp.

    main.cpp:

    #include <iostream>
    #include "mainwindow.hpp"
    using namespace std;
    using namespace Gtk;
    
    int main(int argc, char* argv[]) {
    	Main mainApplication(argc, argv);
            try {
                /*
                    gladedatei oeffnen, um fensterinformationen herauszulesen
                    der zweite parameter gibt an, dass wir lediglich das fenster
                    mit diesem namen instanziieren wollen. wuerden wir diesen
                    parameter weglassen, so wuerden saemtliche, in der datei
                    definierten, fenster instanziiert werden
                */
                Glib::RefPtr<Gnome::Glade::Xml> refXml = 
                    Gnome::Glade::Xml::create("example.glade", "MainWindow"); 
                MainWindow* mainWindow(0);
    
                /*
                    um auf eine instanz zugreifen zu koennen, nutzt man die element-
                    funktion 'get_widget'. Da wir jedoch eine von Gtk::Window
                    abgeleitete Klasse nutzen, muessen wir 'get_widget_derived'
                    aufrufen.
                */
                refXml->get_widget_derived("MainWindow", mainWindow);
    
                //wenn es eine entsprechende instanz gibt...
                if(mainWindow)
                    Main::run(*mainWindow); //...dann koennen wir das fenster nun anzeigen
                else {
                    /*
                        ...oder es konnte es aus irgendwelchen gruenden nicht
                        nicht dem zeiger 'mainWindow' zugewiesen werden?
                    */
                    cout<<"Hauptfenster konnte nicht geladen werden!"<<endl;
                    return 1;
                }
            /*
                falls beim parsen der glade-datei ein problem gibt, dann wird
                eine exception geschmissen, die wir abfangen sollten
            */
            } catch(Gnome::Glade::Xml::Error& xmlError) {
                cout<<xmlError.what()<<endl;
                cin.get();
                return 1;
            }
    	return 0;
    }
    

    So, ich hoffe das Prinzip ist klar geworden. Man kann hiermit sehr schoen die
    grafischen Elemente vom Code trennen und hat nicht die ganzen
    Design-Informationen im Code stehen, was diesen natuerlich auch unheimlich
    aufblaeht.

    Bleibt noch zu erwaehnen, dass ihr zu eurer Anwendung noch zusaetzlich die
    Library 'glademm-2.4' hinzulinken muesst.

    So, Fehler einfach korregieren :). Ich hoffe mal das meine Erklaerungen im Code
    ok sind, mit der Ausdrucksweise tu ich mir immer schwer ;).

    mfg
    v R


Anmelden zum Antworten