GTKmm Tutorial Teil 4



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

    1 Gtk::ScrolledWindow

    Ein Gtk::ScrolledWindow ist ein Container-Widget, welches Scrollbalken für diverse Widgets anbietet. Meistens verwendet man dass Gtk::ScrolledWindow für Gtk::TreeView (bespreche ich in einem anderem Teil) und das Gtk::TextView, welches dieses mal das Hauptthema ist.

    Es kommt zwar selten vor, aber manchmal findet das Gtk::ScrolledWindow Verwendung für die Gtk::DrawingArea, welche zum Zeichnen verwendet wird. Ich werde auf die Gtk::DrawingArea in meinem Tutorial warscheinlich überhaupt nicht weiter zu sprechen kommen. Sollte jemand Interesse an diesem Widget haben, da er selbst etwas zeichnen möchte, empfehle ich diesem die gtkmm Dokumentationsseiten [1]

    Die wichtigsten Methoden die man von Gtk::ScrolledWindow kennen sollte sind die Methode add(), um ein Widget hinzuzufügen und set_policy(). set_policy() verwendet man um anzugeben wie sich die Scrollbalken verhalten sollen. Ob sie immer sichtbar sein sollen (Gtk::POLICY_ALWAYS), ob sie niemals sichtbar sein sollen (Gtk::POLICY_NEVER) oder ob sie automatisch angezeigt werden sollen wenn das Widget zu groß wird (Gtk::POLICY_AUTOMATIC).

    Man kann das Verhalten für den horizontalen und den vertikalen Scrollbalken festlegen.

    Viel mehr muss man meines Erachtens gar nicht über das Gtk::ScrolledWindow wissen. Deswegen höre ich hier mit dem Thema auf und komme gleich zum Nächsten.

    2 Gtk::TextView

    Das Gtk::TextView bietet vielseitige Möglichkeiten um Text darstellen zu können, es ist aber auch möglich Bilder oder andere Widgets einzufügen. Aufgrund seiner Vielfältigkeit werde ich nur ein paar grundlegende Features erklären.

    Grundsätzlich braucht man für ein Gtk::TextView drei Dinge: Ein Gtk::ScrolledWindow, ein Gtk::TextBuffer und das Gtk::TextView selbst.

    2.1 Simples TextView Beispiel

    #include <gtkmm.h>
    
    struct TextViewTutorial : public Gtk::Window
    {
        TextViewTutorial();
        ~TextViewTutorial();
    
    private:
        Gtk::ScrolledWindow           m_scrolled_window;
        Gtk::TextView                 m_textview;
        Glib::RefPtr<Gtk::TextBuffer> m_textbuffer;
    };
    
    TextViewTutorial::TextViewTutorial()
    {
        // Fenstergröße setzen
        set_size_request(400,200);
    
        // Titel setzen
        set_title("GTKmm Tutorial Teil 4");
    
        // Textpuffer erstellen
        m_textbuffer = Gtk::TextBuffer::create();
    
        // Wir wollen das die Balken automatisch angezeigt werden, sobald mehr Text 
        // vorhanden ist als in den Anzeigebereich passt
        m_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC,Gtk::POLICY_AUTOMATIC);
    
        // Textpuffer setzen
        m_textview.set_buffer(m_textbuffer);
    
        // Textview dem ScrolledWindow hinzufügen
        m_scrolled_window.add(m_textview);
    
        // ScrolledWindow dem Fenster hinzufügen
        add(m_scrolled_window);
    
        // Alle Widgets anzeigen
        show_all_children();
    }
    
    TextViewTutorial::~TextViewTutorial()
    {
    }
    
    int main(int argc, char **argv)
    {
        Gtk::Main main(argc,argv);
        TextViewTutorial window;
        main.run(window);
        return EXIT_SUCCESS;
    }
    

    2.2 Textformatierungen

    Wie man am obigen Beispiel gut erkennen kann, ist das Anlegen eines TextViews sehr simpel.
    Interessanter wird es wenn man Text formatieren möchte. Hierzu werden "Tags" definiert, welche eine Formatierung des Textes definieren.
    Man kann mehrere dieser Tags anlegen und mit Namen versehen. Zusätzlich können mehrere Tags auf den gleichen Text angewandt werden.

    Mit Tags kann man unter anderem folgende Formatierungen setzen:
    - Schriftart
    - Schriftgröße
    - Hintergrundfarbe
    - Textfarbe
    - Textstil (wie z.B. Kursiv, Fett, Unterstrichen etc. mit diversen Abstufungen)
    ...

    Für noch mehr Möglichkeiten lohnt sich ein Blick in die Dokumentation.

    Nun zu einem Beispiel, wie man diese Tags verwendet:

    #include <gtkmm.h>
    
    struct TextViewTutorial : public Gtk::Window
    {
    	TextViewTutorial();
    	~TextViewTutorial();
    
    private:
    	typedef Glib::RefPtr<Gtk::TextBuffer::Tag> TextTagPtr;
    
    	Gtk::ScrolledWindow           m_scrolled_window;
    	Gtk::TextView                 m_textview;
    	Glib::RefPtr<Gtk::TextBuffer> m_textbuffer;
    	Gtk::HBox					  m_hbox;
    	Gtk::VButtonBox				  m_vbuttonbox;
    
    	Gtk::Button					  m_bold;
    	Gtk::Button					  m_color;
    	Gtk::Button					  m_underline;
    	Gtk::Button					  m_italic;	
    
    	void apply_tag(TextTagPtr tag);
    	void attach_signal(Gtk::Button & b, TextTagPtr const & tag);
    };
    
    void TextViewTutorial::attach_signal(Gtk::Button & b, TextTagPtr const & tag)
    {
    	b.signal_clicked().connect(sigc::bind<TextTagPtr>(sigc::mem_fun(this, &TextViewTutorial::apply_tag), tag));
    }
    
    void TextViewTutorial::apply_tag(TextTagPtr tag)
    {
    	Gtk::TextBuffer::iterator begin, end;
    	if(m_textbuffer->get_selection_bounds(begin, end))
    	{
    		m_textbuffer->apply_tag(tag, begin, end);
    	}
    }
    
    TextViewTutorial::TextViewTutorial()
    {
    	// Fenstergröße setzen
    	set_size_request(400,200);
    
    	// Titel setzen
    	set_title("GTKmm Tutorial Teil 4 - Textformatierung");	
    
    	// Textpuffer erstellen
    	m_textbuffer = Gtk::TextBuffer::create();
    
    	// Wir wollen das die Balken automatisch angezeigt werden, sobald mehr Text
    	// vorhanden ist als in den Anzeigebereich passt
    	m_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC,Gtk::POLICY_AUTOMATIC);
    
    	// Textpuffer setzen
    	m_textview.set_buffer(m_textbuffer);
    
    	// Textview dem ScrolledWindow hinzufügen
    	m_scrolled_window.add(m_textview);
    
    	// Erstellung der Tags und Definition der Formatierungen
    	// Rote Schrift
    	TextTagPtr color_tag = Gtk::TextBuffer::Tag::create("color");
    	color_tag->property_foreground() = "red";
    
    	// Fettschrift
    	TextTagPtr bold_tag	= Gtk::TextBuffer::Tag::create("bold");
    	bold_tag->property_weight() = 900;
    
    	// Kursive Schrift
    	TextTagPtr italic_tag = Gtk::TextBuffer::Tag::create("italic");
    	italic_tag->property_style() = Pango::STYLE_ITALIC;
    
    	// Unterstrichener Text
    	TextTagPtr underlined_tag = Gtk::TextBuffer::Tag::create("underlined");
    	underlined_tag->property_underline() = Pango::UNDERLINE_SINGLE;
    
    	// Buttonbeschriftung setzen und signal verbinden
    	m_color.set_label("Farbe");
    	attach_signal(m_color, color_tag);
    	m_bold.set_label("Fett");
    	attach_signal(m_bold, bold_tag);
    	m_italic.set_label("Kursiv");
    	attach_signal(m_italic, italic_tag);
    	m_underline.set_label("Unterstrichen");
    	attach_signal(m_underline, underlined_tag);
    
    	// Tags dem TextPuffer bekannt machen
    	m_textbuffer->get_tag_table()->add(color_tag);
    	m_textbuffer->get_tag_table()->add(bold_tag);
    	m_textbuffer->get_tag_table()->add(italic_tag);
    	m_textbuffer->get_tag_table()->add(underlined_tag);
    
    	// Buttons der ButtonBox übergeben
    	m_vbuttonbox.pack_start(m_color);
    	m_vbuttonbox.pack_start(m_bold);
    	m_vbuttonbox.pack_start(m_italic);
    	m_vbuttonbox.pack_start(m_underline);
    
    	// ScrolledWindow dem Layout hinzufügen
    	m_hbox.pack_start(m_scrolled_window);
    	// Button box dem Layout hinzufügen
    	m_hbox.pack_start(m_vbuttonbox, Gtk::PACK_SHRINK, 10);
    
    	// Layout setzen
    	add(m_hbox);
    
    	// Alle Widgets anzeigen
    	show_all_children();
    }
    
    TextViewTutorial::~TextViewTutorial()
    {
    }
    
    int main(int argc, char **argv)
    {
    	Gtk::Main main(argc,argv);
    	TextViewTutorial window;
    	main.run(window);
    	return EXIT_SUCCESS;
    }
    

    Eine kleine Erklärung des Codes:
    In diesem Beispiel werden vier Tags definiert "color", "bold", "italic" und "underlined". Diese vier Tags können mit den Buttons gesetzt werden, indem man den eingegebenen Text auswählt und einen oder eventuell auch mehrere Buttons anklickt.

    Nun noch eine Erklärung der "attach_signal" Methode: Diese Methode setzt den Signalhandler für die übergebene Schaltfläche und bindet das übergebene Tag an den Aufruf, somit kann man einen Handler für alle Buttons verwenden und muss nicht vier verschiedene schreiben.

    Die Methode "apply_tag", welche durch einen Klick auf einen Button ausgelöst wird, holt sich zunächst den Auswahlbereich über

    if(m_textbuffer->get_selection_bounds(begin, end))
    

    Sollte der Bereich leer sein, sprich wenn keine Auswahl vorhanden ist, gibt die Methode "get_selection_bounds" false zurück, andernfalls wird via

    m_textbuffer->apply_tag(tag, begin, end);
    

    das übergebene Tag auf den ausgewählten Bereich angewandt.

    Und so sieht's aus:

    2.3 Weiterführendes zum Thema

    Wenn Ihr den Inhalt des TextView Puffers lesen oder ändern möchtet, wird dies mit Hilfe der Iteratoren erledigt. Iteratoren sind aber nur gültig solange der TextBuffer nicht verändert wurde.

    Manchmal möchte man sich aber eine bestimmte Position im Text merken. Hierfür bietet gtkmm sogenannte TextMarks an, mit welchen man an einer bestimmten Stelle im Text ein "Lesezeichen" setzen kann. Diese Lesezeichen können mit der Methode create_mark() erstellt werden.

    Glib::RefPtr<Gtk::TextBuffer::Mark> mark = m_textbuffer.create_mark(iterator_pos);
    

    Man kann diese Lesezeichen auch benennen, damit kann man umgehen das man die Instanzen der Klasse TextMark herumreichen muss. Um ein benanntes Lesezeichen zu erstellen geht geht man folgenermaßen vor:

    Glib::RefPtr<Gtk::TextBuffer::Mark> mark = m_textbuffer.create_mark("LesezeichenName", iterator_pos);
    

    Zusätzlich gibt es noch einen weiteren Parameter bei der Erstellung einer Textmarke: "left_gravity", welcher per Voreinstellung auf true gesetzt ist.
    Dieser Parameter gibt an, in welche Richtung sich die Textmarke beim Einfügen vom Text verschiebt.

    Hinweis am Rande: Bei Sprachen die Rechts-nach-Links gelesen werden ist "left_gravity" automatisch umgedreht, das heißt es verhält sich genauso zum Text wie bei den in westlichen Ländern verwendeten Eingabeschemata.

    Es sind noch viel mehr Dinge mit Gtk::TextView möglich, jedoch würde dies den Rahmen eines Einsteiger Tutorials bei weitem überschreiten.
    Für nähere Infos zu diesem Thema schlagt einfach in der unten verlinkten Dokumentation nach.

    Im Folgenden noch eine kurze Zusammenfassung, was man so alles mit einer Gtk::TextView anstellen kann:
    - Ihr könnt mit der Zwischenablage arbeiten, um Text auszulesen, auszuschneiden, oder zu kopieren
    - Ihr könnt Bilder und sogar andere Widgets wie Buttons, Checkboxen etc. einfügen (z.b. um eine einem Webbrowser ähnliche Funktionalität zu erzeugen)
    - Ihr habt unzählige Möglichkeiten zur Textformatierung: Größe, Dicke, Farbe, Ausrichtung u.v.m.

    Das Interface zu der Funktionalität ist eigentlich für die Fülle der Möglichkeiten recht kompakt und meines Erachtens nach schnell zu verstehen, auch wenn einen die dazugehörigen Methoden/Klassen auf den ersten Blick erschlagen mögen.

    Okay, ich hoffe Ihr hattet Spaß daran den Code zu lesen und ich hoffe es hat euch zu neuen Programmierideen inspiriert 🙂

    Da ich momentan etwas Zeit habe, werde ich mich auch gleich an den nächsten Teil machen, damit Ihr auch bald wieder was zum Thema zu lesen habt!

    Grüße aus der Tschechischen Republik,

    Vinzenz 'evilissimo' Feenstra 😉

    Referenzen:

    [1] GTKmm Dokumentationsseiten



  • Sehr gelungener, informativer Artikel! Detailliert beschrieben, jedoch nicht unnötig überladen. Das einzige, was mich momentan noch etwas irritiert, ist das Konzept von GTKmm - überhaupt nicht vergleichbar mit wxWidgets z.B.. Aber diese Artikelserie macht es mir um einiges leichter, mich in diese Library einzuarbeiten. 🙂



  • Ich finde es wirklich toll, dass es hier eine so gute Einführung in eine Menge der Widgets von Gtkmm gibt.

    Das alles hier hätte mir vor ein paar Monaten viel herumprobieren ersparen können 😃


Log in to reply