In this tutorial we’ll create simple web browser using Python PyQt framework. As you may know PyQt is a set of Python bindings for Qt framework, and Qt (pronounced cute) is C++ framework used to create GUI-s. To be strict you can use Qt to develop programs without GUI too, but developing user interfaces is probably most common thing people do with this framework. Main benefit of Qt is that it allows you to create GUI-s that are cross platform, your apps can run on various devices using native capabilities of each platform without changing your codebase.
Qt comes with a port of webkit, which means that you can create webkit-based browser in PyQt.
Our browser will do following things:
- load urls entered by user into input box
- show all requests performed while rendering the page
Let’s start with simplest possible use case of PyQt Webkit: loading some url, opening window and rendering page in this window.
This is trivial to do, and requires around 13 lines of code (with imports and whitespace):
If you pass url to script from command line it should load this url and show rendered page in window.
Add address bar
To do this we’ll just add input box at the top of the window, user will type url into text box, browser will load this url. We will use QLineEdit widget for input box. Since we will have two elements (text input and browser frame), we’ll need to add some grid layout to our app.
Add dev tools
Of course the most interesting and important part of every browser are its dev tools. Every browser worth its name should have its developer console. Our Python browser should have some developer tools too.
Let’s add something similar to Chrome “network” tab in dev tools. We will simply keep track of all requests performed by browser engine while rendering page. Requests will be shown in table below main browser frame, for simplicity we will only log url, status code and content type of responses.
Do do this we will need to create a table first, we’ll use QTableWidget for that, header will contain field names, it will auto-resize each time new row is added to table.
To keep track of all requests we’ll need to get bit deeper into PyQt internals. Turns out that Qt exposes NetworkAccessManager class as an API allowing you to perform and monitor requests performed by application. We will need to subclass NetworkAccessManager, add event listeners we need, and tell our webkit view to use this manager to perform its requests.
First let’s create our network access manager:
I have to say that some things in Qt are not as easy and quick as they should be. Note how awkward it is to get status code from response. You have to use response method .attribute() and pass reference to class property of request. This returns QVariant not int and when you convert to int it returns tuple.
Now finally we have a table and a network access manager. We just need to wire all this together.
Now fire up your browser, enter url into input box and enjoy the view of all requests filling up table below webframe.
If you have some spare time you could add lots of new functionality here:
- add filters by content-type
- add sorting to table
- add timings
- highlight requests with errors (e.g. show them in red)
- show more info about each request - all headers, response content, method
- add option to replay requests and load them into browser frame, e.g. user clicks on request in table and this url is loaded into browser.
This is long TODO list and it would be probably interesting learning exercise to do all these things, but describing all of them would probably require to write quite a long book.
Finally let’s add one last feature to our experimental browser - ability to execute custom JavaScipt in page context.
then we instantiate it in our main clause and voila our dev tools are ready.
Moving back and forth, other page actions
Since we already connected our browser to QWebPage object we can also add other actions important for end users. Qt web page object supports lots of different actions and you can add them all to your app.
For now let’s just add support for “back”, “forward” and “reload”. You could add those actions to our GUI by adding buttons, but it will be easier to just add another text input box.
just as before you also need to create instance of ActionInputBox, pass reference to page object and add it to our GUI grid.
Full result should look somewhat like this:
For reference here’s code for final result