PySide/PyQt Tutorial: QWebView

The QWebView is a highly useful control; it allows you to display web pages from URLs, arbitrary HTML, XML with XSLT stylesheets, web pages constructed as QWebPages, and other data whose MIME types it knows how to interpret. It uses the WebKit web browser engine. WebKit is an up-to-date, standards-compliant rendering engine used by Google's Chrome, Apple's Safari, and soon the Opera browser.

Creating and Filling a QWebView

Content from a QWebView's URL

You instantiate a QWebView like any othe QWidget, with an optional parent. There are a number of ways to put content into it, however. The simplest — and possibly the most obvious — is its load method, which takes a QUrl; the simplest way to construct a QUrl is with a unicode URL string:

1

web_view.load(QUrl('http://www.www.pythoncentral.io'))

That loads Python Central's main page in the QWebView control. Equivalent would be to use the setUrl method, like so:

1

web_view.setUrl(QUrl('http://www.www.pythoncentral.io'))

Load Arbitrary HTML with QWebView

There are other interesting ways to load content into a QWebView. You can load generated HTML into it using the setHtml method. For example, you can do something like this:

1

2

3

4

5

6

7

8

9

10

11

12

html='''<html>

<head>

<title>A Sample Page</title>

</head>

<body>

<h1>Hello, World!</h1>

<hr />

I have nothing to say.

</body>

</html>'''

web_view.setHtml(html)

The setHtml method can take an optional second argument, the base URL of the document - the URL based on which any relative links contained in the document are resolved.

Other QWebView Content Types

A QWebView's content does not have to be HTML; if you have other browser-viewable content, you can put it in a QWebView using its setContent method, which accepts the content and optionally its MIME type and a base URL. If the MIME type is omitted, it will be assumed that it is text/html; no autodetection of MIME types has been implemented yet, though it is at least tentatively planned. I've not managed to find a list of MIME types that can be handled by the QWebView, but here is an example that works with a PNG file:

1

2

3

4

5

6

7

8

app=QApplication([])

win=QWebView()

img=open('myImage.png','rb').read()

win.setContent(img,'image/png')

win.show()

app.exec_()

Interacting QWebView with JavaScript

Presenting a user with web-style content is useful in itself, but that content can be made interactive with JavaScript, which can be initiated from Python code.

A QWebView contains a QWebFrame object, which is useful to us right now for its evaluateJavaScript method. That method accepts a string of JavaScript, evaluates it in the context of the QWebView's content, and returns its value.

What values can be returned? The PySide documentation for QWebFrame, like that for PyQt and Qt itself, is not clear on that point. In fact, that information doesn't appear to be available on the web at all, so I did some testing.

It appears that strings and booleans just plain work, and numbers, objects, undefined, and null work with caveats:

Objects are returned as Python dictionaries unless they are functions or arrays; functions are returned as useless empty dictionaries, and arrays become Python lists.

undefined becomes None, sensibly enough.

null becomes, less sensibly, ''. That's right — an empty string.

Note especially the behavior regarding null and functions, as both can cause code that looks right to behave wrong. I see no better option for functions, but null is especially confusing; the only way to detect a null value from evaluateJavaScript is to do the comparison val === null on the JavaScript side before you return it to Python. (It is at this point that we collectively grieve over JavaScript's ill-thought-out types.)

An important caution about evaluateJavaScript: it has all the security implications of JavaScript's built-in eval, and should be used with the discretion that is so seldom displayed by front-end JavaScript coders. It would be far too simple, for example, to allow the execution of arbitrary JavaScript by naïvely building a string and sending it to evaluateJavaScript. Be careful, validate user input, and block anything that looks too clever.

Example of Evaluating JavaScript in a QWebView

Now, let's throw caution to the wind and look at a simple example. It will show a form that allows the user to enter a first and last name. There will be a full name entry that is disabled; the user cannot edit it. There is a submit button, but it is hidden. (Don't do this in real life, okay? This form is a usability and cultural-sensitivity disaster, and would be almost insultingly dumb to show in public.) We'll supply a Qt button that fills out the full name entry, shows the submit button, and prints the full name to the console. Here's the source of the example:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

# Create an application

app=QApplication([])

# And a window

win=QWidget()

win.setWindowTitle('QWebView Interactive Demo')

# And give it a layout

layout=QVBoxLayout()

win.setLayout(layout)

# Create and fill a QWebView

view=QWebView()

view.setHtml('''

<html>

<head>

<title>A Demo Page</title>

<script language="javascript">

// Completes the full-name control and

// shows the submit button

function completeAndReturnName() {

var fname = document.getElementById('fname').value;

var lname = document.getElementById('lname').value;

var full = fname + ' ' + lname;

document.getElementById('fullname').value = full;

document.getElementById('submit-btn').style.display = 'block';

return full;

}

</script>

</head>

<body>

<form>

<label for="fname">First name:</label>

<input type="text" name="fname" id="fname"></input>

<br />

<label for="lname">Last name:</label>

<input type="text" name="lname" id="lname"></input>

<br />

<label for="fullname">Full name:</label>

<input disabled type="text" name="fullname" id="fullname"></input>

<br />

<input style="display: none;" type="submit" id="submit-btn"></input>

</form>

</body>

</html>

''')

# A button to call our JavaScript

button=QPushButton('Set Full Name')

# Interact with the HTML page by calling the completeAndReturnName

# function; print its return value to the console

defcomplete_name():

frame=view.page().mainFrame()

printframe.evaluateJavaScript('completeAndReturnName();')

# Connect 'complete_name' to the button's 'clicked' signal

button.clicked.connect(complete_name)

# Add the QWebView and button to the layout

layout.addWidget(view)

layout.addWidget(button)

# Show the window and run the app

win.show()

app.exec_()

Try running it. Fill out a first and last name and click the button. You should see a full name and a submit button appear. Looking at the console, you should see the full name printed there as well.

This was a very heavily contrived example, but we can do much more interesting things: in the next installment, we'll build a simple application that combines HTML in a QWebView and some other Qt widgets to usefully work with a web API.

Great tutorial for beginners. I went through the whole tutorial and it helped me a lot.

I just want to know how do you highlight the dot members and the QT class names as you show in code snippets. Can any text editor or IDE do these? Or with some plugins? I have tried vim and pydev, neither of them work in that way.

http://jacksonc.com Jackson Cooper

Thanks, good to hear :-). I use a JavaScript based plugin, but it’s not for writing code, only highlighting.
Hmm, I’ve heard PyDev is great, and it does have PyQt and PySide syntax highlighting and code auto completion.

What a fantastic tutorial series: thank you! Really clear, made the clouds part for a topic I’d been finding difficult.

One thing to add is that I think it’s worth mentioning that you need :

from PySide.QtWebKit import *

in addition to the imports from previous parts of the tutorial in order to use the QWebView() method.

Eric

Wonderful series, just read once very quickly, will now go through and do it slowly. Is there a part 9, as you wrote: “in the next installment, we’ll build a simple application that combines HTML in a QWebView and some other Qt widgets to usefully work with a web API”. Sounds pretty awesome!

Vishnu A Venu

Jason please Help me how can i create chat box like we have in Facebook or Whatsapp in which Text area consist of Sender Image and text surrounded by Color! how u understood!

aeroaks

Thanks for the article! I have used setContent(data, contentType) to set the modified (downloaded) HTML in the window. But the images (from image tag, src being external link) dont show up. Observing the requests shows that there is no request made after the HTML request which was modified and used with setContent. What am I missing here? Please help.

http://jacksonc.com Jackson Cooper

Are there any other non-image HTTP requests that do work, like CSS or JS files? And is the markup valid?