##// END OF EJS Templates
Fixed bug in creating error dialog for failed HTML export.
epatters -
Show More
@@ -1,238 +1,237 b''
1 1 """ Defines classes and functions for working with Qt's rich text system.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Imports
5 5 #-----------------------------------------------------------------------------
6 6
7 7 from __future__ import with_statement
8 8
9 9 # Standard library imports.
10 10 import os
11 11 import re
12 12
13 13 # System library imports.
14 14 from IPython.external.qt import QtGui
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Constants
18 18 #-----------------------------------------------------------------------------
19 19
20 20 # A regular expression for an HTML paragraph with no content.
21 21 EMPTY_P_RE = re.compile(r'<p[^/>]*>\s*</p>')
22 22
23 23 # A regular expression for matching images in rich text HTML.
24 24 # Note that this is overly restrictive, but Qt's output is predictable...
25 25 IMG_RE = re.compile(r'<img src="(?P<name>[\d]+)" />')
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Classes
29 29 #-----------------------------------------------------------------------------
30 30
31 31 class HtmlExporter(object):
32 32 """ A stateful HTML exporter for a Q(Plain)TextEdit.
33 33
34 34 This class is designed for convenient user interaction.
35 35 """
36 36
37 37 def __init__(self, control):
38 38 """ Creates an HtmlExporter for the given Q(Plain)TextEdit.
39 39 """
40 40 assert isinstance(control, (QtGui.QPlainTextEdit, QtGui.QTextEdit))
41 41 self.control = control
42 42 self.filename = 'ipython.html'
43 43 self.image_tag = None
44 44 self.inline_png = None
45 45
46 46 def export(self):
47 47 """ Displays a dialog for exporting HTML generated by Qt's rich text
48 48 system.
49 49
50 50 Returns
51 51 -------
52 52 The name of the file that was saved, or None if no file was saved.
53 53 """
54 54 parent = self.control.window()
55 55 dialog = QtGui.QFileDialog(parent, 'Save as...')
56 56 dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
57 57 filters = [
58 58 'HTML with PNG figures (*.html *.htm)',
59 59 'XHTML with inline SVG figures (*.xhtml *.xml)'
60 60 ]
61 61 dialog.setNameFilters(filters)
62 62 if self.filename:
63 63 dialog.selectFile(self.filename)
64 64 root,ext = os.path.splitext(self.filename)
65 65 if ext.lower() in ('.xml', '.xhtml'):
66 66 dialog.selectNameFilter(filters[-1])
67 67
68 68 if dialog.exec_():
69 69 self.filename = dialog.selectedFiles()[0]
70 70 choice = dialog.selectedNameFilter()
71 71 html = self.control.document().toHtml().encode('utf-8')
72 72
73 73 # Configure the exporter.
74 74 if choice.startswith('XHTML'):
75 75 exporter = export_xhtml
76 76 else:
77 77 # If there are PNGs, decide how to export them.
78 78 inline = self.inline_png
79 79 if inline is None and IMG_RE.search(html):
80 80 dialog = QtGui.QDialog(parent)
81 81 dialog.setWindowTitle('Save as...')
82 82 layout = QtGui.QVBoxLayout(dialog)
83 83 msg = "Exporting HTML with PNGs"
84 84 info = "Would you like inline PNGs (single large html " \
85 85 "file) or external image files?"
86 86 checkbox = QtGui.QCheckBox("&Don't ask again")
87 87 checkbox.setShortcut('D')
88 88 ib = QtGui.QPushButton("&Inline")
89 89 ib.setShortcut('I')
90 90 eb = QtGui.QPushButton("&External")
91 91 eb.setShortcut('E')
92 92 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
93 93 dialog.windowTitle(), msg)
94 94 box.setInformativeText(info)
95 95 box.addButton(ib, QtGui.QMessageBox.NoRole)
96 96 box.addButton(eb, QtGui.QMessageBox.YesRole)
97 97 box.setDefaultButton(ib)
98 98 layout.setSpacing(0)
99 99 layout.addWidget(box)
100 100 layout.addWidget(checkbox)
101 101 dialog.setLayout(layout)
102 102 dialog.show()
103 103 reply = box.exec_()
104 104 dialog.hide()
105 105 inline = (reply == 0)
106 106 if checkbox.checkState():
107 107 # Don't ask anymore; always use this choice.
108 108 self.inline_png = inline
109 109 exporter = lambda h, f, i: export_html(h, f, i, inline)
110 110
111 111 # Perform the export!
112 112 try:
113 113 return exporter(html, self.filename, self.image_tag)
114 114 except Exception, e:
115 title = self.window().windowTitle()
116 msg = "Error while saving to: %s\n" % filename + str(e)
117 reply = QtGui.QMessageBox.warning(parent, title, msg,
115 msg = "Error exporting HTML to %s\n" % self.filename + str(e)
116 reply = QtGui.QMessageBox.warning(parent, 'Error', msg,
118 117 QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)
119 118
120 119 return None
121 120
122 121 #-----------------------------------------------------------------------------
123 122 # Functions
124 123 #-----------------------------------------------------------------------------
125 124
126 125 def export_html(html, filename, image_tag = None, inline = True):
127 126 """ Export the contents of the ConsoleWidget as HTML.
128 127
129 128 Parameters:
130 129 -----------
131 130 html : str,
132 131 A utf-8 encoded Python string containing the Qt HTML to export.
133 132
134 133 filename : str
135 134 The file to be saved.
136 135
137 136 image_tag : callable, optional (default None)
138 137 Used to convert images. See ``default_image_tag()`` for information.
139 138
140 139 inline : bool, optional [default True]
141 140 If True, include images as inline PNGs. Otherwise, include them as
142 141 links to external PNG files, mimicking web browsers' "Web Page,
143 142 Complete" behavior.
144 143 """
145 144 if image_tag is None:
146 145 image_tag = default_image_tag
147 146
148 147 if inline:
149 148 path = None
150 149 else:
151 150 root,ext = os.path.splitext(filename)
152 151 path = root + "_files"
153 152 if os.path.isfile(path):
154 153 raise OSError("%s exists, but is not a directory." % path)
155 154
156 155 with open(filename, 'w') as f:
157 156 html = fix_html(html)
158 157 f.write(IMG_RE.sub(lambda x: image_tag(x, path = path, format = "png"),
159 158 html))
160 159
161 160
162 161 def export_xhtml(html, filename, image_tag=None):
163 162 """ Export the contents of the ConsoleWidget as XHTML with inline SVGs.
164 163
165 164 Parameters:
166 165 -----------
167 166 html : str,
168 167 A utf-8 encoded Python string containing the Qt HTML to export.
169 168
170 169 filename : str
171 170 The file to be saved.
172 171
173 172 image_tag : callable, optional (default None)
174 173 Used to convert images. See ``default_image_tag()`` for information.
175 174 """
176 175 if image_tag is None:
177 176 image_tag = default_image_tag
178 177
179 178 with open(filename, 'w') as f:
180 179 # Hack to make xhtml header -- note that we are not doing any check for
181 180 # valid XML.
182 181 offset = html.find("<html>")
183 182 assert(offset > -1)
184 183 html = ('<html xmlns="http://www.w3.org/1999/xhtml">\n'+
185 184 html[offset+6:])
186 185
187 186 html = fix_html(html)
188 187 f.write(IMG_RE.sub(lambda x: image_tag(x, path = None, format = "svg"),
189 188 html))
190 189
191 190
192 191 def default_image_tag(match, path = None, format = "png"):
193 192 """ Return (X)HTML mark-up for the image-tag given by match.
194 193
195 194 This default implementation merely removes the image, and exists mostly
196 195 for documentation purposes. More information than is present in the Qt
197 196 HTML is required to supply the images.
198 197
199 198 Parameters
200 199 ----------
201 200 match : re.SRE_Match
202 201 A match to an HTML image tag as exported by Qt, with match.group("Name")
203 202 containing the matched image ID.
204 203
205 204 path : string|None, optional [default None]
206 205 If not None, specifies a path to which supporting files may be written
207 206 (e.g., for linked images). If None, all images are to be included
208 207 inline.
209 208
210 209 format : "png"|"svg", optional [default "png"]
211 210 Format for returned or referenced images.
212 211 """
213 212 return ''
214 213
215 214
216 215 def fix_html(html):
217 216 """ Transforms a Qt-generated HTML string into a standards-compliant one.
218 217
219 218 Parameters:
220 219 -----------
221 220 html : str,
222 221 A utf-8 encoded Python string containing the Qt HTML.
223 222 """
224 223 # A UTF-8 declaration is needed for proper rendering of some characters
225 224 # (e.g., indented commands) when viewing exported HTML on a local system
226 225 # (i.e., without seeing an encoding declaration in an HTTP header).
227 226 # C.f. http://www.w3.org/International/O-charset for details.
228 227 offset = html.find('<head>')
229 228 if offset > -1:
230 229 html = (html[:offset+6]+
231 230 '\n<meta http-equiv="Content-Type" '+
232 231 'content="text/html; charset=utf-8" />\n'+
233 232 html[offset+6:])
234 233
235 234 # Replace empty paragraphs tags with line breaks.
236 235 html = re.sub(EMPTY_P_RE, '<br/>', html)
237 236
238 237 return html
General Comments 0
You need to be logged in to leave comments. Login now