WebKit in PyQt - rendering web pages

Check out the new site at https://rkblog.dev.

In the 4.4 release of Qt bindings to WebKit have been added. WebKit is a web page rendering engine, that is used in Safari and Google Chrome. Qt bindings are also available in PyQt and in other languages. To get qt-webkit we need just Qt and PyQt 4.4 or newer. Linux users have to check if they have "qt-webkit" installed (if qt comes in several packages). After installing you should see "QWebView" in QtDesiger, in the "Display Widgets" section.

Here is a basic QWebView usage:

#!/usr/bin/env python

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *

app = QApplication(sys.argv)

web = QWebView()
web.load(QUrl("http://google.pl"))
web.show()

sys.exit(app.exec_())
Sample result:
qtwebkit0

Functionality of the "browser" is currently low, but WebKit bindings provide a lot of goodies. The loaded page becomes QWebPage object, which has its own URL history - QWebHistory. Also QWebView can send some signals - page loading progress, link clicked in the web page and more.

Now we will make more functional example browser. We will use QtDesiger to make the UI containing buttons for: back, forward, stop and reload page, URL bar (QLineEdit) and QWebView. Here is the sample design (saved as httpWidget.ui):

qtwebkit

Now we make a Python class from that UI:
pyuic4 httpWidget.ui > httpWidget.py
And we can code the rest:
  • Connect signals with slots
    • QWebView signals (like page loading status, page title)
    • Buttons signals
  • UI look configuration
Here is the final code for the UI we designed:
# -*- coding: utf-8 -*-
import sys

from PyQt4 import QtCore, QtGui
from httpWidget import Ui_HttpWidget

class httpWidget(QtGui.QWidget):
	def __init__(self, parent=None):
		super(httpWidget, self).__init__(parent)
		self.ui = Ui_HttpWidget()
		self.ui.setupUi(self)
		
		# set margins
		l = self.layout()
		l.setMargin(0)
		self.ui.horizontalLayout.setMargin(5)
		
		# set the default page
		url = 'http://google.pl'
		self.ui.url.setText(url)
		
		# load page
		self.ui.webView.setUrl(QtCore.QUrl(url))
		
		# history buttons:
		self.ui.back.setEnabled(False)
		self.ui.next.setEnabled(False)
		
		QtCore.QObject.connect(self.ui.back,QtCore.SIGNAL("clicked()"), self.back)
		QtCore.QObject.connect(self.ui.next,QtCore.SIGNAL("clicked()"), self.next)
		QtCore.QObject.connect(self.ui.url,QtCore.SIGNAL("returnPressed()"), self.url_changed)
		QtCore.QObject.connect(self.ui.webView,QtCore.SIGNAL("linkClicked (const QUrl&)"), self.link_clicked)
		QtCore.QObject.connect(self.ui.webView,QtCore.SIGNAL("urlChanged (const QUrl&)"), self.link_clicked)
		QtCore.QObject.connect(self.ui.webView,QtCore.SIGNAL("loadProgress (int)"), self.load_progress)
		QtCore.QObject.connect(self.ui.webView,QtCore.SIGNAL("titleChanged (const QString&)"), self.title_changed)
		QtCore.QObject.connect(self.ui.reload,QtCore.SIGNAL("clicked()"), self.reload_page)
		QtCore.QObject.connect(self.ui.stop,QtCore.SIGNAL("clicked()"), self.stop_page)
		
		QtCore.QMetaObject.connectSlotsByName(self)
	
	def url_changed(self):
		"""
		Url have been changed by user
		"""
		page = self.ui.webView.page()
		history = page.history()
		if history.canGoBack():
			self.ui.back.setEnabled(True)
		else:
			self.ui.back.setEnabled(False)
		if history.canGoForward():
			self.ui.next.setEnabled(True)
		else:
			self.ui.next.setEnabled(False)
		
		url = self.ui.url.text()
		self.ui.webView.setUrl(QtCore.QUrl(url))
		
	def stop_page(self):
		"""
		Stop loading the page
		"""
		self.ui.webView.stop()
	
	def title_changed(self, title):
		"""
		Web page title changed - change the tab name
		"""
		self.setWindowTitle(title)
	
	def reload_page(self):
		"""
		Reload the web page
		"""
		self.ui.webView.setUrl(QtCore.QUrl(self.ui.url.text()))
	
	def link_clicked(self, url):
		"""
		Update the URL if a link on a web page is clicked
		"""
		page = self.ui.webView.page()
		history = page.history()
		if history.canGoBack():
			self.ui.back.setEnabled(True)
		else:
			self.ui.back.setEnabled(False)
		if history.canGoForward():
			self.ui.next.setEnabled(True)
		else:
			self.ui.next.setEnabled(False)
		
		self.ui.url.setText(url.toString())
	
	def load_progress(self, load):
		"""
		Page load progress
		"""
		if load == 100:
			self.ui.stop.setEnabled(False)
		else:
			self.ui.stop.setEnabled(True)
		
	def back(self):
		"""
		Back button clicked, go one page back
		"""
		page = self.ui.webView.page()
		history = page.history()
		history.back()
		if history.canGoBack():
			self.ui.back.setEnabled(True)
		else:
			self.ui.back.setEnabled(False)
	
	def next(self):
		"""
		Next button clicked, go to next page
		"""
		page = self.ui.webView.page()
		history = page.history()
		history.forward()
		if history.canGoForward():
			self.ui.next.setEnabled(True)
		else:
			self.ui.next.setEnabled(False)

if __name__ == "__main__":
	app = QtGui.QApplication(sys.argv)
	myapp = httpWidget()
	myapp.show()
	sys.exit(app.exec_())
Save the code in a file in the same directory as the UI file, and run it (i used run.py):
python run.py
Sample result:
qtwebkit1
In the __init__ we set the default web page, and some margins for the widgets. Next we have some slots:
  • url_changed - user changed URL in QLineEdit - get it and load the page (validation still missing)
  • stop_page - stop button clicked, stop page loading
  • title_changed - QWebView sends page title, when the page is loaded, we can use it to set the window title
  • reload_page - reload button clicked, we reload the web page
  • link_clicked - a link on the web page has been clicked, we update the QLineEdit url
  • load_progress - if the progress is 100 we can disable the stop button
  • back - back button - go one URL back in the history
  • next - next button - go one URL next in the history
We implemented basic browser UI and features in "few" lines of code. Exploring qt-webkit docs you can read about many other features it contains. Note that browser plugins (and more) are supported from Qt 4.5 (and not 4.4).

Source code

Download browser source code.
RkBlog

PyQt and GUI, 8 December 2008


Check out the new site at https://rkblog.dev.
Comment article
Comment article RkBlog main page Search RSS Contact