~flimm/python-snippets/gtkcrashhandler

« back to all changes in this revision

Viewing changes to webkit/webkit_table.py

  • Committer: Jono Bacon
  • Date: 2010-04-10 19:51:48 UTC
  • mfrom: (81.5.2 webkit-easter-html)
  • Revision ID: jono@ubuntu.com-20100410195148-z096w3p21ko82ksb
Awesome webkit table example.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
#
 
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]
 
8
#
 
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.
 
15
#
 
16
# The garish colours for the "Colourful" style were generated using:
 
17
# http://colorschemedesigner.com/
 
18
#
 
19
# It's Easter, so this snippet shows details about the nutritional information
 
20
# of chocolate, found here: http://www.chokladkultur.se/facts.htm
 
21
 
 
22
import csv
 
23
import sys
 
24
 
 
25
import gtk
 
26
import webkit
 
27
 
 
28
class TableData:
 
29
    """
 
30
    Data model class that encapsulates the content of the CSV file.
 
31
    
 
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.
 
34
    """
 
35
    def __init__(self, csv_file):
 
36
        reader=csv.reader(open(csv_file))
 
37
        self.headers = []
 
38
        self.content = []
 
39
        for row in reader:
 
40
            if reader.line_num == 1:
 
41
                self.headers = row
 
42
            else:
 
43
                self.content.append(row)
 
44
 
 
45
class TableView:
 
46
    """
 
47
    View class that displays the content of the data model class.
 
48
    
 
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.
 
52
    """
 
53
    def delete_event(self, widget, event, data=None):
 
54
        """Handles delete events and ignores them."""
 
55
        return False
 
56
 
 
57
    def destroy(self, widget, data=None):
 
58
        """Handles the destroy event and quits the application."""
 
59
        gtk.main_quit()
 
60
 
 
61
    def __init__(self, file_stem, title, data):
 
62
        """
 
63
        Initialises the view class, creates a HTML document and wires events.
 
64
        
 
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.
 
68
        """
 
69
        # Store the file stem
 
70
        self.file_stem = file_stem
 
71
        
 
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)
 
77
 
 
78
        # Initialize webkit
 
79
        self.web = webkit.WebView()
 
80
        
 
81
        # listen for clicks of links
 
82
        self.web.connect("navigation-requested", self.on_navigation_requested)
 
83
 
 
84
        # the %s will be replaced later on
 
85
        self.template = """
 
86
            <html>
 
87
            <head>
 
88
                <style>
 
89
                {style}
 
90
                </style>
 
91
            </head>
 
92
            {body}
 
93
            </html>
 
94
        """
 
95
        self.body = """
 
96
            <body>
 
97
                <h1>{title}</h1>
 
98
                <div id="content">
 
99
                    <table>
 
100
                        <thead>
 
101
                        {thead}
 
102
                        </thead>
 
103
                        <tbody>
 
104
                        {tbody}
 
105
                        </tbody>
 
106
                    </table>
 
107
                </div>
 
108
            </body>
 
109
        """
 
110
        self.tr = """<tr>{content}</tr>"""
 
111
        self.th = """<th scope="{scope}">{content}</th>"""
 
112
        self.td = """<td class="{hclass}">{content}</td>"""
 
113
 
 
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
 
117
        # replaced
 
118
        self.web.load_html_string(document, "file:///")
 
119
 
 
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)
 
128
        
 
129
        # Create a scroll area and add the webkit item
 
130
        scroll = gtk.ScrolledWindow()
 
131
        scroll.add(self.web)
 
132
        
 
133
        # Create a vbox and add the combo box and scroll area to it
 
134
        vbox = gtk.VBox()
 
135
        vbox.pack_start(combobox, False)    # don't expand
 
136
        vbox.pack_start(scroll, True, True) # expand and fill
 
137
 
 
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)
 
143
 
 
144
    def create_document_body(self, title, data):
 
145
        """
 
146
        Create the document's body from the content of the data model.
 
147
        
 
148
        This method creates the body of the document by inserting headers and
 
149
        body row elements in the core template.
 
150
        """
 
151
        # Create th nodes and wrap them in tr
 
152
        thead = self.tr.format(
 
153
            content = ''.join(
 
154
                [self.th.format(scope = 'col', content = h) for h in data.headers])
 
155
        )
 
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
 
161
        tbody = '\n'.join(
 
162
            [self.tr.format(
 
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]
 
167
        )
 
168
        # Create the document body and return
 
169
        return self.body.format(
 
170
            title = title, thead = thead, tbody = tbody)
 
171
    
 
172
    def create_document(self, style):
 
173
        """
 
174
        Create the complete document from the body and the style sheet.
 
175
        
 
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.
 
178
        """
 
179
        # Load the style sheet
 
180
        f = open(
 
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)
 
185
 
 
186
    def changed_style_combo(self, combobox):
 
187
        """
 
188
        Change the style by re-creating the document from the combo's selection.
 
189
        
 
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.
 
194
        """
 
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:///")
 
201
    
 
202
    def on_navigation_requested(self, view, frame, req, data=None):
 
203
        """
 
204
        Describes what to do when a href link is clicked.
 
205
        
 
206
        In this case, we ignore all navigation requests, as there are no
 
207
        clickable area in the document.
 
208
        """
 
209
        # As Ryan Paul stated he likes to use the prefix program:/
 
210
        uri = req.get_uri()
 
211
        if uri.startswith("program:/"):
 
212
            print uri.split("/")[1]
 
213
        else: 
 
214
            return False
 
215
        return True
 
216
  
 
217
    def main(self):
 
218
        """Start the main GTK thread."""
 
219
        gtk.main()
 
220
 
 
221
if __name__ == "__main__":
 
222
    data = TableData(sys.argv[0].replace('.py', '.csv'))
 
223
    view = TableView(
 
224
        sys.argv[0].replace('.py', ''),
 
225
        "Chocolate's nutritional information", data)
 
226
    view.main()