Dieser und noch weitere Artikel wurde von evilissimo erstellt.


Folgende Themen werden von diesem Artikel berührt:


Druckversion des Artikels


Grafische Benutzerschnittstellen in C++ mit GTKmm betriebssystemunabhängig gestalten Teil 3

In diesem Teil werde ich Gtk::Entry, Gtk::SpinButton, Gtk::Statusbar und Gtk::ProgressBar vorstellen. Zu den Widgets gibts meistens nicht viel zu erzählen und der Code erklärt mehr als langes Gerede.
Schaut euch die Beispiele an und ihr werdet verstehen, wie das ganze funktioniert (hoffe ich mal :p). So und nun geht es auch schon los.



1 Gtk::Entry

Das Gtk::Entry Widget kennen die meisten eher unter Edit. Hierbei handelt es sich um ein einfaches, einzeiliges Eingabefeld.


Ein Verwendungsbeispiel:
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
#include <gtkmm.h>
 
struct EntryTutorial : Gtk::Window
{
    EntryTutorial();
private:
    Gtk::Entry  m_entry;
    Gtk::Button m_button;
    Gtk::HBox   m_hbox;
 
    void on_entry_changed();
};
 
EntryTutorial::EntryTutorial()
: Gtk::Window(),
  m_entry(),
  m_button(Gtk::Stock::QUIT),
  m_hbox(true,10)
{
    // Gtk::Entry als Erstes
    m_hbox.pack_start(m_entry);
   
    // Button als Zweites
    m_hbox.pack_start(m_button);
   
    // HBox dem Fenster übergeben
    add(m_hbox);
 
    // Hier verbinden wir das clicked signal des Buttons mit der hide Methode
    // der Gtk::Window Klasse. Damit kann man dann die Anwendung beenden
    m_button.signal_clicked().connect(sigc::mem_fun(*this,&Gtk::Window::hide));
 
    // Das Signal wird nach jedem Zeichen aufgerufen, das entweder eingegeben oder gelöscht wurde!
    // Damit kann man super Eingaben überprüfen und evtl. bestimmte Zeichen blocken :)
    m_entry.signal_changed().connect(sigc::mem_fun(*this,&EntryTutorial::on_entry_changed));
 
    show_all_children();
}
 
void EntryTutorial::on_entry_changed()
{
    // Da ich hier ein Umlaut verwenden möchte und der String UTF-8 ist,
    // kodiere ich das 'ä' hier von Hand als 0xC3A4
    Glib::ustring msg = "Der Text des Entries hat sich ge\xC3\xA4ndert. Neuer Text: ";
       
    // Den Text, der im Entry eingegeben wurde, an unsere Nachricht anhängen
    msg += m_entry.get_text();
 
    Gtk::MessageDialog dia(*this,msg);
    dia.run();
}
 
int main(int argc, char **argv)
{
    Gtk::Main main(argc,argv);
    EntryTutorial window;
    main.run(window);
    return 0;
}






2 Gtk::SpinButton


Bei einem Gtk::SpinButton handelt es sich um ein Eingabefeld für numerische Werte mit einem Up/Down Widget (siehe Screenshot unterhalb des Codes ;)).

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
#include <gtkmm.h>
#include <boost/format.hpp>
 
 
struct SpinButtonTutorial : Gtk::Window
{
    SpinButtonTutorial();
private:
    Gtk::Adjustment m_spin_button_adjustment;
    Gtk::SpinButton m_spin_button;
    Gtk::Button     m_button;
    Gtk::HBox       m_hbox;
    int             m_previous_value;
    void on_spinbutton_value_changed();
};
 
SpinButtonTutorial::SpinButtonTutorial()
: Gtk::Window(),
  m_spin_button_adjustment(6,1,12,1,6,0),// value = 6, lower = 1, upper = 12, step_inc = 1, page_inc = 6, pagesize = 0
  m_spin_button(m_spin_button_adjustment),// SpinButton mit den Randbedingungen initialisieren
  m_button(Gtk::Stock::QUIT), // Stockbutton wie gehabt ;)
  m_hbox(false,10),
  m_previous_value(0)
{
    set_title("GTKmm Tutorial Teil 3");
 
    // Gtk::SpinButton als Erstes
    m_hbox.pack_start(m_spin_button,Gtk::PACK_SHRINK);
 
    m_previous_value = m_spin_button.get_value_as_int();
 
    // Button als Zweites
    m_hbox.pack_start(m_button);
 
    // 10 px Rand um die Horizontale Box
    m_hbox.set_border_width(10);
 
    // HBox dem Fenster übergeben
    add(m_hbox);
 
    // Hier verbinden wir das clicked signal des Buttons mit der hide Methode
    // der Gtk::Window Klasse. Damit kann man dann die Anwendung beenden
    m_button.signal_clicked().connect(sigc::mem_fun(*this,&Gtk::Window::hide));
 
    m_spin_button.signal_value_changed().connect(sigc::mem_fun(*this,&SpinButtonTutorial::on_spinbutton_value_changed));
 
    show_all_children();
}
 
void SpinButtonTutorial::on_spinbutton_value_changed()
{
    // Ich benutze hier Boost.Format, um meinen String zu erzeugen.
    // Da Glib::ustring einen Pperator std::string() hat und man somit
    // in der Lage ist, das Glib::ustring objekt wie ein std::string zu benutzen
    Glib::ustring msg = "Wert hat sich von %1% zu %2% ge\xC3\xA4ndert.";
   
    boost::format fmt(msg);
 
    // Den alten und den aktuellen Wert an das boost::format Objekt übergeben,
    // damit der String erzeugt werden kann
    fmt % m_previous_value % m_spin_button.get_value_as_int();
 
    // Nun speichere ich den neuen Wert, damit wir den das nächste Mal zur
    // Verfügung haben
    m_previous_value = m_spin_button.get_value_as_int();
   
    // Message Dialog wie gehabt ;)
    Gtk::MessageDialog dia(*this,fmt.str());
    dia.run();
}
 
 
int main(int argc, char **argv)
{
    Gtk::Main main(argc,argv);
    SpinButtonTutorial window;
    main.run(window);
    return 0;
}






3 Gtk::ProgressBar


Eine ProgressBar kann man vertikal oder horizontal verwenden. Ich habe beides in diesem Code untergebracht.

Des Weiteren gibt es zwei Möglichkeiten, eine ProgressBar zu benutzen. Entweder man benutzt sie, um den Fortschritt anzuzeigen, um damit die aktuelle Prozentzahl des Fortschritts anzuzeigen, oder man benutzt sie mit pulse(), um nur anzuzeigen, dass es aktiv ist.

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
#include <gtkmm.h>
#include <boost/format.hpp>
 
struct ProgressBarTutorial : Gtk::Window
{
    ProgressBarTutorial();
private:
   
    Gtk::VBox        m_vbox1;
    Gtk::VBox        m_vbox2;
    Gtk::HBox        m_hbox;
   
    Gtk::ProgressBar m_progressbar_h;
    Gtk::ProgressBar m_progressbar_v;
 
    Gtk::Button      m_button;
   
    bool on_activity_step();
    bool on_progress_step();
};
 
ProgressBarTutorial::ProgressBarTutorial()
: Gtk::Window(),
  m_vbox1(),
  m_vbox2(),
  m_hbox(),
  m_progressbar_h(),
  m_progressbar_v(),
  m_button(Gtk::Stock::QUIT)
{
    set_title("GTKmm Tutorial Teil 3");
 
    // Wir möchten eine vertikale ProgressBar
    m_progressbar_v.set_orientation(Gtk::PROGRESS_BOTTOM_TO_TOP);
 
    // Aktuellen Fortschritt der horizontalen ProgressBar auf 0 setzen
    m_progressbar_h.set_fraction(0);
   
    // Ab hier die übliche Anordnung der Widgets
    m_hbox.pack_start(m_progressbar_v,Gtk::PACK_SHRINK);
    m_hbox.set_spacing(10);
    m_vbox2.pack_start(m_progressbar_h,Gtk::PACK_EXPAND_PADDING);
 
    m_hbox.pack_start(m_vbox2);
 
    m_vbox1.pack_start(m_hbox);
    m_vbox1.pack_start(m_button,Gtk::PACK_SHRINK);
 
    m_vbox1.set_spacing(10);
    m_vbox1.set_border_width(10);
 
    add(m_vbox1);
 
    // Signale verbinden    
    // In diesem Falle verwenden wir hier eine Möglichkeit, um Threads zu vermeiden
    // Normalerweise würde man für dieses Beispiel wohl zwei Threads starten
    // Wir verwenden einfach eine andere Möglichkeit
    // Hierfür bietet die glib das Signal "timeout"
    // Das nimmt mehrere SignalHandler auf und löst diese dann nach n angegebenen Millisekunden aus
Glib::signal_timeout().connect(sigc::mem_fun(*this,&ProgressBarTutorial::on_activity_step),500);
    Glib::signal_timeout().connect(sigc::mem_fun(*this,&ProgressBarTutorial::on_progress_step),1000);    
   
    // Signal verbinden, um das Fenster zu schließen(=> Beenden)
    m_button.signal_clicked().connect(sigc::mem_fun(*this,&Gtk::Window::hide));
 
    // Alle Widgets anzeigen
    show_all_children();
}
 
bool ProgressBarTutorial::on_activity_step()
{
    // Nächsten Pulse-Schritt ausführen
    m_progressbar_v.pulse();
 
    // Wir geben true zurück, weil wir möchten, dass dieses timeout signal
    // wieder aufgerufen wird
    return true;
}
 
bool ProgressBarTutorial::on_progress_step()
{
    // Wir holen uns die aktuelle Prozentzahl und addieren 0.1 (als Schritt)
    // hinzu
    double cur = m_progressbar_h.get_fraction() + 0.1;
    // Prüfen, ob 1.0 oder mehr erreicht wurden (wenn ja: zurücksetzen auf 0)
    if(cur >= 1.0)
        cur = 0;
    // Neuen Fortschrittstand setzen
    m_progressbar_h.set_fraction(cur);
    // Prozentanzeige (Text im Hintergrund der ProgressBar) setzen
    boost::format fmt("%i %%");
    fmt % (cur * 100);
    m_progressbar_h.set_text(fmt.str());
   
    // Wir geben true zurück ,weil wir möchten, dass dieses timeout signal
    // wieder aufgerufen wird
    return true;
}
 
int main(int argc, char **argv)
{
    Gtk::Main main(argc,argv);
    ProgressBarTutorial window;
    main.run(window);
    return 0;
}






4 Gtk::StatusBar


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
#include <gtkmm.h>
#include <boost/format.hpp>
 
struct StatusBarTutorial : Gtk::Window
{
    StatusBarTutorial();
    ~StatusBarTutorial();
private:
    void on_button_push_clicked();
    void on_button_pop_clicked();
private:
    Gtk::VBox m_vbox;
    Gtk::HBox m_hbox;
    Gtk::Button m_button_push;
    Gtk::Button m_button_pop;
    Gtk::Statusbar m_statusbar;
    unsigned m_context_id;
};
 
StatusBarTutorial::StatusBarTutorial()
: m_vbox(false,12),
  m_button_push("Push"),
  m_button_pop("Pop"),
  m_statusbar(),
  m_context_id(0)
{
    m_hbox.pack_start(m_button_push);
 
    m_hbox.pack_start(m_button_pop);
 
    m_vbox.pack_start(m_hbox,Gtk::PACK_EXPAND_PADDING);
 
    // Ich würde, immer wenn ihr eine Statusbar verwendet,
    // diese in eine VBox packen und dann mit Gtk::PACK_SHRINK
    // in das VBox Objekt packen, damit die Statusbar auch wie eine
    // Statusbar aussieht ^^
    // Sonst ist es etwas sehr verschoben ;)
    m_vbox.pack_start(m_statusbar,Gtk::PACK_SHRINK);
   
    m_button_push.signal_clicked()
        .connect(sigc::mem_fun(*this,&StatusBarTutorial::on_button_push_clicked));
 
    m_button_pop.signal_clicked()
        .connect(sigc::mem_fun(*this,&StatusBarTutorial::on_button_pop_clicked));
 
    add(m_vbox);
 
    show_all_children();
}
 
StatusBarTutorial::~StatusBarTutorial()
{  
}
 
void StatusBarTutorial::on_button_push_clicked()
{  
    static unsigned count = 0;
    boost::format fmt("Text Nr %i");
    fmt % count++;
    // Text auf der statusbar stack setzen
    m_statusbar.push(fmt.str());
}
 
void StatusBarTutorial::on_button_pop_clicked()
{
    // Obersten Text von der Statusbar stack entfernen
    m_statusbar.pop();
}
 
int main(int argc, char ** argv)
{
    Gtk::Main main(argc,argv);
    StatusBarTutorial window;
    main.run(window);
    return EXIT_SUCCESS;
}










Dieses Mal war es extrem viel Code mit Kommentaren. Ich hoffe, dass es trotzdem verständlich genug war, denn ich denke, dass man mehr vom Praktischen lernt als vom theoretischen Gefasel ;)

Solltet ihr Fragen haben, einfach raus damit.

So, bis zum nächsten Teil :)

Vinzenz 'evilissimo' Feenstra


Weitere Teile:
Teil 1 des Tutorials
Teil 2 des Tutorials

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

Logo-Design: MastaMind Webdesign