GTKmm Tutorial Teil 3
-
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:
#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 ;)).
#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.
#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
#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
-
Hi, hat wieder Spaß gemacht deinen Code zu lesen
Ein Frage:
Wieso konvertierst du nicht die String die Umlaute haben in UTF-8 Strings, anstatt diese komischen Zeichen hinzuschreiben ???
Hat das irgendwelche Nachteile ?
Glib::ustring msg = Glib::locale_to_utf8("Der Text des Entries hat sich geändert. Neuer Text: ");
-
Man sagte mir in der GTKmm Mailinglist das man keine string literale nehmen soll.
Ich hatte nämlich diverse crashes mit Glib::locale_to_utf8 also hab ich mich einfach dazu entschlossen die zu konvertieren und die Strings einfach so zu verwenden, da diese auf diese weise bereits UTF-8 sind
BR
Vinzenz
-
Achso.
Und wie kommst du auf diese Codes, stehen die Irgendwo ??
-
ja z.b. hier http://www.utf8-chartable.de/
BR
Vinzenz
-
Danke
-
Mal ne dumme Frage, warum verwendest du immer eine eigene Struktur für dein Window? Nötig wäre das ja nicht, oder? OK, GTKmm legt es wohl auf OOP an, aber prozedural könnte ich das doch auch machen, oder?
Und verstehe ich das richtig, die mit signal_changed() verbundene Funktion wird erst aufgerufen, nachdem Enter gedrückt wurde?
-
Hi,
ja du könntest es rein theoretisch auch prozedual machen. Erstens finde ich es so besser wie es ist und zweitens ist das auch der sogesehen zu bevorzugende Weg mit GTKmm zu arbeiten.
Nicht nur GTKmm auch GTK+ setzt auf Objektorientierung. Bei GTK+ eben nur mit den Mitteln die in C auch zur Verfügung stehen.Ich weiß jetzt nicht von welchem Beispiel du redest, aber ich gehe davon aus das du vom Entry-Widget Beispiel sprichst. Nein, das Signal kommt nach jedem eingegeben Zeichen. Sprich du gibst das Zeichen 'a' ein und das Signal wird ausgelöst, du gibst '5' ein und das Signal wird wieder ausgelöst, du löscht '5' und das Signal wird wiederum ausgelöst, etc. pp.
BR
Vinzenz
-
Danke, dann wäre ja alles klar!
-
Hi evilissimo, ich verfolge deine Artikel mit großem Interesse, deshalb kann ich mir auch eine Frage nicht verkneifen: Ist ein 4. Teil geplant / in Arbeit ?
-
Ja, weitere Teile sind in Arbeit, aber noch nicht fertiggestellt.
Grüße
GPC
-
Wie siehts aus bei dir? Kommst du weiter?
-
hil evilissimo,
super Tutorial
Alle drei Teile bieten einen guten Einstieg in die Materie.
Hier ist noch ein Link auf eine eclipse Integration.
http://kapo-cpp.blogspot.com/2007/02/gtkmm-and-eclipse.html