GTK mit Builder: Dialoge

Aus Wikibooks

Dialoge im Eigenbau[Bearbeiten]

Dialoge zeigen zumeist kurzfristig Informationen an. Mit ihnen lassen sich auch Einstellungen vornehmen und Daten eingeben. Dialoge stellen manchmal eine Unterbrechung des aktuellen Betriebes dar, in Einzelfällen sollte man daher überlegen, ob man einen Dialog nicht durch Elemente im Hauptfenster ersetzt. In Textverarbeitungen hat es sich beispielsweise eingebürgert, auf Dialoge zur Änderung der Schriftart zu verzichten - zu Gunsten von aufklappbaren Listen in der Werkzeugleiste. Da, wo Dialoge gebraucht werden, bieten sie eine prima Möglichkeit zur strukturierten Interaktion mit dem Programmnutzer.

In diesem Kapitel stellen wir Ihnen drei Programme vor, die selbst gebaute Dialoge anzeigen. Gleichzeitig zeigen wir Ihnen, wie typische Widgets in diesen Dialogen funktionieren.

Eine kleine Auswahl[Bearbeiten]

Das folgende Programm stellt Ihnen einen Dialog zur Verfügung, bei der Sie aus drei Optionen eine auswählen können. Klicken Sie beim Dialog auf „Ok“, dann erhalten Sie einen Hinweis auf Ihre Auswahl. Bei „Abbrechen“ passiert gar nichts.

Hier die XML-Beschreibung:

Align=none
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<interface>
    <object class="GtkUIManager" id="uimanager">
    <child>
        <object class="GtkActionGroup" id="aktionen">
        <child>
            <object class="GtkAction" id="dialog-action">
            <property name="label">_Datei</property>
            </object>
        </child>
        <child>
            <object class="GtkAction" id="neuer-dialog-action">
            <property name="label">_Neuer Dialog</property>
            <signal name="activate" handler="starte_dialog"/>
            </object>
        </child>
        <child>
            <object class="GtkAction" id="exit-action">
            <property name="label">_Beenden</property>
            <property name="stock-id">gtk-quit</property>
            <signal name="activate" handler="gtk_main_quit"/>
            </object>
        </child>
        </object>
    </child>
    <ui>
    <menubar name="menubar">
        <menu action="dialog-action" >
            <menuitem action="neuer-dialog-action" />
            <menuitem action="exit-action" />
        </menu>
    </menubar>
    </ui>
    </object>

    <object class="GtkDialog" id="mein-dialog1">
    <property name="title">Vielleicht Formatieren</property>
    <child internal-child="vbox">
        <object class="GtkVBox" id="vbox">
        <child>
            <object class="GtkFrame" id="frame1">
            <property name="label">Formatieren</property>
            <child>
                <object class="GtkVBox" id="vbox-im-frame1">
                <child>
                    <object class="GtkRadioButton" id="radiobutton1" >
                    <property name="label">Festplatte sicher formatieren</property>
                    </object>
                </child>
                <child>
                    <object class="GtkRadioButton" id="radiobutton2" >
                    <property name="label">Festplatte unsicher formatieren</property>
                    <property name="group">radiobutton1</property>
                    </object>
                </child>
                <child>
                    <object class="GtkRadioButton" id="radiobutton3" >
                    <property name="label">Ich überlege noch</property>
                    <property name="group">radiobutton1</property>
                    </object>
                </child>
                </object>
            </child>
            </object>
        </child>
        <child internal-child="action_area">
            <object class="GtkHButtonBox" id="knopfkiste">
            <child>
                <object class="GtkButton" id="knopf_cancel">
                <property name="use-stock">TRUE</property>
                <property name="label">gtk-cancel</property>
                </object>
            </child>
            <child>
                <object class="GtkButton" id="knopf_ok">
                <property name="use-stock">TRUE</property>
                <property name="label">gtk-ok</property>
                </object>
            </child>
            </object>
        </child>
        </object>
    </child>
    <action-widgets>
        <action-widget response="1">knopf_cancel</action-widget>
        <action-widget response="2">knopf_ok</action-widget>
    </action-widgets>
    </object>

    <object class="GtkWindow" id="hauptfenster" >
    <signal name="destroy" handler="gtk_main_quit"/>
    <child>
        <object class="GtkVBox" id="vbox-layout">
        <property name="homogeneous">FALSE</property>
        <child>
            <object class="GtkMenuBar" id="menubar" constructor="uimanager" />
            <packing>
            	<property name="expand">FALSE</property>
            	<property name="fill">FALSE</property>
            </packing>
        </child>
        <child>
            <object class="GtkLabel" id="mein-label-1">
            <property name="label">Starten Sie den Dialog über das Menü</property>
            </object>
        </child>
        </object>
    </child>
    </object>
</interface>

In dieser Beschreibung finden Sie ein Menü, dessen Aufbau Sie schon kennen. Mit der Callback-Funktion starte_dialog() wird im Programm Ihr Dialog aufgerufen.

Das in diesem Beispiel neue Objekt der Klasse GtkDialog mit der Id „mein-dialog1“ enthält eine vertikale Box, in der sich ein Rahmen (GtkFrame) befindet. In diesem Rahmen sind drei Radioknöpfe angebracht. Ebenfalls enthält der Dialog eine Knopfkiste (GtkHButtonBox), die sich so verhält wie eine horizontale Kiste, mit zwei Knöpfen drin. Darüber hinaus werden diese Knöpfe in einem so genannten „action-widgets“-Bereich referenziert, um den Dialog eindeutig beenden zu können.

Objekte vom Typ GtkDialog enthalten automatisch zwei Bereiche, ein Bereich ist eine vertikale Box, der im Beispiel als „internal-child=“vbox““ gekennzeichnet ist, der andere Bereich ist durch das Attribut „internal-child=“action_area““ markiert. Diese Bereiche sind vordefiniert, wobei die „action_area“ in der „vbox“ enthalten ist.

In der oberen vertikalen Box fügen wir einen Rahmen ein. Ein Rahmen kann einen beschreibenden Text im oberen Bereich enthalten. Sie können einen Rahmen unter anderem dahingehend verändern, dass Sie die Schattierung verändern. Dadurch kann er angehoben oder abgesenkt wirken.

Innerhalb des Rahmens bringen wir eine weitere vertikale Box an, die einige besondere Auswahlknöpfe der Klasse GtkRadioButton beinhalten. Diese Knöpfe sollen es ermöglichen, aus drei Optionen genau eine auswählen zu können. Diese Knöpfe haben einen beschreibenden Text. Zwei der Knöpfe verweisen auf den ersten Knopf und bilden so eine Gruppe (<property name="group">). Innerhalb diese Gruppe kann nur ein Knopf angewählt sein. Wählt man einen aus, dann verschwindet die Auswahl bei einem anderen Knopf.

In der „action_area“ kommen Knöpfe hinein, die den Dialog beenden sollen. Es stehen in diesem Beispiel „Ok“ und „Abbrechen“ zur Auswahl. Jeder der Knöpfe soll ein Piktogramm anzeigen. Etwas trickreich ist es hier, dass über das Attribut „label“ das Piktogramm und gleichzeitig der beschreibende Text des Knopfes ausgewählt wird. Hier unterstützt uns Gtk+ mit passenden vordefinierten Feldern.

Dem Hauptprogramm wollen wir, wenn der Dialog beendet wird, mitteilen, welcher Knopf gedrückt wurde. Die „action-widget“-Elemente geben uns genau dafür die Gelegenheit. Hier definieren wir mit dem Attribut „response“ diejenigen Zahlen, die beim Drücken der jeweiligen Knöpfe übermittelt werden. Bitte verwenden Sie hier nur positive Werte.

Der Quelltext sieht folgendermaßen aus:

C
#include <gtk/gtk.h>
#include <glib.h>
#include <glib/gprintf.h>

void starte_dialog (GtkWidget *w, GtkBuilder *b)
{
    GtkWidget *dialog, *radio, *nachrichtendialog;
    gint knopf;
    dialog = GTK_WIDGET(gtk_builder_get_object (b, "mein-dialog1"));
    gtk_widget_show_all (dialog);
    knopf = gtk_dialog_run (GTK_DIALOG(dialog));
    if (knopf == 2)
    {
        /* "OK" wurde geklickt, herausfinden, Knopf gedrückt wurde */
        gchar *antwort;
        radio = GTK_WIDGET(gtk_builder_get_object (b, "radiobutton1"));
        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio)))
            antwort = "sicher";
        else
        {
            radio = GTK_WIDGET(gtk_builder_get_object (b, "radiobutton2"));
            if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio)))
                antwort = "unsicher";
            else
                antwort = "gar nicht";
        }
        /* Dem User mitteilen, was nun passiert */
        nachrichtendialog = gtk_message_dialog_new (NULL,
            GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK,
            "Ich werden nun ihre Festplatte %s formatieren...", antwort);
        gtk_dialog_run (GTK_DIALOG(nachrichtendialog));
        gtk_widget_destroy (nachrichtendialog);
    }
    gtk_widget_hide (dialog);
}

int main (int argc, char *argv[])
{
    GError *errors = NULL;
    GtkWidget *window;
    GtkBuilder *builder;
    
    gtk_init (&argc, &argv);
    builder = gtk_builder_new ();
    gtk_builder_add_from_file (builder, "dialog1.xml", &errors);
    gtk_builder_connect_signals (builder, builder);
    window = GTK_WIDGET(gtk_builder_get_object (builder, "hauptfenster"));
    gtk_widget_show_all (window);
    gtk_main ();
    return 0;
}

Die Funktion starte_dialog() wird unter anderem mit dem Builder-Objekt als Parameter aufgerufen. Dieser hilft uns dabei, den Dialog mit gtk_builder_get_object() zu finden und anzuzeigen. Der Dialog wird mit gtk_dialog_run() so gestartet, dass seine eigene Nachrichtenschleife läuft. Diese Funktion wird nur dann verlassen, wenn der Anwender auf einen der Knöpfe oder auf das Schließen-Symbol in der Titelleiste geklickt hat. Der Knopf, der gedrückt wurde, wird als Zahl wie in der XML-Beschreibung festgelegt, von der Funktion gtk_dialog_run() übermittelt.

Wurde „Ok“ geklickt, dann wird nach dem aktuell ausgewählten Auswahlknopf gesucht. Hierzu fragen wir mit gtk_toggle_button_get_active() jeden einzelnen Knopf ab, ob dieser ausgewählt wurde. Um dem Anwender Rückmeldung zu geben, starten wir in bekannter Weise einen Nachrichtendialog.


Widgets in Dialogen[Bearbeiten]

Wir wollen Ihnen mit dem folgenden Beispiel einige Widgets zeigen, die sich gut in Dialoge einfügen lassen und helfen, Themen zu gruppieren. Hier bietet sich besonders die Klasse GtkNotebook an, die ähnlich wie Karteikarten funktioniert. Jede Karteikarte, also Seite im Sinne von GtkNotebook, enthält ein Thema. Auf jeder unserer im folgenden Beispiel zwei Seiten wird etwas vorgestellt. Einmal erhalten Sie Knöpfe, im anderen Fall eine Seite, in der Sie Personendaten eingeben können.

Die XML-Beschreibung:

Align=none
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<interface>
    <object class="GtkUIManager" id="uimanager">
    <child>
        <object class="GtkActionGroup" id="aktionen">
        <child>
            <object class="GtkAction" id="dialog-action">
            <property name="label">_Datei</property>
            </object>
        </child>
        <child>
            <object class="GtkAction" id="neuer-dialog-action">
            <property name="label">_Neuer Dialog</property>
            <signal name="activate" handler="starte_dialog"/>
            </object>
        </child>
        <child>
            <object class="GtkAction" id="exit-action">
            <property name="label">_Beenden</property>
            <property name="stock-id">gtk-quit</property>
            <signal name="activate" handler="gtk_main_quit"/>
            </object>
        </child>
        </object>
    </child>
    <ui>
    <menubar name="menubar">
        <menu action="dialog-action" >
            <menuitem action="neuer-dialog-action" />
            <menuitem action="exit-action" />
        </menu>
    </menubar>
    </ui>
    </object>

    <object class="GtkDialog" id="mein-dialog1">
    <property name="title">Konfigurieren</property>
    <child internal-child="vbox">
        <object class="GtkVBox" id="vbox">
        <child>
            <object class="GtkNotebook" id="notebook-1">
            <!-- Seite 1 -->
            <child>
                <object class="GtkFrame" id="frame-knoepfe-1">
                <property name="label">Knöpfe</property>
                <child>
                    <object class="GtkVBox" id="vbox-1-1">
                    <child>
                        <object class="GtkCheckButton" id="check-1-1-1">
                        <property name="label">Alle Daten automatisch überschreiben</property>
                        </object>
                    </child>
                    <child>
                        <object class="GtkCheckButton" id="check-1-1-2">
                        <property name="label">SPAM-Mails immer ausdrucken</property>
                        <property name="active">TRUE</property>
                        </object>
                    </child>
                    </object>
                </child>
                </object>
            </child>
            <child type="tab">
                <object class="GtkLabel" id="notebook-seitentitel-1">
                <property name="label">Knöpfe</property>
                </object>
            </child>
            <!-- Seite 2 -->
            <child>
                <object class="GtkFrame" id="frame-formular-2">
                <property name="label">Formular</property>
                <child>
                    <object class="GtkTable" id="table-2-2"> 
                    <property name="homogeneous">TRUE</property>
                    <property name="n-columns">2</property>
                    <property name="n-rows">3</property>
                    <child>
                        <object class="GtkLabel" id="label-2-2-1">
                        <property name="label">Anrede</property>
                        </object>
                        <packing>
                       	<property name="left-attach">0</property>
                       	<property name="right-attach">1</property>
                       	<property name="top-attach">0</property>
                       	<property name="bottom-attach">1</property>
                       	</packing>
                    </child>
                    <child>
                        <object class="GtkComboBoxText" id="combo-2-2-1">
                        <property name="entry-text-column">0</property>
                        <items>
                            <item>Frau</item>
                            <item>Herr</item>
                        </items>
                        </object>
                        <packing>
                       	<property name="left-attach">1</property>
                       	<property name="right-attach">2</property>
                       	<property name="top-attach">0</property>
                       	<property name="bottom-attach">1</property>
                       	</packing>
                    </child>
                    <child>
                        <object class="GtkLabel" id="label-2-2-2">
                        <property name="label">Name</property>
                        </object>
                        <packing>
                       	<property name="left-attach">0</property>
                       	<property name="right-attach">1</property>
                       	<property name="top-attach">1</property>
                       	<property name="bottom-attach">2</property>
                       	</packing>
                    </child>
                    <child>
                        <object class="GtkEntry" id="entry-2-2-2">
                        <property name="text">Müller</property>
                        </object>
                        <packing>
                       	<property name="left-attach">1</property>
                       	<property name="right-attach">2</property>
                       	<property name="top-attach">1</property>
                       	<property name="bottom-attach">2</property>
                       	</packing>
                    </child>
                    <child>
                        <object class="GtkLabel" id="label-2-2-3">
                        <property name="label">Alter</property>
                        </object>
                        <packing>
                       	<property name="left-attach">0</property>
                       	<property name="right-attach">1</property>
                       	<property name="top-attach">2</property>
                       	<property name="bottom-attach">3</property>
                       	</packing>
                    </child>
                    <child>
                        <object class="GtkAdjustment" id="adjustment-2-2-3">
                        <property name="lower">1</property>
                        <property name="upper">121</property>
                        <property name="value">27</property>
                        <property name="step-increment">1</property>
                        <property name="page-increment">10</property>
                        </object>
                        <object class="GtkSpinButton" id="spinbutton-2-2-3">
                        <property name="digits">0</property>
                        <property name="adjustment">adjustment-2-2-3</property>
                        </object>
                        <packing>
                       	<property name="left-attach">1</property>
                       	<property name="right-attach">2</property>
                       	<property name="top-attach">2</property>
                       	<property name="bottom-attach">3</property>
                       	</packing>
                    </child>
                    </object>
                </child>
                </object>
            </child>
            <child type="tab">
                <object class="GtkLabel" id="notebook-seitentitel-2">
                <property name="label">Formular</property>
                </object>
            </child>
            </object>
        </child>
        <child internal-child="action_area">
            <object class="GtkHButtonBox" id="knopfkiste">
            <child>
                <object class="GtkButton" id="knopf_cancel">
                <property name="use-stock">TRUE</property>
                <property name="label">gtk-cancel</property>
                </object>
            </child>
            <child>
                <object class="GtkButton" id="knopf_ok">
                <property name="use-stock">TRUE</property>
                <property name="label">gtk-ok</property>
                </object>
            </child>
            </object>
        </child>
        </object>
    </child>
    <action-widgets>
        <action-widget response="1">knopf_cancel</action-widget>
        <action-widget response="2">knopf_ok</action-widget>
    </action-widgets>
    </object>

    <object class="GtkWindow" id="hauptfenster" >
    <signal name="destroy" handler="gtk_main_quit"/>
    <child>
        <object class="GtkVBox" id="vbox-layout">
        <property name="homogeneous">FALSE</property>
        <child>
            <object class="GtkMenuBar" id="menubar" constructor="uimanager" />
            <packing>
            	<property name="expand">FALSE</property>
            	<property name="fill">FALSE</property>
            </packing>
        </child>
        <child>
            <object class="GtkLabel" id="mein-label-1">
            <property name="label">Starten Sie den Dialog über das Menü</property>
            </object>
        </child>
        </object>
    </child>
    </object>
</interface>

Innerhalb des Objektes der Klasse GtkDialog liegt ein Objekt vom Typ GtkNotebook. Über „<child>“-Elemente werden die Seiten eingefügt. Zu jeder Seite gibt es zwei solcher Elemente. Einmal handelt es sich um den Inhalt, der auf der Seite dargestellt werden soll. Danach kommt ein Element mit dem zusätzlichen Attribut „type=tab“. Hierbei handelt es sich um den Kopf der Karteikartenseite. Beachten Sie bitte, dass die Reihenfolge zwingend einzuhalten ist. Wenn Sie in diesem Beispiel eine Seite hinzufügen wollen, dann müssen Sie zuerst eine Inhaltsseite per <child>-Element einfügen und anschließend das Kopffeld mit <child type=“tab“>.

Die erste Seite enthält in einem Rahmen zwei Knöpfe vom Typ GtkCheckButton. Diese Sorte Knöpfe können, ähnlich wie Knöpfe vom Typ GtkRadioButton, zwei Zustände einnehmen, an oder aus. Sie sind dazu gedacht, unabhängig voneinander zu sein, werden also nicht gruppiert.

Die zweite Seite enthält eine Tabelle, diese kennen Sie schon aus einem früheren Kapitel. Diese Tabelle hat zwei Spalten und drei Reihen. In die linke Spalte werden Textfelder eingefügt. Das Einfügen von Widgets in eine Tabelle ist etwas aufwendig denn statt eines einfachen Funktionsaufrufs von gtk_table_attach_defaults(), werden hier vier <property>-Elemente benötigt, um ein Widget zu positionieren.

In die rechte Spalte der zweiten Seite werden verschiedene Widgets eingefügt. Zuerst eine Menübox vom Typ GtkComboBoxText. Diese enthält eine Liste mit Texteinträgen, von denen man sich eines aussuchen kann. Sie bietet also eine Alternative zu GtkRadioButton. In diesem Fall werden zwei Texte hinzugefügt, nämlich „Herr“ und „Frau“. Diese Liste kann aufgeklappt werden, und man sucht sich den passenden Eintrag aus. Diese Box kann einen alternativen Editor haben, bei dem der Benutzer eigenständig Text eingibt, der vielleicht nicht in der Liste steht.

Darunter folgt eine Editorzeile vom Typ GtkEntry. In diese Zeile haben wir schon etwas für Sie hineingeschrieben. Dieser Text kann aber ersetzt werden.

Anschließend folgt ein Widget, bei dem Sie eine Zahl auswählen können. Dieses Widget ist zweigeteilt. Das Objekt der Klasse GtkAdjustment sorgt dafür, dass Sie die Möglichkeit erhalten, von 1 bis 121 zu zählen. Es hat den voreingestellten Wert von 27, den Sie in Einer-Schritten vergrößern können. Wenn Sie auf die „Seite-Nach-Oben“-Taste auf der Tastatur drücken, dann soll in 10er-Schritten vergrößert werden. Ist dieses Objekt fertig konfiguriert, dann kann man es in die Anzeige vom Typ GtkSpinButton per Referenz einbringen. Dieser Regler enthält dann alle Dinge so, wie sie von GtkAdjustment konfiguriert wurden.

Im Programm wird es jetzt vornehmlich um das Auslesen der Eingaben gehen:

C
#include <gtk/gtk.h>
#include <glib.h>
#include <glib/gprintf.h>

void starte_dialog (GtkWidget *w, GtkBuilder *b)
{
    GtkWidget *dialog, *nachrichtendialog, *widget;
    gint knopf;
    dialog = GTK_WIDGET(gtk_builder_get_object (b, "mein-dialog1"));
    gtk_widget_show_all (dialog);
    knopf = gtk_dialog_run (GTK_DIALOG(dialog));
    
    if (knopf == 2)
    {
        /* Wird SPAM gewünscht? */
        gboolean will_spam;
        gchar *spam;
        widget = GTK_WIDGET(gtk_builder_get_object (b, "check-1-1-2"));
        will_spam = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
        if (will_spam) 
            spam = "";
        else
            spam = "keine ";
        /* Anrede */
        gchar *anrede;
        widget = GTK_WIDGET(gtk_builder_get_object (b, "combo-2-2-1"));
        anrede = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(widget));
        /* Name */
        const gchar *name;
        widget = GTK_WIDGET(gtk_builder_get_object (b, "entry-2-2-2"));
        name = gtk_entry_get_text (GTK_ENTRY(widget));
        /* Alter */
        int alter;
        widget = GTK_WIDGET(gtk_builder_get_object (b, "spinbutton-2-2-3"));
        alter = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(widget));
        /* Daten ausgeben */
        nachrichtendialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
            GTK_MESSAGE_INFO, GTK_BUTTONS_OK,
            "%s %s (%d Jahre) wünscht %sSPAM-Mails!", anrede, name, alter, spam);
        gtk_dialog_run (GTK_DIALOG(nachrichtendialog));
        gtk_widget_destroy (nachrichtendialog);
        /* Aufräumen */
        g_free (anrede);
    }
    gtk_widget_hide (dialog);
}

int main (int argc, char *argv[])
{
    GError *errors = NULL;
    GtkWidget *window;
    GtkBuilder *builder;
    
    gtk_init (&argc, &argv);
    builder = gtk_builder_new ();
    gtk_builder_add_from_file (builder, "dialog2.xml", &errors);
    gtk_builder_connect_signals (builder, builder);
    window = GTK_WIDGET(gtk_builder_get_object (builder, "hauptfenster"));
    gtk_widget_show_all (window);
    gtk_main ();
    return 0;
}

Auch in diesem Beispiel wird der Dialog über die Funktion starte_dialog() geöffnet. Drückt der Anwender am Schluss der Eingabe in den Dialog auf „Ok“, dann werden die Widgets ausgelesen und es erscheint ein passender Text in einem Nachrichtendialog. Alle Widgets in der XML-Beschreibung haben eine eindeutige Id, deswegen können wir an jeder Stelle über das Builder-Objekt auf sie zugreifen.

GtkRadioButton und GtkCheckButton werden auf die gleiche Weise ausgelesen. Man benutzt hier gtk_toggle_button_get_active(). Der Grund dafür ist, dass die beide Knöpfe von der Klasse GtkToggleButton abgeleitet werden. Man erhält TRUE, wenn der Knopf gedrückt wurde.

Der Text, der im Objekt vom Typ GtkComboBoxText steht, kann mit der Funktion gtk_combo_box_text_get_active_text() ausgelesen werden. Das funktioniert aber nur, wenn in der dazugehörigen XML-Beschreibung „entry-text-column“ gesetzt wurde.

Der einzeilige Editor vom Typ GtkEntry kann mit gtk_entry_get_text() ausgelesen werden. Man bekommt hier einen konstanten Text zurückgeliefert, den man nicht modifizieren kann.

GtkSpinButton-Objekte können mit der Funktion gtk_spin_button_get_value_as_int() ausgelesen werden. Man kann das dazugehörige GtkAdjustment so konfigurieren, dass es Gleitkommawerte verarbeitet. Möchte man diese auslesen, benutzt man gtk_spin_button_get_value().


Blockierend oder nicht blockierend[Bearbeiten]

Alle Dialoge in den bisherigen Beispielen waren so gestaltet, dass man im Programm nichts anderes tun konnte, als den Dialog zu bedienen. Die Menüs des Hauptprogramms waren nicht mehr zugänglich. Öffnete ein Konfigurationsdialog einen Nachrichtendialog, so kam man nicht mehr an den Konfigurationsdialog, sondern musste zuerst den Nachrichtendialog beenden. In vielen Fällen ist genau dieses Verhalten gewünscht. Ein Anwendungsfall von Dialogen ist es aber auch, dauerhaft eine Werkzeugpalette zur Verfügung zu stellen, oder den Status eines Programms anzuzeigen.

In der Sprache von Gtk+ sind Fenster, die keine Interaktion mit dem übrigen Programm erlauben „modal“, sie blockieren also alle anderen Fenster. Nicht blockierende Fenster erlauben es hingegen, auch auf andere Fenster zuzugreifen. Dies gilt nicht alleine für Dialoge, sondern für alle Arten von Fenstern.

Wir stellen in den folgenden Beispielen zwei Dialoge vor, einen modalen und einen nicht-modalen und beginnen wie üblich mit der XML-Beschreibung:

Align=none
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<interface>
    <object class="GtkUIManager" id="uimanager">
    <child>
        <object class="GtkActionGroup" id="aktionen">
        <child>
            <object class="GtkAction" id="dialog-action">
            <property name="label">_Datei</property>
            </object>
        </child>
        <child>
            <object class="GtkAction" id="neuer-dialog-action-modal">
            <property name="label">_Modaler Dialog</property>
            <signal name="activate" handler="starte_modal_dialog"/>
            </object>
        </child>
        <child>
            <object class="GtkAction" id="neuer-dialog-action-non-modal">
            <property name="label">_Nicht-Modaler Dialog</property>
            <signal name="activate" handler="starte_nichtmodal_dialog"/>
            </object>
        </child>
        <child>
            <object class="GtkAction" id="exit-action">
            <property name="label">_Beenden</property>
            <property name="stock-id">gtk-quit</property>
            <signal name="activate" handler="gtk_main_quit"/>
            </object>
        </child>
        </object>
    </child>
    <ui>
    <menubar name="menubar">
        <menu action="dialog-action" >
            <menuitem action="neuer-dialog-action-modal" />
            <menuitem action="neuer-dialog-action-non-modal" />
            <menuitem action="exit-action" />
        </menu>
    </menubar>
    </ui>
    </object>
    <!-- Modaler Dialog -->
    <object class="GtkDialog" id="mein-dialog-modal">
    <property name="title">Ich bin modal!</property>
    <property name="modal">TRUE</property>
    <child internal-child="vbox">
        <object class="GtkVBox" id="vbox-modal">
        <child>
            <object class="GtkLabel" id="mein-dialog-modal-label-1">
            <property name="label">Ich bin modal</property>
            </object>
        </child>
        <child internal-child="action_area">
            <object class="GtkHButtonBox" id="knopfkiste-modal">
            <child>
                <object class="GtkButton" id="knopf-cancel-modal">
                <property name="use-stock">TRUE</property>
                <property name="label">gtk-cancel</property>
                </object>
            </child>
            <child>
                <object class="GtkButton" id="knopf-ok-modal">
                <property name="use-stock">TRUE</property>
                <property name="label">gtk-ok</property>
                </object>
            </child>
            </object>
        </child>
        </object>
    </child>
    <action-widgets>
        <action-widget response="1">knopf-cancel-modal</action-widget>
        <action-widget response="2">knopf-ok-modal</action-widget>
    </action-widgets>
    </object>
    <!-- Nicht-Modaler Dialog -->
    <object class="GtkDialog" id="mein-dialog-nicht-modal">
    <property name="title">Ich bin nicht modal!</property>
    <property name="modal">FALSE</property>
    <child internal-child="vbox">
        <object class="GtkVBox" id="vbox">
        <child>
            <object class="GtkLabel" id="mein-dialog-label-1">
            <property name="label">Ich bin nicht modal</property>
            </object>
        </child>
        <child internal-child="action_area">
            <object class="GtkHButtonBox" id="knopfkiste">
            <child>
                <object class="GtkButton" id="knopf-cancel">
                <property name="use-stock">TRUE</property>
                <property name="label">gtk-cancel</property>
                </object>
            </child>
            <child>
                <object class="GtkButton" id="knopf-ok">
                <property name="use-stock">TRUE</property>
                <property name="label">gtk-ok</property>
                </object>
            </child>
            </object>
        </child>
        </object>
    </child>
    <action-widgets>
        <action-widget response="1">knopf-cancel</action-widget>
        <action-widget response="2">knopf-ok</action-widget>
    </action-widgets>
    </object>
    <!-- Hauptfenster -->
    <object class="GtkWindow" id="hauptfenster" >
    <signal name="destroy" handler="gtk_main_quit"/>
    <child>
        <object class="GtkVBox" id="vbox-layout">
        <property name="homogeneous">FALSE</property>
        <child>
            <object class="GtkMenuBar" id="menubar" constructor="uimanager" />
            <packing>
            	<property name="expand">FALSE</property>
            	<property name="fill">FALSE</property>
            </packing>
        </child>
        <child>
            <object class="GtkLabel" id="mein-label-1">
            <property name="label">Starten Sie den Dialog über das Menü</property>
            </object>
        </child>
        </object>
    </child>
    </object>
</interface>

In dieser Beschreibung finden Sie ein erweitertes Menü, das Hauptfenster und zwei Dialoge vor. Die Dialoge sind gleich gestaltet, bis auf den beschreibenden Text und dem Property-Element

Align=none

{{{1}}}


Hiermit wird festgelegt, ob der Dialog blockierend sein soll. Da unabhängig von diesem Element alle Dialoge, die über gtk_dialog_run() gestartet werden blockierend sind, zeigen wir die Unterschiede ohne den Aufruf der Ereignisschleife im Hauptprogramm:

C
#include <gtk/gtk.h>
#include <glib.h>

void starte_modal_dialog (GtkWidget *w, GtkBuilder *b)
{
    GtkWidget *dialog;
    dialog = GTK_WIDGET(gtk_builder_get_object (b, "mein-dialog-modal"));
    gtk_widget_show_all (dialog);
    g_signal_connect_swapped (dialog, "response",
        G_CALLBACK (gtk_widget_hide), dialog);
}

void starte_nichtmodal_dialog (GtkWidget *w, GtkBuilder *b)
{
    GtkWidget *dialog;
    dialog = GTK_WIDGET(gtk_builder_get_object (b, "mein-dialog-nicht-modal"));
    gtk_widget_show_all (dialog);
    g_signal_connect_swapped (dialog, "response", 
        G_CALLBACK (gtk_widget_hide), dialog);
}

int main (int argc, char *argv[])
{
    GError *errors = NULL;
    GtkWidget *window;
    GtkBuilder *builder;
    
    gtk_init (&argc, &argv);
    builder = gtk_builder_new ();
    gtk_builder_add_from_file (builder, "dialog3.xml", &errors);
    gtk_builder_connect_signals (builder, builder);
    window = GTK_WIDGET(gtk_builder_get_object (builder, "hauptfenster"));
    gtk_widget_show_all (window);
    gtk_main ();
    return 0;
}

Die Ereignisschleife gtk_dialog_run() wird beendet, wenn das Signal „response“ empfangen wird. Das machen wir uns in diesem Beispiel zu Nutze, in dem wir das entsprechende Signal mit der Callback-Funktion gtk_widget_hide() verknüpfen. Wir verzichten dabei auf eine Auswertung der Rückgabewerte. Diese würde in einer separaten Callback erfolgen.

Der modale Dialog blockiert, der nicht blockierende hingegen gibt dem Anwender die Möglichkeit, weiterhin auf das Menü zuzugreifen. Startet man nacheinander beide Dialoge, so blockiert der modale Dialog auch den nicht-modalen.

Zusammenfassung[Bearbeiten]

In diesem Kapitel haben Sie viele Details aus der Welt der Dialoge kennen gelernt. Sie sind nun in der Lage, eigene Dialoge zu bauen und anzuzeigen. Ebenfalls haben wir eine Reihe von Widgets vorgestellt, die Sie in Ihre Programme einbinden und auswerten können.