Progmar Marcin Załęczny

Wykorzystanie komponentu WebKitWebView

Przygotowanie środowiska

Na początek skopiuj do nowego katalogu kod przygotowany w rozdziale 4 ("Refaktoryzacja kodu"). Następnie zapisz w tym katalogu plik interfejsu (main_window.ui) stworzony w rozdziale poprzednim (6. Tworzenie przeglądarki internetowej). Teraz wyedytuj plik main.cpp i zastąp linijkę:

window = new MainWindow(gui->getWidget("wndHelloWorld"));

linijką:

window = new MainWindow(gui->getWidget("wndMainWindow"));

Zrobiliśmy to ponieważ nasze okno główne aplikacji nie nazywa się już wndHelloWorld lecz wndMainWindow. Na koniec wyedytuj plik Makefile i ustaw nazwę aplikacji na internet_viewer. W tym celu zastąp linijkę:

APP_NAME = hello_world

linijką:

APP_NAME = internet_viewer

Na koniec skompiluj program wydając polecenie make. Powstanie aplikacja internet_viewer. Po uruchomieniu program powinien prezentować się następująco:

Empty main window

Nieźle co? Zaledwie dwie drobne zmiany w kodzie i mamy zupełnie nowy program. Na razie jednak nie jest on kompletny. Przede wszystkim nie ma komponentu przeglądarki internetowej i brak obsługi zdarzeń dla przycisków. Zacznijmy więc uzupełniać nasz kod. Na początek zainstaluj komponent przeglądarki. W tym celu wykonaj polecenie:

sudo apt-get install libwebkitgtk-3.0

Dodanie komponentu WebKitWebView

Po instalacji otwórz w geany pliki MainWindow.h i MainWindow.cpp. Do pliku MainWindow.h dodaj wytłuszczone na czarno zmiany:


#ifndef __MAIN_WINDOW_H__
#define __MAIN_WINDOW_H__

#include <gtk/gtk.h>
#include <webkit/webkit.h>
#include "Window.h"

class MainWindow : public Window {
public:
	MainWindow(GtkWidget *wnd);
	virtual ~MainWindow();
	
	static void onDestroy(GtkWidget* widget, gpointer data);
	static gboolean onDeleteEvent(GtkWidget* widget, GdkEvent* event, gpointer data);
	static gboolean closeWebView(WebKitWebView* webView, GtkWidget* window);

protected:
	WebKitWebView *webView;
};

#endif
	

Zainkludowaliśmy tu plik nagłówkowy biblioteki webkit, stworzyliśmy nagłówek funkcji statycznej closeWebView, która będzie odpowiadała za zamknięcie programu w momencie gdy nastąpiło żądanie zamknięcia okna przeglądarki (np. przez wywołanie w JavaScripcie komendy window.close) oraz zadeklarowaliśmy zmienną webView, która będzie przechowywała wskaźnik na okno przeglądarki.

Przejdźmy teraz do pliku MainWindow.cpp. Na jego końcu dodaj definicję metody closeWebView:


gboolean MainWindow::closeWebView(WebKitWebView* webView, GtkWidget* window) {
	gtk_widget_destroy(window);
}
	

Jak widzimy - niszczy ona okno główne naszego programu przekazane w parametrze window. Teraz zastąp kod konstruktora następującym kodem:


MainWindow::MainWindow(GtkWidget *wnd) 
	: Window(wnd) {	
	g_signal_connect(G_OBJECT(this->wnd), "delete-event", G_CALLBACK(MainWindow::onDeleteEvent), NULL);
	g_signal_connect(G_OBJECT(this->wnd), "destroy", G_CALLBACK(MainWindow::onDestroy), NULL);

	this->webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
	GtkWidget* scrolledWindow = gui->getWidget("scrolledWindow");
	gtk_container_add(GTK_CONTAINER(scrolledWindow), GTK_WIDGET(this->webView));
	gtk_widget_show(GTK_WIDGET(this->webView));
	g_signal_connect(this->webView, "close-web-view", G_CALLBACK(MainWindow::closeWebView), this->wnd);
	
	webkit_web_view_load_uri(this->webView, "http://www.webkitgtk.org/");
	gtk_widget_grab_focus(GTK_WIDGET(this->webView));	
}
	

W dodanym wytłuszczonym kodzie wykonujemy co następuje:

  • Tworzymy nowy obiekt komponentu WEBKIT_WEB_VIEW i wskaźnik na niego zapamiętujemy w atrybucie webView.
  • Pobieramy ze zmiennej gui wskaźnik na okno GTK_SCROLLED_WINDOW.
  • Dodajemy komponent webView do okna scrolledWindow.
  • Pokazujemy komponent webView na ekranie.
  • Podpinamy funkcję obsługi zdarzenia close-web-view przekazując w dodatkowym parametrze wskaźnik na nasze okno główne.
  • Za pomocą funkcji webkit_web_view_load_uri otwieramy w komponencie webView stronę o adresie http://www.webkitgtk.org/.
  • Przekazujemy fokus do kontrolki webView.

Przed kompilacją wyedytuj jeszcze plik Makefile i dodaj bibliotekę webkitgtk-3.0 do opcji kompilatora:


CFLAGS = `pkg-config --cflags gtk+-3.0 webkitgtk-3.0` -Wno-format-security
LIBS = `pkg-config --libs gtk+-3.0 webkitgtk-3.0`
	

Skompiluj teraz program przy pomocy poleceń: make clean i make i uruchom go. Rezultat będzie wyglądał jak na rysunku poniżej:

Webkit main window

Nawigacja po stronach w komponencie WebKitWebView

Dodajmy teraz atrybuty edtUrl oraz homePage do klasy MainWindow. Pierwszy z nich będzie przechowywał wskaźnik na pole tekstowe adresu strony a drugi będzie zawierał adres strony głównej:


protected:
	WebKitWebView *webView;
	GtkWidget* edtUrl;
	const gchar* homePage;
	

Natomiast do konstruktora klasy MainWindow dodaj kod inicjalizujący atrybut homePage, pobierający wskaźnik na pole tekstowe adresu strony oraz ustawiający zawartość pola tekstowego na aktualnie otwartą stronę główną:


gtk_widget_show(GTK_WIDGET(this->webView));
g_signal_connect(this->webView, "close-web-view", G_CALLBACK(MainWindow::closeWebView), this->wnd);

this->homePage = "http://www.webkitgtk.org/";

this->edtUrl = gui->getWidget("edtURL");
gtk_entry_set_text(GTK_ENTRY(this->edtUrl), this->homePage);

webkit_web_view_load_uri(this->webView, this->homePage);
gtk_widget_grab_focus(GTK_WIDGET(this->webView));
	

Teraz zróbmy, żeby pole tekstowe adresu strony przyjmowało poprawny adres aktualnie wyświetlany w komponencie webView. W tym celu podepnijmy funkcję obsługi zdarzenia notify::load-status pod tę kontrolkę. W pliku MainWindow.h dodaj nagłówek tej funkcji:


	static gboolean closeWebView(WebKitWebView* webView, GtkWidget* window);
	static void webViewLoadStatus(WebKitWebView *webView, GParamSpec *pspec, MainWindow* window);
	
protected:
	

Jako dodatkowy parametr przekazaliśmy wskaźnik na obiekt MainWindow, po to aby w funkcji statycznej mieć dostęp do kontrolki edtUrl. Następnie na samym końcu pliku MainWindow.cpp dodaj definicję tej funkcji:


void MainWindow::webViewLoadStatus(WebKitWebView *webView, GParamSpec *pspec, MainWindow* window) {
	WebKitLoadStatus status = webkit_web_view_get_load_status(webView);
	GObject *object = G_OBJECT(webView);
	
	g_object_freeze_notify(object);
	
	switch (status) {
		case WEBKIT_LOAD_COMMITTED:
			gtk_entry_set_text(GTK_ENTRY(window->edtUrl), webkit_web_view_get_uri(webView));
			break;
	}
	g_object_thaw_notify(object);
}
	

W funkcji tej pobieramy aktualny status kontrolki webView i na czas obsługi zdarzenia blokujemy przy pomocy funkcji g_object_freeze_notify emisję następnych sygnałów notify. Jeśli status jest równy WEBKIT_LOAD_COMMITTED, to do pola tekstowego edtUrl ładujemy aktualny adres przeglądarki. Komponent webView ma status WEBKIT_LOAD_COMMITTED wtedy gdy pobrał pierwsze bajty z nowego adresu. Na koniec przy pomocy funkcji g_object_thaw_notify odblokowujemy otrzymywanie sygnałów notify. Pozostało jeszcze podpiąć funkcję webViewLoadStatus pod sygnał notify::load-status:


g_signal_connect(this->webView, "close-web-view", G_CALLBACK(MainWindow::closeWebView), this->wnd);
g_signal_connect(this->webView, "notify::load-status", G_CALLBACK(MainWindow::webViewLoadStatus), this);

this->homePage = "http://www.webkitgtk.org/";
	

Na koniec rozdziału podepnijmy pod przycisk Przejdź funkcję ładującą podany w polu tekstowym adres strony. W tym celu stwórzmy funkcję obsługi zdarzenia click przycisku w pliku MainWindow.h:

	static void webViewLoadStatus(WebKitWebView *webView, GParamSpec *pspec, MainWindow* window);
	static void btnGoClicked(GtkWidget* button, MainWindow* window);
	
protected:
	

Następnie na końcu pliku MainWindow.cpp dodaj definicję tej funkcji:


void MainWindow::btnGoClicked(GtkWidget* button, MainWindow* window) {
	const gchar* uri = gtk_entry_get_text(GTK_ENTRY(window->edtUrl));
	webkit_web_view_load_uri(window->webView, uri);
	gtk_widget_grab_focus(GTK_WIDGET(window->webView));
}
	

W funkcji tej najpierw pobieramy zawartość pola tekstowego (gtk_entry_get_text) a następnie ładujemy stronę o tym adresie w kontrolce webView. Aby operować na kontrolkach: polu tekstowym i przeglądarce, wykorzystujemy obiekt typu MainWindow przekazany w funkcji podpinającej tę funkcję pod sygnał clicked w konstruktorze obiektu:


this->edtUrl = gui->getWidget("edtURL");
gtk_entry_set_text(GTK_ENTRY(this->edtUrl), this->homePage);

GtkWidget* btnGo = gui->getWidget("btnGo");
g_signal_connect(btnGo, "clicked", G_CALLBACK(MainWindow::btnGoClicked), this);

webkit_web_view_load_uri(this->webView, this->homePage);
gtk_widget_grab_focus(GTK_WIDGET(this->webView));
	

I to narazie tyle. Obsługą zdarzeń dla przycisków toolbara oraz pozycji menu zajmiemy się w dalszych rozdziałach tego tutorialu.