Dieser und noch weitere Artikel wurde von GPC erstellt.


Folgende Themen werden von diesem Artikel berührt:


Druckversion des Artikels


Grafische Benutzerschnittstellen in C++ mit gtkmm betriebssystemunabhängig gestalten Teil 5

In diesem Tutorial wird es um drei Komponenten gehen: Listen, Bäume und ComboBox-Felder. Auf den ersten Blick mögen sie nicht viel gemein haben, allerdings täuscht dieser hier, da sie alle auf der gleichen Technik basieren, die ich hier vorstellen möchte.

1 Das Gtk::TreeView

Das Gtk::TreeView wird zum Anzeigen von Listen und Baumstrukturen benutzt und ist eines der wichtigsten Widgets in einem Toolkit.

Ähnlich dem Verhältnis von Gtk::TextView und Gtk::TextBuffer gibt es auch hier mehrere Widgets, die involviert sind.

Eine gute Übersicht findet man unter [2].

Während das Gtk::TreeView nur die Darstellung von Informationen übernimmt, werden die Daten in einem Gtk::TreeModel abgelegt. Das Gtk::TreeModel-Widget stellt nur eine generische Schnittstelle dar und wird dann je nach Bedarf durch eine abgeleitete Klasse wie Gtk::ListStore oder Gtk::TreeStore ersetzt. Selbstverständlich kann man auch sein eigenes TreeModel erstellen, indem man von Gtk::TreeModel ableitet. In den allermeisten Fällen wird dies jedoch nicht nötig sein.

Außerdem wird ein Gtk::TreeView immer in ein Gtk::ScrolledWindow eingebettet, welches evilissimo in Teil 4 vorgestellt hat.

Grundsätzlich gilt es folgende Dinge zu tun, um das Gtk::TreeView zu benutzen:


1.1 Eine simple Liste

In diesem simplen Beispiel werden wir ein paar Daten in einer Liste darstellen:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <gtkmm.h>
 
class Application : public Gtk::Window {
    //Wir definieren hier unsere Spalten bzw. wie eine Zeile aussieht
    class Columns : public Gtk::TreeModel::ColumnRecord {
    public:
        Columns() {
            add(col_id);
            add(col_name);
            add(col_price);
            add(col_stock);
        }
   
        ~Columns() {}
   
        Gtk::TreeModelColumn<unsigned> col_id;
        Gtk::TreeModelColumn<Glib::ustring> col_name;
        Gtk::TreeModelColumn<float> col_price;
        Gtk::TreeModelColumn<bool> col_stock;
    };
 
    Gtk::VBox topBox;
 
    //Hier die drei wichtigsten Komponenten
    Gtk::ScrolledWindow scrollWindow;
    Gtk::TreeView treeView;
    Glib::RefPtr<Gtk::ListStore> listStore;
 
    Columns cols;
 
    public:
    Application() {
        set_title("ListStore Example");
        set_default_size(350,200);
 
        //Wir erstellen das ListStore und weisen es dem TreeView zu
        treeView.set_model(listStore = Gtk::ListStore::create(cols));
 
        //Wir fuegen noch ein paar Daten ein
        Gtk::TreeModel::Row row = *(listStore->append());
        row[cols.col_id] = 2485;
        row[cols.col_name] = "Staubsauger";
        row[cols.col_price] = 79.99f;
        row[cols.col_stock] = true;
 
        row = *(listStore->append());
        row[cols.col_id] = 2751;
        row[cols.col_name] = "Mixer";
        row[cols.col_price] = 25.75f;
        row[cols.col_stock] = true;
 
        row = *(listStore->append());
        row[cols.col_id] = 2982;
        row[cols.col_name] = "Waschmaschine";
        row[cols.col_price] = 699.99f;
        row[cols.col_stock] = false;
 
        //Nun muessen wir noch angeben, welche Spalten angezeigt werden sollen
        treeView.append_column("Produktnr.", cols.col_id);
        treeView.append_column("Name", cols.col_name);
 
        //Hier legen wir fest, wie die Spalte "Preis" formatiert werden soll
        treeView.append_column_numeric("Preis (EUR)", cols.col_price, "%.2f");
 
        //Die CheckBoxen werden automatisch gesetzt
        treeView.append_column("Am Lager", cols.col_stock);
 
        //Wir weisen das TreeView dem ScrollWindow zu
        scrollWindow.add(treeView);
        scrollWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
       
        topBox.pack_start(scrollWindow);
        add(topBox);
 
        show_all_children();
    }
    ~Application() { }
};
 
int main(int argc, char **argv) {
    Gtk::Main m(argc, argv);
 
    Application app;
    m.run(app);
 
    return EXIT_SUCCESS;
}


So sieht das dann aus:


Erwähnenswert ist, dass natürlich nur die Spalten angezeigt werden, die man per Gtk::TreeView::append_column-Methode hinzugefügt hat. Auf diese Art und Weise kann man leicht Daten "verstecken", indem man im ColumnModel weitere Eigenschaften deklariert, diese Spalten aber nicht hinzufügt. Später kann man die Spalte bei Bedarf mit einem Aufruf von Gtk::TreeView::append_column zur Laufzeit hinzufügen und mit Gtk::TreeView::remove_column auch wieder entfernen, wenn sie doch überflüssig wird.

1.2 Eine Baumstruktur

Nun ist so eine Liste mit Produkten ja etwas schönes, aber noch schöner wäre es, wenn wir unsere Produkte in Produktkategorien ablegen könnten. Hierzu werden wir nun eine Baumstruktur erstellen (der vorherige Quellcode ist Grundlage hierfür):
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include <gtkmm.h>
 
class Application : public Gtk::Window {
    //Wir definieren hier unsere Spalten bzw. wie eine Zeile aussieht
    class Columns : public Gtk::TreeModel::ColumnRecord {
    public:
        Columns() {
            add(col_id);
            add(col_name);
            add(col_price);
            add(col_stock);
        }
   
        ~Columns() {}
   
        Gtk::TreeModelColumn<unsigned> col_id;
        Gtk::TreeModelColumn<Glib::ustring> col_name;
        Gtk::TreeModelColumn<float> col_price;
        Gtk::TreeModelColumn<bool> col_stock;
    };
 
    Gtk::VBox topBox;
    //Hier die drei wichtigsten Komponenten
    Gtk::ScrolledWindow scrollWindow;
    Gtk::TreeView treeView;
 
    //Nun brauchen wir ein TreeStore anstelle eines ListStores
    Glib::RefPtr<Gtk::TreeStore> treeStore;
 
    Columns cols;
 
    public:
    Application() {
        set_title("TreeStore Example");
        set_default_size(350,250);
 
        //Wir erstellen das TreeStore und weisen es dem TreeView zu
        treeView.set_model(treeStore = Gtk::TreeStore::create(cols));
 
        //Ein Knoten auf der ersten Ebene
        Gtk::TreeModel::Row row = *(treeStore->append());
        row[cols.col_id] = 2000;
        row[cols.col_name] = "Elektrogeräte";
        row[cols.col_price] = 0.0f;
        row[cols.col_stock] = false;
 
        //Drei Subknoten
        Gtk::TreeModel::Row childrow = *(treeStore->append(row.children()));
        childrow[cols.col_id] = 2485;
        childrow[cols.col_name] = "Staubsauger";
        childrow[cols.col_price] = 79.99f;
        childrow[cols.col_stock] = true;
       
        childrow = *(treeStore->append(row.children()));
        childrow[cols.col_id] = 2751;
        childrow[cols.col_name] = "Mixer";
        childrow[cols.col_price] = 25.75f;
        childrow[cols.col_stock] = true;
 
        childrow = *(treeStore->append(row.children()));
        childrow[cols.col_id] = 2982;
        childrow[cols.col_name] = "Waschmaschine";
        childrow[cols.col_price] = 699.99f;
        childrow[cols.col_stock] = false;
 
        //Wieder ein Knoten auf Ebene 1
        row = *(treeStore->append());
        row[cols.col_id] = 3000;
        row[cols.col_name] = "Reinigungsmittel";
        row[cols.col_price] = 0.0f;
        row[cols.col_stock] = false;
 
        //Und drei Subknoten
        childrow = *(treeStore->append(row.children()));
        childrow[cols.col_id] = 3149;
        childrow[cols.col_name] = "Brof";
        childrow[cols.col_price] = 5.99f;
        childrow[cols.col_stock] = false;
       
        childrow = *(treeStore->append(row.children()));
        childrow[cols.col_id] = 3587;
        childrow[cols.col_name] = "Kill it!";
        childrow[cols.col_price] = 8.99f;
        childrow[cols.col_stock] = true;
 
        childrow = *(treeStore->append(row.children()));
        childrow[cols.col_id] = 2982;
        childrow[cols.col_name] = "Uber-Cleaner";
        childrow[cols.col_price] = 15.25f;
        childrow[cols.col_stock] = true;
 
        //Nun moechten wir noch, dass man bestimmte Spalten editieren kann
        //In dem Fall bleibt nur die Produktnummer unveraenderlich, alles
        //andere kann veraendert werden    
        treeView.append_column("Produktnr.", cols.col_id);
        treeView.append_column_editable("Name", cols.col_name);
 
        //Hier legen wir fest, wie die Spalte "Preis" formatiert werden soll
        treeView.append_column_numeric_editable("Preis (€)", cols.col_price,
                                                "%.2f");
 
        //Die CheckBoxen werden automatisch gesetzt
        treeView.append_column_editable("Am Lager", cols.col_stock);
 
        //Wir weisen das TreeView dem ScrollWindow zu
        scrollWindow.add(treeView);
        scrollWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
       
        topBox.pack_start(scrollWindow);
        add(topBox);
 
        show_all_children();
    }
    ~Application() { }
};
 
int main(int argc, char **argv) {
    Gtk::Main m(argc, argv);
 
    Application app;
    m.run(app);
 
    return EXIT_SUCCESS;
}


Sieht doch schon ganz gut aus:


Hier ein Bild, bei dem ein Zweig geschlossen ist:


1.3 Auswahl und Zugriff auf Datensätze

In der Regel wird man mit den Daten, die ein Gtk::TreeView beinhaltet, etwas anstellen müssen. Meistens wird der User eine oder mehrere Zeilen auswählen und auf diese soll nun eine Aktion ausgeführt werden.

Generell gilt, dass auch das Gtk::TreeView mit Iteratoren arbeitet, um auf die Daten zuzugreifen. Hier heißt der Iterator Gtk::TreeModel::Iterator (dies ist ein typedef, im Original einfach Gtk::TreeIter).

Nun, um herauszufinden, was gerade ausgewählt ist, erstellt man zuerst ein Gtk::TreeView::Selection-Objekt:
C++:
Glib::RefPtr<Gtk::TreeView::Selection> selection = treeView.get_selection();


Mit diesem Objekt können wir auch per set_mode(SelectionMode m) festlegen, ob Einfach- oder Mehrfachselektion verfügbar sein soll (Einfachauswahl ist die Voreinstellung). Abgesehen davon verwaltet dieses Objekt alles, was irgendwie mit der Auswahl zusammenhängt. So kann man per select(Gtk::TreeModel::Iterator) auch eine Auswahl setzen.

Nun, abhängig von Einfach- bzw. Mehrfachauswahl entscheiden wir über das weitere Vorgehen. Bei Einfachauswahl hat man es recht einfach:
C++:
Gtk::TreeModel::iterator iter = selection->get_selected();
if(iter) {  //wurde eine Auswahl getroffen?
    Gtk::TreeModel::Row row = *iter;
   
    //Wir sind im Konkurrenzkampf, also wird der Preis halbiert
    row[cols.col_price] = row[cols.col_price] / 2;
}


Hat man jedoch Mehrfachauswahl eingestellt, wird die Geschichte ein wenig komplizierter, da man eine callback-Funktion erstellen muss:
C++:
1
2
3
4
5
6
7
8
selection->selected_foreach_iter( sigc::mem_fun(*this, &Application::handle_multiple_selection) );
 
void Application::handle_multiple_selection(const Gtk::TreeModel::iterator &iter) {
    Gtk::TreeModel::Row row = *iter;
   
    //Wir sind immer noch im Konkurrenzkampf, also wird der Preis ueber die ganze Selektion halbiert ;-)
    row[cols.col_price] = row[cols.col_price] / 2;
}


Dieser Code bewirkt, dass die Methode handle_multiple_selection für jedes Element der Auswahl aufgerufen und abgearbeitet wird.

Im Prinzip haben wir nun durch die "Preisveränderungen" auch schon gesehen, wie man auf die Daten zugreift. Der Weg ist immer der gleiche:


1.4 Gtk::Cellrenderer

Bisher haben wir in unseren Listen/Bäumen immer nur Text, Zahlen oder auch mal eine CheckBox angezeigt. Nun bietet gtkmm über die Klasse Gtk::CellRenderer (und den Derivaten) diverse Möglichkeiten, Informationen darzustellen. Seien es nun Bilder, CheckBoxen, SpinButtons oder Fortschrittsanzeigen. Eine Übersicht findet man unter [3].

Ich möchte hier am Beispiel einer Liste zeigen, wie das geht.
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <gtkmm.h>
 
class Application : public Gtk::Window {
    //Wir definieren hier unsere Spalten bzw. wie eine Zeile aussieht
    class Columns : public Gtk::TreeModel::ColumnRecord {
    public:
        Columns() {
            add(col_id);
            add(col_colour);
        }
   
        ~Columns() {}
   
        Gtk::TreeModelColumn<unsigned> col_id;
        Gtk::TreeModelColumn<Glib::ustring> col_colour;
    };
 
    Gtk::VBox topBox;
 
    //Hier die drei wichtigsten Komponenten
    Gtk::ScrolledWindow scrollWindow;
    Gtk::TreeView treeView;
    Glib::RefPtr<Gtk::ListStore> listStore;
 
    Columns cols;
 
    public:
        Application() {
        set_title("ListStore Example");
        set_default_size(200,200);
 
        //Wir erstellen das ListStore und weisen es dem TreeView zu
        treeView.set_model(listStore = Gtk::ListStore::create(cols));
 
        //Wir fuegen noch ein paar Daten ein
        Gtk::TreeModel::Row row = *(listStore->append());
        row[cols.col_id] = 0;
        row[cols.col_colour] = "red";
 
        row = *(listStore->append());
        row[cols.col_id] = 1;
        row[cols.col_colour] = "blue";
 
        row = *(listStore->append());
        row[cols.col_id] = 2;
        row[cols.col_colour] = "green";
       
        row = *(listStore->append());
        row[cols.col_id] = 3;
        row[cols.col_colour] = "white";
       
        treeView.append_column("Id", cols.col_id);
 
        //Die Spalte Colour soll farbig dargestellt werden
        //Zuerst legen wir einen entsprechenden CellRenderer an
        Gtk::CellRendererText *cell = new Gtk::CellRendererText;
 
        //Wir fuegen die Spalte hinzu
        int column_count = treeView.append_column("Colour", *cell);
 
        //Dann holen wir uns die gerade hinzugefuegte Spalte
        Gtk::TreeViewColumn *column = treeView.get_column(column_count-1);
 
        //Und sagen dann, woher die Informationen kommen und
        //wie sie dargestellt werden sollen
        if (column) {
#ifndef GLIBMM_PROPERTIES_ENABLED
            column->add_attribute(cell->property_text(), cols.col_colour);
            column->add_attribute(cell->property_background(), cols.col_colour);
#else
            column->add_attribute(*cell, "text", cols.col_colour);
            column->add_attribute(*cell, "background", cols.col_colour);
#endif
        }
       
        scrollWindow.add(treeView);
        scrollWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
       
        topBox.pack_start(scrollWindow);
        add(topBox);
 
        show_all_children();
    }
    ~Application() { }
};
 
int main(int argc, char **argv) {
    Gtk::Main m(argc, argv);
 
    Application app;
    m.run(app);
 
    return EXIT_SUCCESS;
}


Ergebnis:


An sich hat sich unser Programm nicht groß verändert, nur das Hinzufügen der letzten Spalte ist neu. Natürlich hat jeder der verschiedenen Gtk::CellRenderer sein eigenes Property, welches man setzen muss, damit die Daten angezeigt werden. Jedoch greift einem die Dokumentation hier unter die Arme und sagt, welches Property gesetzt werden muss.

2 Die "Gtk::ComboBoxen"

gtkmm bietet vier ComboBoxen an: Gtk::ComboBox, Gtk::ComboBoxText, Gtk::ComboBoxEntry und Gtk::ComboBoxEntryText, wobei Gtk::ComboBox die Basisklasse und die anderen drei ComboBoxen Spezialisierungen darstellen. Für Gtk::ComboBox und Gtk::ComboBoxEntry muss man zuerst ein Gtk::TreeModel erstellen, bevor man Einträge hinzufügen kann (hier wird auch die Gemeinsamkeit mit Gtk::TreeView deutlich, auf die ich bereits einführend hingewiesen habe). Bei den anderen beiden hingegen benutzt man append_text um Einträge einzufügen.

Nun, schauen wir uns die einzelnen Komponenten an.

2.1 Gtk::ComboBox und Gtk::ComboBoxText

Die Vorgehensweise bei einer Gtk::ComboBox ist fast gleich wie bei einem Gtk::TreeView:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <iostream>
#include <gtkmm.h>
 
class Application : public Gtk::Window {
    //Wir definieren hier unsere Spalten bzw. wie eine Zeile aussieht
    class Columns : public Gtk::TreeModel::ColumnRecord {
    public:
        Columns() {
            add(col_id);
            add(col_name);
            add(col_market_share);
        }
   
        ~Columns() {}
   
        Gtk::TreeModelColumn<unsigned> col_id;
        Gtk::TreeModelColumn<Glib::ustring> col_name;
        Gtk::TreeModelColumn<int> col_market_share;
    };
 
    Gtk::VBox topBox;
 
    Gtk::ComboBox comboBox;
    Glib::RefPtr<Gtk::TreeStore> treeStore;
 
    Columns cols;
 
    protected:
    //Signalhandler der reagiert, sobald sich die Auswahl aendert
    virtual void on_combo_changed() {
        Gtk::TreeModel::iterator iter = comboBox.get_active();
        if (iter) {
            Gtk::TreeModel::Row row = *iter;
            if (row) {
                std::cout   <<"ID: "<<row[cols.col_id]<<'\n'
                            <<"Name: "<<row[cols.col_name]<<'\n'
                            <<"Marktanteil: "<<row[cols.col_market_share]<<std::endl;
            }
        }
    }
 
    public:
    Application() {
        set_title("Gtk::ComboBox Example");
        set_default_size(300,25);
 
        //Wir erstellen das TreeStore und weisen es dem TreeView zu
        comboBox.set_model(treeStore = Gtk::TreeStore::create(cols));
 
        //Wir fuegen noch ein paar Daten ein
        //Ein Knoten auf der ersten Ebene
        Gtk::TreeModel::Row row = *(treeStore->append());
        row[cols.col_id] = 2000;
        row[cols.col_name] = "Elektrogeräte";
        row[cols.col_market_share] = 23;
 
        //Drei Subknoten
        Gtk::TreeModel::Row childrow = *(treeStore->append(row.children()));
        childrow[cols.col_id] = 2485;
        childrow[cols.col_name] = "Staubsauger";
        childrow[cols.col_market_share] = 40;
       
        childrow = *(treeStore->append(row.children()));
        childrow[cols.col_id] = 2751;
        childrow[cols.col_name] = "Mixer";
        childrow[cols.col_market_share] = 10;
 
        childrow = *(treeStore->append(row.children()));
        childrow[cols.col_id] = 2982;
        childrow[cols.col_name] = "Waschmaschine";
        childrow[cols.col_market_share] = 20;
 
        //Wieder ein Knoten auf Ebene 1
        row = *(treeStore->append());
        row[cols.col_id] = 3000;
        row[cols.col_name] = "Reinigungsmittel";
        row[cols.col_market_share] = 32;
 
        //Und drei Subknoten
        childrow = *(treeStore->append(row.children()));
        childrow[cols.col_id] = 3149;
        childrow[cols.col_name] = "Brof";
        childrow[cols.col_market_share] = 12;
   
        childrow = *(treeStore->append(row.children()));
        childrow[cols.col_id] = 3587;
        childrow[cols.col_name] = "Kill it!";
        childrow[cols.col_market_share] = 19;
 
        childrow = *(treeStore->append(row.children()));
        childrow[cols.col_id] = 2982;
        childrow[cols.col_name] = "Uber-Cleaner";
        childrow[cols.col_market_share] = 67;
 
        //Wir fuegen die Spalten hinzu
        comboBox.pack_start(cols.col_name);
 
        //Die Spalte Marktanteil soll als Fortschrittsbalken dargestellt werden
        //Zuerst legen wir einen entsprechenden CellRenderer an
        Gtk::CellRendererProgress *cell = new Gtk::CellRendererProgress;
 
        //Wir fuegen den CellRenderer bzw. die Spalte hinzu
        comboBox.pack_start(*cell);
 
        //Und sagen dann, woher die Informationen kommen, die dargestellt werden
        if (cell) {
#ifndef GLIBMM_PROPERTIES_ENABLED
            comboBox.add_attribute(cell->property_value(), cols.col_market_share);
#else
            comboBox.add_attribute(*cell, "value", cols.col_market_share);
#endif
        }
 
        //Erstes Element soll vorselektiert sein
        comboBox.set_active(0);
       
        //Verknuepfung des Signalhandlers
        comboBox.signal_changed().connect(sigc::mem_fun(*this, &Application::on_combo_changed));
 
        //Hinzufuegen der Komponenten
        topBox.pack_start(comboBox);
        add(topBox);
 
        show_all_children();
    }
    ~Application() { }
};
 
int main(int argc, char **argv) {
    Gtk::Main m(argc, argv);
 
    Application app;
    m.run(app);
 
    return EXIT_SUCCESS;
}




Nun ja, alles in allem sieht das jetzt so alleine auf weiter Flur nicht besonders schön aus, aber es zeigt, was alles mit Gtk::ComboBox möglich ist. Gleichzeitig zeigt es aber auch, dass dieses Widget in den allermeisten Fällen totaler Overkill ist, da man häufig nur eine einzige Spalte mit Text für die Einträge braucht. Hier kommt Gtk::ComboBoxText ins Spiel:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <gtkmm.h>
 
class Application : public Gtk::Window {
    Gtk::VBox topBox;
    Gtk::ComboBoxText comboBox;
 
    protected:
    //Signalhandler der reagiert, sobald sich die Auswahl aendert
    virtual void on_combo_changed() {
        std::cout<<"Auswahl: "<<comboBox.get_active_text()<<std::endl;
    }
 
    public:
    Application() {
        set_title("Gtk::ComboBoxText Example");
        set_default_size(220,25);
 
        //Wir fuegen noch ein paar Daten ein
        comboBox.append_text("Led Zeppelin");
        comboBox.append_text("Deep Purple");
        comboBox.append_text("The Doors");
 
        //Erstes Element soll vorselektiert sein
        comboBox.set_active(0);
       
        //Verknuepfung des Signalhandlers
        comboBox.signal_changed().connect(sigc::mem_fun(*this, &Application::on_combo_changed));
 
        //Hinzufuegen der Komponenten
        topBox.pack_start(comboBox);
        add(topBox);
 
        show_all_children();
    }
    ~Application() { }
};
 
int main(int argc, char **argv) {
    Gtk::Main m(argc, argv);
 
    Application app;
    m.run(app);
 
    return EXIT_SUCCESS;
}




Wie man sieht, hat sich die ganze Geschichte dadurch erheblich vereinfacht. :)

2.2 Gtk::ComboBoxEntry und Gtk::ComboBoxEntryText

Die in diesem Abschnitt behandelten ComboBoxen unterscheiden sich wie gesagt nur in einem Punkt von den anderen beiden: Sie ermöglichen die Eingabe von Text via Textfeld, um so auch andere Werte als die vorgegebenen anzugeben.

Zuerst ein Beispiel mit dem Gtk::ComboBoxEntry-Widget:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <iostream>
#include <gtkmm.h>
 
class Application : public Gtk::Window {
    //Wir definieren hier unsere Spalten bzw. wie eine Zeile aussieht
    class Columns : public Gtk::TreeModel::ColumnRecord {
    public:
        Columns() {
            add(col_id);
            add(col_name);
            add(col_price);
            add(col_stock);
        }
   
        ~Columns() {}
   
        Gtk::TreeModelColumn<unsigned> col_id;
        Gtk::TreeModelColumn<Glib::ustring> col_name;
        Gtk::TreeModelColumn<float> col_price;
        Gtk::TreeModelColumn<bool> col_stock;
    };
 
    Gtk::VBox topBox;
 
    //Hier die drei wichtigsten Komponenten
    Gtk::ScrolledWindow scrollWindow;
    Gtk::ComboBoxEntry comboBox;
    Glib::RefPtr<Gtk::ListStore> listStore;
 
    Columns cols;
 
    protected:
    //Signalhandler der reagiert, sobald sich die Auswahl aendert
    void on_combo_changed() {
        std::cout<<"Auswahl: "<<(comboBox.get_entry())->get_text()<<std::endl;
    }
 
    public:
    Application() {
        set_title("ComboBoxEntry Example");
        set_default_size(350,25);
 
        //Wir erstellen das ListStore und weisen es dem TreeView zu
        comboBox.set_model(listStore = Gtk::ListStore::create(cols));
 
        //Wir fuegen noch ein paar Daten ein
        Gtk::TreeModel::Row row = *(listStore->append());
        row[cols.col_id] = 2485;
        row[cols.col_name] = "Staubsauger";
        row[cols.col_price] = 79.99f;
        row[cols.col_stock] = true;
 
        row = *(listStore->append());
        row[cols.col_id] = 2751;
        row[cols.col_name] = "Mixer";
        row[cols.col_price] = 25.75f;
        row[cols.col_stock] = true;
 
        row = *(listStore->append());
        row[cols.col_id] = 2982;
        row[cols.col_name] = "Waschmaschine";
        row[cols.col_price] = 699.99f;
        row[cols.col_stock] = false;
 
        //Nun muessen wir noch angeben, welche Spalten angezeigt werden sollen
        comboBox.pack_start(cols.col_id);
        comboBox.pack_start(cols.col_price);
        comboBox.pack_start(cols.col_stock);
 
        //Auf welche Spalte sich die Eingabe beziehen soll...
        comboBox.set_text_column(cols.col_name);
 
        comboBox.signal_changed().connect(sigc::mem_fun(*this, &Application::on_combo_changed));
 
        //Wir weisen die ComboBox dem ScrollWindow zu
        topBox.pack_start(comboBox);
        add(topBox);
 
        show_all_children();
    }
    ~Application() { }
};
 
int main(int argc, char **argv) {
    Gtk::Main m(argc, argv);
 
    Application app;
    m.run(app);
 
    return EXIT_SUCCESS;
}




Das einzige, was man bei Gtk::ComboBoxEntry beachten muss, ist dass man per m_combo.set_text_column(Columns::col_name); angeben muss, welche Spalte den Text beinhaltet. Diese Spalte wird bei diesem Aufruf auch gleich zum Gtk::ComboBoxEntry-Widget hinzugefügt. Das heißt, man sollte nicht auch noch pack_start auf diese Spalte anwenden, weil sie sonst zweimal vorkommt.

Abschließend wollen wir uns noch das Gtk::ComboBoxEntryText-Widget anschauen:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <gtkmm.h>
 
class Application : public Gtk::Window {
    Gtk::VBox topBox;
    Gtk::ComboBoxEntryText comboBox;
 
    protected:
    //Signalhandler der reagiert, sobald sich die Auswahl aendert
    virtual void on_combo_changed() {
        std::cout<<"Auswahl: "<<(comboBox.get_entry())->get_text()<<std::endl;
    }
 
    public:
    Application() {
        set_title("Gtk::ComboBoxEntryText Example");
        set_default_size(220,25);
 
        //Wir fuegen noch ein paar Daten ein
        comboBox.append_text("Led Zeppelin");
        comboBox.append_text("Deep Purple");
        comboBox.append_text("The Doors");
 
        //Verknuepfung des Signalhandlers
        comboBox.signal_changed().connect(sigc::mem_fun(*this, &Application::on_combo_changed));
 
        //Hinzufuegen der Komponenten
        topBox.pack_start(comboBox);
        add(topBox);
 
        show_all_children();
    }
    ~Application() { }
};
 
int main(int argc, char **argv) {
    Gtk::Main m(argc, argv);
 
    Application app;
    m.run(app);
 
    return EXIT_SUCCESS;
}




3 Referenzen

[1] Tutorial zu Gtk::TreeView
[2] Gtk::TreeView Übersicht
[3] Gtk::CellRenderer Hierarchie
[4] Tutorial zu den Gtk::ComboBoxen

Sie können Kommentare zu diesem Artikel im Forum schreiben. (Eine Registrierung ist nicht notwendig.)

Logo-Design: MastaMind Webdesign