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 ). 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.
// 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();
// 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();
// 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();
// 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));
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();
}
// 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));
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();
}
// 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));
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();
}
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.
// 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);
// 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));
// 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;
}
// 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);
// 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));
// 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;
}
// 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);
// 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));
// 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;
}
// 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);
// 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);
// 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);
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
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
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?
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.
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 ?
Nächstes Thema anzeigen Vorheriges Thema anzeigen
Sie können keine Beiträge in dieses Forum schreiben. Sie können auf Beiträge in diesem Forum antworten. Sie können Ihre Beiträge in diesem Forum nicht bearbeiten. Sie können Ihre Beiträge in diesem Forum nicht löschen. Sie können an Umfragen in diesem Forum nicht mitmachen.
c++.de ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums
für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de
Werbekostenerstattung verdient werden kann.
Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info, www.c-sar.de, www.c-plusplus.net und www.baeckmann.de
enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt
(vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in
privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden.
Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere
übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.