##// END OF EJS Templates
log failure to render latex...
Min RK -
Show More
@@ -1,344 +1,348 b''
1 # Copyright (c) IPython Development Team.
1 # Copyright (c) IPython Development Team.
2 # Distributed under the terms of the Modified BSD License.
2 # Distributed under the terms of the Modified BSD License.
3
3
4 from base64 import decodestring
4 from base64 import decodestring
5 import os
5 import os
6 import re
6 import re
7
7
8 from IPython.external.qt import QtCore, QtGui
8 from IPython.external.qt import QtCore, QtGui
9
9
10 from IPython.lib.latextools import latex_to_png
10 from IPython.lib.latextools import latex_to_png
11 from IPython.utils.path import ensure_dir_exists
11 from IPython.utils.path import ensure_dir_exists
12 from IPython.utils.traitlets import Bool
12 from IPython.utils.traitlets import Bool
13 from IPython.qt.svg import save_svg, svg_to_clipboard, svg_to_image
13 from IPython.qt.svg import save_svg, svg_to_clipboard, svg_to_image
14 from .ipython_widget import IPythonWidget
14 from .ipython_widget import IPythonWidget
15
15
16
16
17 class RichIPythonWidget(IPythonWidget):
17 class RichIPythonWidget(IPythonWidget):
18 """ An IPythonWidget that supports rich text, including lists, images, and
18 """ An IPythonWidget that supports rich text, including lists, images, and
19 tables. Note that raw performance will be reduced compared to the plain
19 tables. Note that raw performance will be reduced compared to the plain
20 text version.
20 text version.
21 """
21 """
22
22
23 # RichIPythonWidget protected class variables.
23 # RichIPythonWidget protected class variables.
24 _payload_source_plot = 'IPython.kernel.zmq.pylab.backend_payload.add_plot_payload'
24 _payload_source_plot = 'IPython.kernel.zmq.pylab.backend_payload.add_plot_payload'
25 _jpg_supported = Bool(False)
25 _jpg_supported = Bool(False)
26
26
27 # Used to determine whether a given html export attempt has already
27 # Used to determine whether a given html export attempt has already
28 # displayed a warning about being unable to convert a png to svg.
28 # displayed a warning about being unable to convert a png to svg.
29 _svg_warning_displayed = False
29 _svg_warning_displayed = False
30
30
31 #---------------------------------------------------------------------------
31 #---------------------------------------------------------------------------
32 # 'object' interface
32 # 'object' interface
33 #---------------------------------------------------------------------------
33 #---------------------------------------------------------------------------
34
34
35 def __init__(self, *args, **kw):
35 def __init__(self, *args, **kw):
36 """ Create a RichIPythonWidget.
36 """ Create a RichIPythonWidget.
37 """
37 """
38 kw['kind'] = 'rich'
38 kw['kind'] = 'rich'
39 super(RichIPythonWidget, self).__init__(*args, **kw)
39 super(RichIPythonWidget, self).__init__(*args, **kw)
40
40
41 # Configure the ConsoleWidget HTML exporter for our formats.
41 # Configure the ConsoleWidget HTML exporter for our formats.
42 self._html_exporter.image_tag = self._get_image_tag
42 self._html_exporter.image_tag = self._get_image_tag
43
43
44 # Dictionary for resolving document resource names to SVG data.
44 # Dictionary for resolving document resource names to SVG data.
45 self._name_to_svg_map = {}
45 self._name_to_svg_map = {}
46
46
47 # Do we support jpg ?
47 # Do we support jpg ?
48 # it seems that sometime jpg support is a plugin of QT, so try to assume
48 # it seems that sometime jpg support is a plugin of QT, so try to assume
49 # it is not always supported.
49 # it is not always supported.
50 _supported_format = map(str, QtGui.QImageReader.supportedImageFormats())
50 _supported_format = map(str, QtGui.QImageReader.supportedImageFormats())
51 self._jpg_supported = 'jpeg' in _supported_format
51 self._jpg_supported = 'jpeg' in _supported_format
52
52
53
53
54 #---------------------------------------------------------------------------
54 #---------------------------------------------------------------------------
55 # 'ConsoleWidget' public interface overides
55 # 'ConsoleWidget' public interface overides
56 #---------------------------------------------------------------------------
56 #---------------------------------------------------------------------------
57
57
58 def export_html(self):
58 def export_html(self):
59 """ Shows a dialog to export HTML/XML in various formats.
59 """ Shows a dialog to export HTML/XML in various formats.
60
60
61 Overridden in order to reset the _svg_warning_displayed flag prior
61 Overridden in order to reset the _svg_warning_displayed flag prior
62 to the export running.
62 to the export running.
63 """
63 """
64 self._svg_warning_displayed = False
64 self._svg_warning_displayed = False
65 super(RichIPythonWidget, self).export_html()
65 super(RichIPythonWidget, self).export_html()
66
66
67
67
68 #---------------------------------------------------------------------------
68 #---------------------------------------------------------------------------
69 # 'ConsoleWidget' protected interface
69 # 'ConsoleWidget' protected interface
70 #---------------------------------------------------------------------------
70 #---------------------------------------------------------------------------
71
71
72 def _context_menu_make(self, pos):
72 def _context_menu_make(self, pos):
73 """ Reimplemented to return a custom context menu for images.
73 """ Reimplemented to return a custom context menu for images.
74 """
74 """
75 format = self._control.cursorForPosition(pos).charFormat()
75 format = self._control.cursorForPosition(pos).charFormat()
76 name = format.stringProperty(QtGui.QTextFormat.ImageName)
76 name = format.stringProperty(QtGui.QTextFormat.ImageName)
77 if name:
77 if name:
78 menu = QtGui.QMenu()
78 menu = QtGui.QMenu()
79
79
80 menu.addAction('Copy Image', lambda: self._copy_image(name))
80 menu.addAction('Copy Image', lambda: self._copy_image(name))
81 menu.addAction('Save Image As...', lambda: self._save_image(name))
81 menu.addAction('Save Image As...', lambda: self._save_image(name))
82 menu.addSeparator()
82 menu.addSeparator()
83
83
84 svg = self._name_to_svg_map.get(name, None)
84 svg = self._name_to_svg_map.get(name, None)
85 if svg is not None:
85 if svg is not None:
86 menu.addSeparator()
86 menu.addSeparator()
87 menu.addAction('Copy SVG', lambda: svg_to_clipboard(svg))
87 menu.addAction('Copy SVG', lambda: svg_to_clipboard(svg))
88 menu.addAction('Save SVG As...',
88 menu.addAction('Save SVG As...',
89 lambda: save_svg(svg, self._control))
89 lambda: save_svg(svg, self._control))
90 else:
90 else:
91 menu = super(RichIPythonWidget, self)._context_menu_make(pos)
91 menu = super(RichIPythonWidget, self)._context_menu_make(pos)
92 return menu
92 return menu
93
93
94 #---------------------------------------------------------------------------
94 #---------------------------------------------------------------------------
95 # 'BaseFrontendMixin' abstract interface
95 # 'BaseFrontendMixin' abstract interface
96 #---------------------------------------------------------------------------
96 #---------------------------------------------------------------------------
97 def _pre_image_append(self, msg, prompt_number):
97 def _pre_image_append(self, msg, prompt_number):
98 """ Append the Out[] prompt and make the output nicer
98 """ Append the Out[] prompt and make the output nicer
99
99
100 Shared code for some the following if statement
100 Shared code for some the following if statement
101 """
101 """
102 self.log.debug("execute_result: %s", msg.get('content', ''))
102 self.log.debug("execute_result: %s", msg.get('content', ''))
103 self._append_plain_text(self.output_sep, True)
103 self._append_plain_text(self.output_sep, True)
104 self._append_html(self._make_out_prompt(prompt_number), True)
104 self._append_html(self._make_out_prompt(prompt_number), True)
105 self._append_plain_text('\n', True)
105 self._append_plain_text('\n', True)
106
106
107 def _handle_execute_result(self, msg):
107 def _handle_execute_result(self, msg):
108 """ Overridden to handle rich data types, like SVG.
108 """ Overridden to handle rich data types, like SVG.
109 """
109 """
110 if self.include_output(msg):
110 if self.include_output(msg):
111 self.flush_clearoutput()
111 self.flush_clearoutput()
112 content = msg['content']
112 content = msg['content']
113 prompt_number = content.get('execution_count', 0)
113 prompt_number = content.get('execution_count', 0)
114 data = content['data']
114 data = content['data']
115 metadata = msg['content']['metadata']
115 metadata = msg['content']['metadata']
116 if 'image/svg+xml' in data:
116 if 'image/svg+xml' in data:
117 self._pre_image_append(msg, prompt_number)
117 self._pre_image_append(msg, prompt_number)
118 self._append_svg(data['image/svg+xml'], True)
118 self._append_svg(data['image/svg+xml'], True)
119 self._append_html(self.output_sep2, True)
119 self._append_html(self.output_sep2, True)
120 elif 'image/png' in data:
120 elif 'image/png' in data:
121 self._pre_image_append(msg, prompt_number)
121 self._pre_image_append(msg, prompt_number)
122 png = decodestring(data['image/png'].encode('ascii'))
122 png = decodestring(data['image/png'].encode('ascii'))
123 self._append_png(png, True, metadata=metadata.get('image/png', None))
123 self._append_png(png, True, metadata=metadata.get('image/png', None))
124 self._append_html(self.output_sep2, True)
124 self._append_html(self.output_sep2, True)
125 elif 'image/jpeg' in data and self._jpg_supported:
125 elif 'image/jpeg' in data and self._jpg_supported:
126 self._pre_image_append(msg, prompt_number)
126 self._pre_image_append(msg, prompt_number)
127 jpg = decodestring(data['image/jpeg'].encode('ascii'))
127 jpg = decodestring(data['image/jpeg'].encode('ascii'))
128 self._append_jpg(jpg, True, metadata=metadata.get('image/jpeg', None))
128 self._append_jpg(jpg, True, metadata=metadata.get('image/jpeg', None))
129 self._append_html(self.output_sep2, True)
129 self._append_html(self.output_sep2, True)
130 elif 'text/latex' in data:
130 elif 'text/latex' in data:
131 self._pre_image_append(msg, prompt_number)
131 self._pre_image_append(msg, prompt_number)
132 png = latex_to_png(data['text/latex'], wrap=False)
132 try:
133 png = latex_to_png(data['text/latex'], wrap=False)
134 except Exception:
135 self.log.error("Failed to render latex: %r", data['text/latex'], exc_info=True)
136 png = None
133 if png is not None:
137 if png is not None:
134 self._append_png(png, True)
138 self._append_png(png, True)
135 self._append_html(self.output_sep2, True)
139 self._append_html(self.output_sep2, True)
136 else:
140 else:
137 # Print plain text if png can't be generated
141 # Print plain text if png can't be generated
138 return super(RichIPythonWidget, self)._handle_execute_result(msg)
142 return super(RichIPythonWidget, self)._handle_execute_result(msg)
139 else:
143 else:
140 # Default back to the plain text representation.
144 # Default back to the plain text representation.
141 return super(RichIPythonWidget, self)._handle_execute_result(msg)
145 return super(RichIPythonWidget, self)._handle_execute_result(msg)
142
146
143 def _handle_display_data(self, msg):
147 def _handle_display_data(self, msg):
144 """ Overridden to handle rich data types, like SVG.
148 """ Overridden to handle rich data types, like SVG.
145 """
149 """
146 if self.include_output(msg):
150 if self.include_output(msg):
147 self.flush_clearoutput()
151 self.flush_clearoutput()
148 data = msg['content']['data']
152 data = msg['content']['data']
149 metadata = msg['content']['metadata']
153 metadata = msg['content']['metadata']
150 # Try to use the svg or html representations.
154 # Try to use the svg or html representations.
151 # FIXME: Is this the right ordering of things to try?
155 # FIXME: Is this the right ordering of things to try?
152 if 'image/svg+xml' in data:
156 if 'image/svg+xml' in data:
153 self.log.debug("display: %s", msg.get('content', ''))
157 self.log.debug("display: %s", msg.get('content', ''))
154 svg = data['image/svg+xml']
158 svg = data['image/svg+xml']
155 self._append_svg(svg, True)
159 self._append_svg(svg, True)
156 elif 'image/png' in data:
160 elif 'image/png' in data:
157 self.log.debug("display: %s", msg.get('content', ''))
161 self.log.debug("display: %s", msg.get('content', ''))
158 # PNG data is base64 encoded as it passes over the network
162 # PNG data is base64 encoded as it passes over the network
159 # in a JSON structure so we decode it.
163 # in a JSON structure so we decode it.
160 png = decodestring(data['image/png'].encode('ascii'))
164 png = decodestring(data['image/png'].encode('ascii'))
161 self._append_png(png, True, metadata=metadata.get('image/png', None))
165 self._append_png(png, True, metadata=metadata.get('image/png', None))
162 elif 'image/jpeg' in data and self._jpg_supported:
166 elif 'image/jpeg' in data and self._jpg_supported:
163 self.log.debug("display: %s", msg.get('content', ''))
167 self.log.debug("display: %s", msg.get('content', ''))
164 jpg = decodestring(data['image/jpeg'].encode('ascii'))
168 jpg = decodestring(data['image/jpeg'].encode('ascii'))
165 self._append_jpg(jpg, True, metadata=metadata.get('image/jpeg', None))
169 self._append_jpg(jpg, True, metadata=metadata.get('image/jpeg', None))
166 else:
170 else:
167 # Default back to the plain text representation.
171 # Default back to the plain text representation.
168 return super(RichIPythonWidget, self)._handle_display_data(msg)
172 return super(RichIPythonWidget, self)._handle_display_data(msg)
169
173
170 #---------------------------------------------------------------------------
174 #---------------------------------------------------------------------------
171 # 'RichIPythonWidget' protected interface
175 # 'RichIPythonWidget' protected interface
172 #---------------------------------------------------------------------------
176 #---------------------------------------------------------------------------
173
177
174 def _append_jpg(self, jpg, before_prompt=False, metadata=None):
178 def _append_jpg(self, jpg, before_prompt=False, metadata=None):
175 """ Append raw JPG data to the widget."""
179 """ Append raw JPG data to the widget."""
176 self._append_custom(self._insert_jpg, jpg, before_prompt, metadata=metadata)
180 self._append_custom(self._insert_jpg, jpg, before_prompt, metadata=metadata)
177
181
178 def _append_png(self, png, before_prompt=False, metadata=None):
182 def _append_png(self, png, before_prompt=False, metadata=None):
179 """ Append raw PNG data to the widget.
183 """ Append raw PNG data to the widget.
180 """
184 """
181 self._append_custom(self._insert_png, png, before_prompt, metadata=metadata)
185 self._append_custom(self._insert_png, png, before_prompt, metadata=metadata)
182
186
183 def _append_svg(self, svg, before_prompt=False):
187 def _append_svg(self, svg, before_prompt=False):
184 """ Append raw SVG data to the widget.
188 """ Append raw SVG data to the widget.
185 """
189 """
186 self._append_custom(self._insert_svg, svg, before_prompt)
190 self._append_custom(self._insert_svg, svg, before_prompt)
187
191
188 def _add_image(self, image):
192 def _add_image(self, image):
189 """ Adds the specified QImage to the document and returns a
193 """ Adds the specified QImage to the document and returns a
190 QTextImageFormat that references it.
194 QTextImageFormat that references it.
191 """
195 """
192 document = self._control.document()
196 document = self._control.document()
193 name = str(image.cacheKey())
197 name = str(image.cacheKey())
194 document.addResource(QtGui.QTextDocument.ImageResource,
198 document.addResource(QtGui.QTextDocument.ImageResource,
195 QtCore.QUrl(name), image)
199 QtCore.QUrl(name), image)
196 format = QtGui.QTextImageFormat()
200 format = QtGui.QTextImageFormat()
197 format.setName(name)
201 format.setName(name)
198 return format
202 return format
199
203
200 def _copy_image(self, name):
204 def _copy_image(self, name):
201 """ Copies the ImageResource with 'name' to the clipboard.
205 """ Copies the ImageResource with 'name' to the clipboard.
202 """
206 """
203 image = self._get_image(name)
207 image = self._get_image(name)
204 QtGui.QApplication.clipboard().setImage(image)
208 QtGui.QApplication.clipboard().setImage(image)
205
209
206 def _get_image(self, name):
210 def _get_image(self, name):
207 """ Returns the QImage stored as the ImageResource with 'name'.
211 """ Returns the QImage stored as the ImageResource with 'name'.
208 """
212 """
209 document = self._control.document()
213 document = self._control.document()
210 image = document.resource(QtGui.QTextDocument.ImageResource,
214 image = document.resource(QtGui.QTextDocument.ImageResource,
211 QtCore.QUrl(name))
215 QtCore.QUrl(name))
212 return image
216 return image
213
217
214 def _get_image_tag(self, match, path = None, format = "png"):
218 def _get_image_tag(self, match, path = None, format = "png"):
215 """ Return (X)HTML mark-up for the image-tag given by match.
219 """ Return (X)HTML mark-up for the image-tag given by match.
216
220
217 Parameters
221 Parameters
218 ----------
222 ----------
219 match : re.SRE_Match
223 match : re.SRE_Match
220 A match to an HTML image tag as exported by Qt, with
224 A match to an HTML image tag as exported by Qt, with
221 match.group("Name") containing the matched image ID.
225 match.group("Name") containing the matched image ID.
222
226
223 path : string|None, optional [default None]
227 path : string|None, optional [default None]
224 If not None, specifies a path to which supporting files may be
228 If not None, specifies a path to which supporting files may be
225 written (e.g., for linked images). If None, all images are to be
229 written (e.g., for linked images). If None, all images are to be
226 included inline.
230 included inline.
227
231
228 format : "png"|"svg"|"jpg", optional [default "png"]
232 format : "png"|"svg"|"jpg", optional [default "png"]
229 Format for returned or referenced images.
233 Format for returned or referenced images.
230 """
234 """
231 if format in ("png","jpg"):
235 if format in ("png","jpg"):
232 try:
236 try:
233 image = self._get_image(match.group("name"))
237 image = self._get_image(match.group("name"))
234 except KeyError:
238 except KeyError:
235 return "<b>Couldn't find image %s</b>" % match.group("name")
239 return "<b>Couldn't find image %s</b>" % match.group("name")
236
240
237 if path is not None:
241 if path is not None:
238 ensure_dir_exists(path)
242 ensure_dir_exists(path)
239 relpath = os.path.basename(path)
243 relpath = os.path.basename(path)
240 if image.save("%s/qt_img%s.%s" % (path, match.group("name"), format),
244 if image.save("%s/qt_img%s.%s" % (path, match.group("name"), format),
241 "PNG"):
245 "PNG"):
242 return '<img src="%s/qt_img%s.%s">' % (relpath,
246 return '<img src="%s/qt_img%s.%s">' % (relpath,
243 match.group("name"),format)
247 match.group("name"),format)
244 else:
248 else:
245 return "<b>Couldn't save image!</b>"
249 return "<b>Couldn't save image!</b>"
246 else:
250 else:
247 ba = QtCore.QByteArray()
251 ba = QtCore.QByteArray()
248 buffer_ = QtCore.QBuffer(ba)
252 buffer_ = QtCore.QBuffer(ba)
249 buffer_.open(QtCore.QIODevice.WriteOnly)
253 buffer_.open(QtCore.QIODevice.WriteOnly)
250 image.save(buffer_, format.upper())
254 image.save(buffer_, format.upper())
251 buffer_.close()
255 buffer_.close()
252 return '<img src="data:image/%s;base64,\n%s\n" />' % (
256 return '<img src="data:image/%s;base64,\n%s\n" />' % (
253 format,re.sub(r'(.{60})',r'\1\n',str(ba.toBase64())))
257 format,re.sub(r'(.{60})',r'\1\n',str(ba.toBase64())))
254
258
255 elif format == "svg":
259 elif format == "svg":
256 try:
260 try:
257 svg = str(self._name_to_svg_map[match.group("name")])
261 svg = str(self._name_to_svg_map[match.group("name")])
258 except KeyError:
262 except KeyError:
259 if not self._svg_warning_displayed:
263 if not self._svg_warning_displayed:
260 QtGui.QMessageBox.warning(self, 'Error converting PNG to SVG.',
264 QtGui.QMessageBox.warning(self, 'Error converting PNG to SVG.',
261 'Cannot convert PNG images to SVG, export with PNG figures instead. '
265 'Cannot convert PNG images to SVG, export with PNG figures instead. '
262 'If you want to export matplotlib figures as SVG, add '
266 'If you want to export matplotlib figures as SVG, add '
263 'to your ipython config:\n\n'
267 'to your ipython config:\n\n'
264 '\tc.InlineBackend.figure_format = \'svg\'\n\n'
268 '\tc.InlineBackend.figure_format = \'svg\'\n\n'
265 'And regenerate the figures.',
269 'And regenerate the figures.',
266 QtGui.QMessageBox.Ok)
270 QtGui.QMessageBox.Ok)
267 self._svg_warning_displayed = True
271 self._svg_warning_displayed = True
268 return ("<b>Cannot convert PNG images to SVG.</b> "
272 return ("<b>Cannot convert PNG images to SVG.</b> "
269 "You must export this session with PNG images. "
273 "You must export this session with PNG images. "
270 "If you want to export matplotlib figures as SVG, add to your config "
274 "If you want to export matplotlib figures as SVG, add to your config "
271 "<span>c.InlineBackend.figure_format = 'svg'</span> "
275 "<span>c.InlineBackend.figure_format = 'svg'</span> "
272 "and regenerate the figures.")
276 "and regenerate the figures.")
273
277
274 # Not currently checking path, because it's tricky to find a
278 # Not currently checking path, because it's tricky to find a
275 # cross-browser way to embed external SVG images (e.g., via
279 # cross-browser way to embed external SVG images (e.g., via
276 # object or embed tags).
280 # object or embed tags).
277
281
278 # Chop stand-alone header from matplotlib SVG
282 # Chop stand-alone header from matplotlib SVG
279 offset = svg.find("<svg")
283 offset = svg.find("<svg")
280 assert(offset > -1)
284 assert(offset > -1)
281
285
282 return svg[offset:]
286 return svg[offset:]
283
287
284 else:
288 else:
285 return '<b>Unrecognized image format</b>'
289 return '<b>Unrecognized image format</b>'
286
290
287 def _insert_jpg(self, cursor, jpg, metadata=None):
291 def _insert_jpg(self, cursor, jpg, metadata=None):
288 """ Insert raw PNG data into the widget."""
292 """ Insert raw PNG data into the widget."""
289 self._insert_img(cursor, jpg, 'jpg', metadata=metadata)
293 self._insert_img(cursor, jpg, 'jpg', metadata=metadata)
290
294
291 def _insert_png(self, cursor, png, metadata=None):
295 def _insert_png(self, cursor, png, metadata=None):
292 """ Insert raw PNG data into the widget.
296 """ Insert raw PNG data into the widget.
293 """
297 """
294 self._insert_img(cursor, png, 'png', metadata=metadata)
298 self._insert_img(cursor, png, 'png', metadata=metadata)
295
299
296 def _insert_img(self, cursor, img, fmt, metadata=None):
300 def _insert_img(self, cursor, img, fmt, metadata=None):
297 """ insert a raw image, jpg or png """
301 """ insert a raw image, jpg or png """
298 if metadata:
302 if metadata:
299 width = metadata.get('width', None)
303 width = metadata.get('width', None)
300 height = metadata.get('height', None)
304 height = metadata.get('height', None)
301 else:
305 else:
302 width = height = None
306 width = height = None
303 try:
307 try:
304 image = QtGui.QImage()
308 image = QtGui.QImage()
305 image.loadFromData(img, fmt.upper())
309 image.loadFromData(img, fmt.upper())
306 if width and height:
310 if width and height:
307 image = image.scaled(width, height, transformMode=QtCore.Qt.SmoothTransformation)
311 image = image.scaled(width, height, transformMode=QtCore.Qt.SmoothTransformation)
308 elif width and not height:
312 elif width and not height:
309 image = image.scaledToWidth(width, transformMode=QtCore.Qt.SmoothTransformation)
313 image = image.scaledToWidth(width, transformMode=QtCore.Qt.SmoothTransformation)
310 elif height and not width:
314 elif height and not width:
311 image = image.scaledToHeight(height, transformMode=QtCore.Qt.SmoothTransformation)
315 image = image.scaledToHeight(height, transformMode=QtCore.Qt.SmoothTransformation)
312 except ValueError:
316 except ValueError:
313 self._insert_plain_text(cursor, 'Received invalid %s data.'%fmt)
317 self._insert_plain_text(cursor, 'Received invalid %s data.'%fmt)
314 else:
318 else:
315 format = self._add_image(image)
319 format = self._add_image(image)
316 cursor.insertBlock()
320 cursor.insertBlock()
317 cursor.insertImage(format)
321 cursor.insertImage(format)
318 cursor.insertBlock()
322 cursor.insertBlock()
319
323
320 def _insert_svg(self, cursor, svg):
324 def _insert_svg(self, cursor, svg):
321 """ Insert raw SVG data into the widet.
325 """ Insert raw SVG data into the widet.
322 """
326 """
323 try:
327 try:
324 image = svg_to_image(svg)
328 image = svg_to_image(svg)
325 except ValueError:
329 except ValueError:
326 self._insert_plain_text(cursor, 'Received invalid SVG data.')
330 self._insert_plain_text(cursor, 'Received invalid SVG data.')
327 else:
331 else:
328 format = self._add_image(image)
332 format = self._add_image(image)
329 self._name_to_svg_map[format.name()] = svg
333 self._name_to_svg_map[format.name()] = svg
330 cursor.insertBlock()
334 cursor.insertBlock()
331 cursor.insertImage(format)
335 cursor.insertImage(format)
332 cursor.insertBlock()
336 cursor.insertBlock()
333
337
334 def _save_image(self, name, format='PNG'):
338 def _save_image(self, name, format='PNG'):
335 """ Shows a save dialog for the ImageResource with 'name'.
339 """ Shows a save dialog for the ImageResource with 'name'.
336 """
340 """
337 dialog = QtGui.QFileDialog(self._control, 'Save Image')
341 dialog = QtGui.QFileDialog(self._control, 'Save Image')
338 dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
342 dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
339 dialog.setDefaultSuffix(format.lower())
343 dialog.setDefaultSuffix(format.lower())
340 dialog.setNameFilter('%s file (*.%s)' % (format, format.lower()))
344 dialog.setNameFilter('%s file (*.%s)' % (format, format.lower()))
341 if dialog.exec_():
345 if dialog.exec_():
342 filename = dialog.selectedFiles()[0]
346 filename = dialog.selectedFiles()[0]
343 image = self._get_image(name)
347 image = self._get_image(name)
344 image.save(filename, format)
348 image.save(filename, format)
General Comments 0
You need to be logged in to leave comments. Login now