3
# [SNIPPET_NAME: Webkit Table]
4
# [SNIPPET_CATEGORIES: Webkit, PyGTK, csv]
5
# [SNIPPET_DESCRIPTION: Shows how to load tabular data into a Webkit view]
6
# [SNIPPET_AUTHOR: Bruno Girin <brunogirin@gmail.com>]
7
# [SNIPPET_LICENSE: GPL]
9
# This snippet was derived from Andy Breiner's "Webkit Button" snippet and
10
# Ryan Paul's article at Ars Technica:
11
# http://arstechnica.com/open-source/guides/2009/07/how-to-build-a-desktop-wysiwyg-editor-with-webkit-and-html-5.ars/
12
# It demonstrates how to create a HTML table from the content of a CSV file,
13
# display it in a Webkit view and handle change events from a GTK combo box to
14
# change the document's style sheet.
16
# The garish colours for the "Colourful" style were generated using:
17
# http://colorschemedesigner.com/
19
# It's Easter, so this snippet shows details about the nutritional information
20
# of chocolate, found here: http://www.chokladkultur.se/facts.htm
30
Data model class that encapsulates the content of the CSV file.
32
This class reads the content of the CSV file, stores the first row as a
33
header and the other rows as a list of list representing the content.
35
def __init__(self, csv_file):
36
reader=csv.reader(open(csv_file))
40
if reader.line_num == 1:
43
self.content.append(row)
47
View class that displays the content of the data model class.
49
This class creates a HTML table from the data held in the model class
50
and uses Webkit to display it. It also provides the user with a combo box
51
to change the style used to display the table.
53
def delete_event(self, widget, event, data=None):
54
"""Handles delete events and ignores them."""
57
def destroy(self, widget, data=None):
58
"""Handles the destroy event and quits the application."""
61
def __init__(self, file_stem, title, data):
63
Initialises the view class, creates a HTML document and wires events.
65
This is the main method in the view class. It initialises all elements
66
of the view, creates a HTML document based on the data model and wires
67
GTK and Webkit events to handling methods.
70
self.file_stem = file_stem
72
# Setup the window properties
73
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
74
self.window.set_resizable(True)
75
self.window.connect("delete_event", self.delete_event)
76
self.window.connect("destroy", self.destroy)
79
self.web = webkit.WebView()
81
# listen for clicks of links
82
self.web.connect("navigation-requested", self.on_navigation_requested)
84
# the %s will be replaced later on
110
self.tr = """<tr>{content}</tr>"""
111
self.th = """<th scope="{scope}">{content}</th>"""
112
self.td = """<td class="{hclass}">{content}</td>"""
114
self.document_body = self.create_document_body(title, data)
115
document = self.create_document('plain')
116
# tell webkit to load local html and this is where the %s will get
118
self.web.load_html_string(document, "file:///")
120
# Create the style combo box
121
combobox = gtk.combo_box_new_text()
122
combobox.append_text('Plain')
123
combobox.append_text('Business')
124
combobox.append_text('Rounded')
125
combobox.append_text('Colourful')
126
combobox.set_active(0)
127
combobox.connect('changed', self.changed_style_combo)
129
# Create a scroll area and add the webkit item
130
scroll = gtk.ScrolledWindow()
133
# Create a vbox and add the combo box and scroll area to it
135
vbox.pack_start(combobox, False) # don't expand
136
vbox.pack_start(scroll, True, True) # expand and fill
138
# add the vbox to the window and show all items on the window
139
self.window.add(vbox)
140
self.window.show_all()
141
self.window.move(0, 10)
142
self.window.resize(580, 350)
144
def create_document_body(self, title, data):
146
Create the document's body from the content of the data model.
148
This method creates the body of the document by inserting headers and
149
body row elements in the core template.
151
# Create th nodes and wrap them in tr
152
thead = self.tr.format(
154
[self.th.format(scope = 'col', content = h) for h in data.headers])
156
# Create td nodes, wrap the tr nodes and join them
157
# The expression used to set the value of hclass is derived from:
158
# http://code.activestate.com/recipes/52282-simulating-the-ternary-operator-in-python/
159
# For more details on nested list comprehensions, as used below, see:
160
# http://docs.python.org/tutorial/datastructures.html#nested-list-comprehensions
163
content = ''.join([self.td.format(
164
hclass = (i>0 and 'right' or 'left'), content = d
165
) for i, d in enumerate(l)]))
166
for l in data.content]
168
# Create the document body and return
169
return self.body.format(
170
title = title, thead = thead, tbody = tbody)
172
def create_document(self, style):
174
Create the complete document from the body and the style sheet.
176
This method creates the final document by reading the CSS style sheet
177
file and inserting it along with the document body into the template.
179
# Load the style sheet
181
'{stem}-{style}.css'.format(stem = self.file_stem, style = style), 'r')
182
# Apply to the document and return
183
return self.template.format(
184
style = f.read(), body = self.document_body)
186
def changed_style_combo(self, combobox):
188
Change the style by re-creating the document from the combo's selection.
190
This method gets the current value out of the combo box, transforms it
191
to lower case and uses the resulting value to re-create the complete
192
document with the relevant CSS style sheet. It then re-displays the
193
document using the Webkit view.
195
model = combobox.get_model()
196
index = combobox.get_active()
197
style = model[index][0].lower()
198
print 'Changing style to {style}'.format(style = model[index][0])
199
document = self.create_document(style)
200
self.web.load_html_string(document, "file:///")
202
def on_navigation_requested(self, view, frame, req, data=None):
204
Describes what to do when a href link is clicked.
206
In this case, we ignore all navigation requests, as there are no
207
clickable area in the document.
209
# As Ryan Paul stated he likes to use the prefix program:/
211
if uri.startswith("program:/"):
212
print uri.split("/")[1]
218
"""Start the main GTK thread."""
221
if __name__ == "__main__":
222
data = TableData(sys.argv[0].replace('.py', '.csv'))
224
sys.argv[0].replace('.py', ''),
225
"Chocolate's nutritional information", data)