Progmar Marcin Załęczny

Kod programu witaj świecie z demonstracją potwierdzenia wyjścia

main.cpp

#include <gtk/gtk.h>

void onDestroy(GtkWidget* widget, gpointer data) {
	gtk_main_quit();
}


gboolean onDeleteEvent(GtkWidget* widget, GdkEvent* event, gpointer data) {
	GtkWidget *dialog;
	dialog = gtk_message_dialog_new(GTK_WINDOW(widget), GTK_DIALOG_DESTROY_WITH_PARENT,
		GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
		"Czy na pewno chcesz opuścić program?");
	gtk_window_set_title(GTK_WINDOW(dialog), "Pytanie");
	GtkResponseType result = (GtkResponseType)gtk_dialog_run(GTK_DIALOG(dialog));
	gtk_widget_destroy(dialog);
	if (result == GTK_RESPONSE_YES) {
		return FALSE;
	}
	return TRUE;
}

int main(int argc, char** argv) {
	GtkBuilder *builder;
	GError *error;
	GtkWidget *window;
	
	gtk_init(&argc, &argv);
	
	builder = gtk_builder_new();
	error = NULL;
	gtk_builder_add_from_file(builder, "main_window.ui", &error);
	if (error) {
		g_print("Wystąpił błąd: %s\n", error->message);
		g_error_free(error);
		return -1;
	}
	window = GTK_WIDGET(gtk_builder_get_object(builder, "wndHelloWorld"));
	g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(onDeleteEvent), NULL);
	g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(onDestroy), NULL);
	gtk_widget_show_all(window);
	
	gtk_main();
	return 0;
}

Wyjaśnienie kodu

Program ten demonstruje jak opuścić działającą aplikację po kliknięciu w przycisk TAK w okienku dialogowym wymagającym potwierdzenia operacji zamknięcia programu. Aby to osiągnąć podpięliśmy funkcje obsługi zdarzenia "delete-event" - onDeleteEvent oraz sygnału "destroy" - onDestroy. Funkcje te różnią się nieco nagłówkami. Pierwsza z nich ma nagłówek:

gboolean user_function(GtkWidget *widget, GdkEvent *event, gpointer user_data)

natomiast druga:

void user_function(GtkWidget *object, gpointer user_data)

O tym jak wygląda nagłówek funkcji obsługi zdarzenia możemy się przekonać w programie devhelp, wpisując w nim frazy wyszukiwania delete-event oraz destroy signal.

Dlaczego użyliśmy tych zdarzeń? Ponieważ w funkcji onDeleteEvent możemy się wycofać z chęci zamknięcia aplikacji zwracając wartość TRUE. Jeśli natomiast zwrócimy w niej wartość FALSE, to okno główne zostanie zniszczone a przed jego zniszczeniem wyemitowany zostanie sygnał destroy. Funkcja onDestroy odpowiada za opuszczenie funkcji gtk_main() po to, aby program zakończył działanie po zniszczeniu okna głównego aplikacji. Natomiast funkcja onDeleteEvent wyświetla okno dialogowe z zapytaniem "Czy na pewno chcesz opuścić program?". Jeśli użytkownik kliknie przycisk TAK - aplikacja zostanie zamknięta. W przeciwnym razie będzie kontynuowała działanie.

Okienko dialogowe tworzymy przy pomocy funkcji gtk_message_dialog_new i przypisujemy do zmiennej dialog. Funkcja ta przyjmuje następujące parametry:

  • Wskaźnik na okno rodzica (typu GtkWindow). Tutaj obiekt widget został zrzutowany na obiekt okna przy pomocy makra GTK_WINDOW.
  • Flagi dla tworzonego okna dialogowego. Tutaj przekazaliśmy flagę: GTK_DIALOG_DESTROY_WITH_PARENT oznaczającą, że utworzony dialog zostanie zniszczony wraz ze zniszczeniem okna rodzica. Nie oznacza to jednak, że nie możemy zniszczyć dialogu wywołując w prost funkcję gtk_widget_destroy.
  • Typ okna dialogowego - tutaj GTK_MESSAGE_QUESTION. Oznacza to, że okno dialogowe będzie mieć ikonkę pytajnika. Inne możliwe typy to: GTK_MESSAGE_INFO, GTK_MESSAGE_WARNING, GTK_MESSAGE_ERROR, GTK_MESSAGE_OTHER. W przypadku ostatniego z tych typów żadna ikonka nie zostanie wyświetlona.
  • Typy przycisków, które pojawią się w okienku - tutaj GTK_BUTTONS_YES_NO wyświetlające przyciski TAK i NIE. Inne możliwe typy to: GTK_BUTTONS_OK, GTK_BUTTONS_CLOSE, GTK_BUTTONS_CANCEL, GTK_BUTTONS_OK_CANCEL, GTK_BUTTONS_NONE. W przypadku ostatniego z tych typów żadne przyciski nie pojawią się w okienku dialogowym.
  • Jako ostatni parametr przekazywany jest tekst komunikatu. Może to być tekst zawierający formanty takie jak w funkcji printf. Wtedy wszystkie dodatkowe zmienne przekazujemy po tekście komunikatu.

Następnie utworzonemu widgetowi okna dialogowego nadajemy tytuł przy pomocy funkcji gtk_window_set_title. Teraz jesteśmy gotowi na wyświetlenie okna dialogowego. Robimy to wywołując funkcję gtk_dialog_run i przekazując jej nasz widget. Funkcja ta przetwarza w pętli komunikaty okna dialogowego aż do naciśnięcia któregokolwiek z przycisków. Wtedy kończy działanie i zwraca w postaci liczby całkowitej gint typ przycisku, który został kliknięty. Rezultat rzutujemy do typu GtkResponseType i zapamiętujemy w zmiennej result. Teraz możemy już zniszczyć okno dialogowe przy pomocy funkcji gtk_widget_destroy. Następnie testujemy zawartość zmiennej result i jeśli jest równa GTK_RESPONSE_YES, to zwracamy wartość FALSE pozwalając zamknąć okno główne. W przeciwnym razie przerywamy operację zamknięcia zwracając TRUE.