Writing my first Web Browser in Python with GTK

Installing packages

Before starting, make sure that you have installed development packages such as pygobject3-devel and webkitgtk3-devel.

Creating your first script

We are going to start from the beginning, by creating an empty window:

from gi.repository import Gtk
window = Gtk.Window()
Gtk.main()

The first line will let us import the Gtk library from gi, and the second line, will let us create a window.

Now, if you have copy-paste this code, you will see that it won’t do much… and it happens because you created the window but it has not been shown. So let’s call the method show. Even if you can see the Close Icon on the Window, it won’t work because it is not programmed to do anything. So, lets add the signal handler to our code:

def on_destroy(window):
    Gtk.main_quit()
window.connect("destroy",on_destroy)

After running, you will see that the close icon on the window will work. If you want to change the title of the window, you can do it by adding this line:

window.set_title("GUADEC 2017")

Coding a browser

Import the WebKit library as well in the first line and create a webview widget:

webview = WebKit.WebView()

Finally, we need to add the widget into the window

window.add(webview)

and call window.show_all() instead of window.show() because now our window contains a widget.As you can see, the webview is empty with a default size. We might open any website by calling it:Adding Scroll Bars

To have a better experience, we can add scroll bars. To code this, we must understand that we are going to have three objects: a window, a scrolled window, and a webview.  For this experience, I will also delete the on_destroy handler and just use the Gtk.main_quit, and add code for scrolling:Because it does not look good, let’s set an appropriate default size (800 x 600):

window.set_default_size(800,600)

GNOME has a defined an interface guidelines, so we are going to delete the title and add a header bar widget:

headerbar = Gtk.HeaderBar()
headerbar.set_show_close_button(True)
window.set_titlebar(headerbar)

We have written the method set_show_close_button in order to display the close icon of the window:Adding the URL textbox

To be able to type a URL on this Web browser, add a text entry widget:It is not able to browse any URL at all, so we can define a signal handler:

def on_enter(entry):
 url = entry.get_text()
 print(url)

The next step is to connect it:As you can see in the terminal, an URL written in the headerbar was printed. Now let’s open the page in the browser:Adding the back and forward buttons

First, we need to find the right name of the icon of the button in GNOME:Now we find the image to represent the Back icon. I’ve chosen “go-previous”:By adding these lines, we are going to be able to display the button:

go_back_button = Gtk.Button()
go_back_arrow = Gtk.Image.new_from_icon_name("go-previous", Gtk.IconSize.SMALL_TOOLBAR)
go_back_button.add(go_back_arrow)
headerbar.pack_start(go_back_button)

To make it useful, we are going to add the handler:Now, as homework, please do the forward button. Here is my picture 😉Adding historial

Create a file history.html in /tmp and paste this code inside on_enter function:

if (url == "about:history"):
 webview.open("/tmp/history.html")
 return

history_file = open("/tmp/history.html", "a+")
 history_file.writelines("* " + url + "<br>")
 history_file.close()
 webview.open(url)

Now we are able to see a list of a websites visited:Important Note before continuing

GTK3 is still working but it will be obsolete in Fedora 27. As the comments are claiming, there are some changes that must be done in this material.

1.- Replace WebKit2 instead of WebKit while it is importing or when it is used

2.- Instead of open, use load_uri for webview widget

3.- Delete creation and handlers of scrolled_window because it is not needed

4.- Replace window.add(scrolled_window) for window.add(webview)

Adding every event to the history

So far, we have save all the pages written including an ENTER at the end, but what about the pages I visited while clicking on tabs or that make the WebBrowser changed. Besides that the tmp will be lost if you reboot the machine, it is better to save the history in another path, in this case, the home. Function such as open_page and on_load_changed that can be developed and be called later while the webview will be loaded. Make sure you have defined the proper functions to make it work:

def open_page(url):
 entry.set_text(url)
 webview.load_uri(url)

def open_history(button):
 open_page("file:///home/yulytas/.browser.html")

def on_load_changed(webview, event):
 url = webview.get_uri ()
 history_file = open("/home/yulytas/.browser.html", "a+")
 history_file.writelines("* <a href=\"" + url + "\">" + url + "</a><br>")
 history_file.close()

def on_enter(entry):
 url = entry.get_text()
 webview.load_uri(url)
 if (url == "about:history"):
 open_history(webview)
 return

 open_page(url)

open_page("https://gnome.org")
webview.connect("load-changed", on_load_changed)

Present the history by clicking a button

All the code that includes saving history in the /home/yulytas/browser.html can be presented also in a history button. These are the lines required:

def open_history(button):
 open_page("file:///home/yulytas/.browser.html")

history_button = Gtk.Button()
history_button_image = Gtk.Image.new_from_icon_name("open-menu-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
history_button.add(history_button_image)
history_button.connect("clicked", open_history)

headerbar.pack_end(history_button)

Test that the links and events are satisfied by the code:Naming global variables

Finally, instead of hard-coding the path of each user home, we can use the GLib library to capture the home path of any user with the following instructions:

1.- Add the GLib library in the first line to imported.

2.- Edit the sources of the file that will contain the history:

HISTORY_FILE = "/home/" + GLib.get_user_name() + "/browser1_history.html"
HOME_PAGE = "https://gnome.org"

3.- Edit the functions defined and use the global variables already set:

def open_history(button):
 open_page("file://" + HISTORY_FILE)

def on_load_changed(webview, event):
 url = webview.get_uri ()
 history_file = open(HISTORY_FILE, "a+")
 history_file.writelines("* <a href=\"" + url + "\">" + url + "</a><br>")
 history_file.close()

4.- Call properly the handlers using the variables

open_page(HOME_PAGE)

The End:Useful dictionary of GTK and PyGobject:

1.- https://lazka.github.io/pgi-docs/#Gtk-3.0/classes/Window.html#Gtk.Window

2.- Install devHelp from the Software Center or by terminal.Special thanks

Felipe Borges for teaching me & Michael Catanzaro for the GTK/WebKit update

About Julita Inca

System Engineering degree at UNAC, Computer Science Masters at PUCP, High Performance Masters at University of Edinburgh, Winner OPW GNOME 2011, GNOME Foundation member since 2012, Fedora Ambassador since 2012, winner of the Linux Foundation scholarship 2012, Linux Admin at GMD 2012, IT Specialist at IBM 2013. Academia experience in lecturing at PUCP, USIL and UNI Peru (2010-2018). HPC intern at ORNL 2018. HPC Software Specialist at UKAEA since 2020. Tech Certifications: RHCE, RHCSA, AIX 6.1, AIX 7 Administrator, and ITILv3. Leader of LinuXatUNI Community, Creator of the "Mujeres Imperfectas | I'm perfect woman" channel, Reviewer of the Technological Magazine of ESPOL-RTE, Online trainer at BackTrackAcademy, blogger, photographer, IT-Linux-HPC-science worldwide speaker, graphic designer, researcher, content creator, press communicator... a simple mortal, just like you!
This entry was posted in FEDORA, GNOME, τεχνολογια :: Technology and tagged , , , , , , , , , . Bookmark the permalink.

15 Responses to Writing my first Web Browser in Python with GTK

  1. Stuart Axon says:

    Hi,
    Any chance you could link to your simple browser, even if just as a github gist? The world always needs more GI python examples.

    S

  2. Chris says:

    Hi, nice tutorial! You might want to use webkitgtk4 (WebKit2 engine for Gtk3) instead of webkitgtk3 (old and deprecated WebKit1 engine for Gtk3). webkitgtk3 will not be shipped with Fedora 27 (or current rawhide) any more, see https://bugzilla.redhat.com/show_bug.cgi?id=1375784 for a tracking bug and more details. In your case this should be as simple as replacing “WebKit” with “WebKit2” everywhere in your program.

    • Julita Inca says:

      Hi Chris! Thanks for reading this post! I have already added a part under the title: “Important Note”, where it is explained what you have mentioned. Thanks again.

  3. Michael Catanzaro says:

    Note that this won’t work in Fedora 27, because WebKit1 is being removed due to years of unfixed security vulnerabilities. You’ll want to use WebKit2 instead. The API is mostly the same; the main difference that affects this blog post is that you don’t use a GtkScrolledWindow anymore.

  4. Jean Figa says:

    Muy bien. Gracias.

  5. Pingback: Links 1/8/2017: PiCluster 2.0, Qubes OS 4.0 RC1, and New RHEL | Techrights

  6. Pingback: Confirming Fedora and GNOME presence in INFOSOFT 2017 | Sólo para Mí!

  7. Pingback: Julita Inca Chiroque: Confirming Fedora and GNOME presence in INFOSOFT 2017 | Fedora Colombia

  8. Jair Cano says:

    Tuve problemas al momento de import WebKit, pero se soluciona con la siguiente linea de codigo:
    # apt-get install gir1.2-webkit-3.0
    Despues de instalar el paquete corre como en el tuto

  9. taupist says:

    Thank you, I was having trouble getting things working after I upgraded to Fedora 27.

  10. Name (required) says:

    I want to use and contribute to this code but I don’t want to do it myself (pure laziness). Could you give the source code?

  11. dudu says:

    Many links have the additive “target=_blank”, because the user shall not leave the website, when
    activating the link. So the browser should be able to open the link in a new window or a new tab.
    Would it be possible to include this function into a python webkit(2) browser ?

Leave a comment