Blog (9)
Komentarze (7)
Recenzje (0)
@skrzeczol755Prosta nakładka graficzna na eSpeak w GTK+

Prosta nakładka graficzna na eSpeak w GTK+

10.07.2012 18:55

Czas na powrót do GTK… Jak zaznaczono w tytule, zajmiemy się GTK+, a nie GTKmm, czyli napiszemy program w C.

Jeżeli zajmujemy się tworzeniem za pomocą Anjuta, wybieramy zakładkę „C” i typ projektu „GTK+ (prosty)” przy tworzeniu nowego projektu i zostawiamy domyślne ustawienia, ale jeśli chodzi o nazwę projektu oraz podstawowe informacje o autorze, wpisujemy, co nam będzie pasować.

Zaznaczenie opcji (ptaszek lub inny znaczek) „Użycie biblioteki GtkBuilder dla interfejsu użytkownika” jest kluczowe, gdyż to nam umożliwi wykorzystanie gotowego interfejsu programu dla tych, którzy woleliby to napisać w czymś innym niż C.

Interfejs użytkownika

Użyjemy dwóch pudełek (ang. Box): jedno jako zawartość okna, drugie jako zawartość górnej komórki tego pierwszego. Pierwsze ma orientację poziomą (domyślną), drugie pionową. W lewej komórce będzie etykieta tekstowa, w drugiej pole wejścia tekstowego.

Druga komórka najbardziej zewnętrznego pudełka będzie zawierać ramkę (z tytułem) Wewnątrz umieszczamy Przewijane okno, a wewnątrz niego wieloliniowe pole textowe (Text View).

Trzecia komórka najbardziej zewnętrznego pudełka będzie zawierać przycisk (Button). Podepniemy do niego funkcję pozwalającą na obsługę kliknięcia.

W panelu „Widżety” (Widgets) odnajdujemy zakładkę Sygnały (Signals). W rozwijanej liście odnajdujemy GtkButton i pozycję „clicked”. Klikamy w tym wierszu na kolumnę „Program obsługujący i wpisujemy nazwę funkcji. We wszystkich nakładkach na GTK obsługujących automatyczne łączenie sygnałów nazwa funkcji będzie niezależna od użytego języka programowania.

W naszym przypadku zaczynamy od wpisywania „on”. Wyświetli się podpowiedź, co do nazwy tej funkcji. Zatwierdzamy ją. Nazwa sygnału zostanie pogrubiona dla wyróżnienia. Zapisujemy zmiany i mamy gotowy plik interfejsu.

Całość wygląda tak:

Gotowy interfejs naszej nakładku
Gotowy interfejs naszej nakładku

Kod

Automatycznie nam się wygeneruje import widżetu okna z pliku interfejsu, włączenie obsługi GTK, obsługa zdarzenia zamykania okna (powiązanie z nim zakończenia działania programu), pusta struktura _Private, odpowiednie stałe i włączone nagłówki.

Na początku użyjemy polecenia system() z biblioteki standardowej C, które umożliwi nam wykonywanie poleceń systemowych. Polecenie to staje się dostępne po włączeniu nagłówka stdlib.h. Możemy również wykorzystać inne polecenia do tworzenia procesów, ale o tym w innym wpisie.

Struktura _Private

Wewnątrz struktury _Private zadeklarujemy widżety GtkTextView i GtkEntry. Do tego między klamrą zamykającą strukturę a średnikiem wpiszemy nazwę struktury — dzięki temu struktura będzie zadeklarowana. Wszystkie pozostałe związane z tą strukturą rzeczy zostawiamy bez zmian. Struktura ta po zmianach jest taka:


static struct _Private
{
	/* ANJUTA: Widgets declaration for simple_espeak_gui.ui - DO NOT REMOVE */
	GtkTextView* users_text_to_speech;
	GtkEntry* lang_code;
} Widgets;

Import widżetów

Po zadeklarowaniu struktury zajmiemy się obsługą importu widżetów z pliku interfejsu. Sam kod będzie opierać się o domyślnie wygenerowany fragment dotyczący importu okna, przy założeniu, że Window jest typu wskaźnikowego GtkWidget:


window = GTK_WIDGET (gtk_builder_get_object (builder, TOP_WINDOW));
        if (!window)
        {
                g_critical ("Widget \"%s\" is missing in file %s.",
				TOP_WINDOW,
				UI_FILE);
        }

A teraz importujemy dwa najważniejsze widżety:


Widgets.input_text_to_speech = GTK_TEXT_VIEW (gtk_builder_get_object (builder, TTS));
if (!Struct.input_text_to_speech)
        {
                g_critical ("Widget \"%s\" is missing in file %s.",
				TTS,
				UI_FILE);
        }

Widgets.language = GTK_ENTRY (gtk_builder_get_object (builder, LANG));
if (!Struct.language)
        {
                g_critical ("Widget \"%s\" is missing in file %s.",
				LANG,
				UI_FILE);
        }

Mając w użyciu GtkBuider do importu widżetów z plików *.ui, importujemy widżety o określonych nazwach (najlepiej stałych, dlatego definiujemy sobie stałe. Tutaj została użyta dyrektywa #define na początku kodu źródłowego. Robi się to tak:

#define YOUR_TEXT "This is my text"

Tutaj zostało zrobione to tak:


#define LANG "langcode"
#define TTS "text-to-speech"

Aby import zadziałał, nazwy w stałych muszą się zgadzać z tymi przyporządkowanymi widżetom w pliku interfejsu.

Kod ten wstawiamy wewnątrz funkcji create_window(void) pod oznaczoną linijką komentarza:

ANJUTA: Widgets initialization for simple_espeak_gui.ui - DO NOT REMOVE

Dotyczy to tylko tworzenia z użyciem Anjuta IDE. Jeśli używamy czegoś innego, np. zestawu edytor tekstu (osobno) + kompilator (ręcznie obsługiwany) + projektant interfejsu (osobno), tworzymy całą funkcję (dostępną w kodzie referencyjnym, do którego link zostanie podany).

Zamieniamy tekst na mowę

Zakładam, że w pliku interfejsu zdefiniowaliśmy nazwę funkcji, która ma obsłużyć kliknięcie na przycisk. W tym przypadku nie trzeba definiować połączeń funkcji z sygnałami, gdyż zostaną wygenerowane automatycznie. Do tego nie importujemy przycisku, bo argumentem funkcji jest sam przycisk.

Oto kod, dzięki któremu zamienimy tekst na mowę:


GtkTextIter begin; //Beginning. NOTE: not a pointer
	GtkTextIter	end; // End. NOTE: not a pointer
	GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(Struct.input_text_to_speech));
	
	gtk_text_buffer_get_start_iter (buffer, &begin); //initialize start point
	gtk_text_buffer_get_end_iter (buffer, &end); //initialize end point

	//get text to be read by eSpeak
	const gchar* text_to_speech = gtk_text_buffer_get_text (GTK_TEXT_BUFFER(buffer), &begin, &end, FALSE);

	//get language code
	const gchar* language_code = gtk_entry_get_text(Struct.language);

	//composing a command;
	const gchar* command = g_strdup_printf("espeak \"%s\" -v %s", text_to_speech, language_code);
	
	//command to run espeak
	system(command);

Najpierw tworzymy dwie zmienne typu GtkTextIter, które będą wskazywać początek i koniec tekstu. Następnie deklarujemy bufor tekstowy i inicjalizujemy go buforem tekstu z dużego pola tekstowego. I zajmujemy się ustaleniem początku i końca tekstu przez funkcje gtk_text_buffer_get_start_iter (GtkTextBuffer*, GtkTextIter*) oraz gtk_text_buffer_get_start_iter (GtkTextBuffer*, GtkTextIter*).

Mając te dane pobierzemy do stałej tablicy znaków (const gchar*) zawartość widoku tekstowego za pomocą funkcji gtk_text_buffer_get_text(GtkTextBuffer*, GtkTextIter*, GtkTextIter*, gboolean). Pobierzemy również kod języka (niezbędny do ustalenia języka, w jakim będzie mówić eSpeak.

Napis o określonym formacie utworzymy za pomocą funkcji g_strdup_printf(const gchar*, ...) podobnie, jak to się robi przy wyświetlaniu tekstu za pomocą printf(const char*,...). Zadziała to tak, jak znane z Javy czy C# tworzenie napisu o określonym formacie.

Mając tak przygotowane polecenie (espeak "tu twój tekst" -v jezyk), mamy już sposób na wykorzystanie eSpeaka. Teraz ta zmienna, w której jest ono przechowywane, będzie argumentem polecenia system(const char*) i za jego pomocą przeczytamy tekst wprowadzony przez użytkownika.

Kompilacja i uruchomienie

Jeśli chodzi o zależności kompilacyjne, powinny wystarczyć te, które muszą zostać spełnione, aby prosty program w GTK+ mógł zostać skompilowany i działać prawidłowo. Po uruchomieniu program będzie wyglądać tak:

Program w działaniu
Program w działaniu

Kod referencyjny jest dostępny tu.

Podsumowanie

Co prawda już istnieją takie nakładki, ale nie zaszkodzi napisać własnej :) Możemy wtedy uwzględnić opcje, jakich potrzebujemy, a autorzy istniejących nie przewidzieli. Następne wpisy z tej tematyki będą omawiać ulepszenia tej oto nakładki.

Wybrane dla Ciebie
Komentarze (2)