##// END OF EJS Templates
support latex in display messages in qt
Min RK -
Show More
@@ -1,348 +1,357 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 try:
132 try:
133 png = latex_to_png(data['text/latex'], wrap=False)
133 png = latex_to_png(data['text/latex'], wrap=False)
134 except Exception:
134 except Exception:
135 self.log.error("Failed to render latex: %r", data['text/latex'], exc_info=True)
135 self.log.error("Failed to render latex: %r", data['text/latex'], exc_info=True)
136 png = None
136 png = None
137 if png is not None:
137 if png is not None:
138 self._append_png(png, True)
138 self._append_png(png, True)
139 self._append_html(self.output_sep2, True)
139 self._append_html(self.output_sep2, True)
140 else:
140 else:
141 # Print plain text if png can't be generated
141 # Print plain text if png can't be generated
142 return super(RichIPythonWidget, self)._handle_execute_result(msg)
142 return super(RichIPythonWidget, self)._handle_execute_result(msg)
143 else:
143 else:
144 # Default back to the plain text representation.
144 # Default back to the plain text representation.
145 return super(RichIPythonWidget, self)._handle_execute_result(msg)
145 return super(RichIPythonWidget, self)._handle_execute_result(msg)
146
146
147 def _handle_display_data(self, msg):
147 def _handle_display_data(self, msg):
148 """ Overridden to handle rich data types, like SVG.
148 """ Overridden to handle rich data types, like SVG.
149 """
149 """
150 if self.include_output(msg):
150 if self.include_output(msg):
151 self.flush_clearoutput()
151 self.flush_clearoutput()
152 data = msg['content']['data']
152 data = msg['content']['data']
153 metadata = msg['content']['metadata']
153 metadata = msg['content']['metadata']
154 # Try to use the svg or html representations.
154 # Try to use the svg or html representations.
155 # FIXME: Is this the right ordering of things to try?
155 # FIXME: Is this the right ordering of things to try?
156 self.log.debug("display: %s", msg.get('content', ''))
156 if 'image/svg+xml' in data:
157 if 'image/svg+xml' in data:
157 self.log.debug("display: %s", msg.get('content', ''))
158 svg = data['image/svg+xml']
158 svg = data['image/svg+xml']
159 self._append_svg(svg, True)
159 self._append_svg(svg, True)
160 elif 'image/png' in data:
160 elif 'image/png' in data:
161 self.log.debug("display: %s", msg.get('content', ''))
162 # PNG data is base64 encoded as it passes over the network
161 # PNG data is base64 encoded as it passes over the network
163 # in a JSON structure so we decode it.
162 # in a JSON structure so we decode it.
164 png = decodestring(data['image/png'].encode('ascii'))
163 png = decodestring(data['image/png'].encode('ascii'))
165 self._append_png(png, True, metadata=metadata.get('image/png', None))
164 self._append_png(png, True, metadata=metadata.get('image/png', None))
166 elif 'image/jpeg' in data and self._jpg_supported:
165 elif 'image/jpeg' in data and self._jpg_supported:
167 self.log.debug("display: %s", msg.get('content', ''))
168 jpg = decodestring(data['image/jpeg'].encode('ascii'))
166 jpg = decodestring(data['image/jpeg'].encode('ascii'))
169 self._append_jpg(jpg, True, metadata=metadata.get('image/jpeg', None))
167 self._append_jpg(jpg, True, metadata=metadata.get('image/jpeg', None))
168 elif 'text/latex' in data:
169 try:
170 png = latex_to_png(data['text/latex'], wrap=False)
171 except Exception:
172 self.log.error("Failed to render latex: %r", data['text/latex'], exc_info=True)
173 png = None
174 if png is not None:
175 self._append_png(png, True)
176 else:
177 # Print plain text if png can't be generated
178 return super(RichIPythonWidget, self)._handle_display_data(msg)
170 else:
179 else:
171 # Default back to the plain text representation.
180 # Default back to the plain text representation.
172 return super(RichIPythonWidget, self)._handle_display_data(msg)
181 return super(RichIPythonWidget, self)._handle_display_data(msg)
173
182
174 #---------------------------------------------------------------------------
183 #---------------------------------------------------------------------------
175 # 'RichIPythonWidget' protected interface
184 # 'RichIPythonWidget' protected interface
176 #---------------------------------------------------------------------------
185 #---------------------------------------------------------------------------
177
186
178 def _append_jpg(self, jpg, before_prompt=False, metadata=None):
187 def _append_jpg(self, jpg, before_prompt=False, metadata=None):
179 """ Append raw JPG data to the widget."""
188 """ Append raw JPG data to the widget."""
180 self._append_custom(self._insert_jpg, jpg, before_prompt, metadata=metadata)
189 self._append_custom(self._insert_jpg, jpg, before_prompt, metadata=metadata)
181
190
182 def _append_png(self, png, before_prompt=False, metadata=None):
191 def _append_png(self, png, before_prompt=False, metadata=None):
183 """ Append raw PNG data to the widget.
192 """ Append raw PNG data to the widget.
184 """
193 """
185 self._append_custom(self._insert_png, png, before_prompt, metadata=metadata)
194 self._append_custom(self._insert_png, png, before_prompt, metadata=metadata)
186
195
187 def _append_svg(self, svg, before_prompt=False):
196 def _append_svg(self, svg, before_prompt=False):
188 """ Append raw SVG data to the widget.
197 """ Append raw SVG data to the widget.
189 """
198 """
190 self._append_custom(self._insert_svg, svg, before_prompt)
199 self._append_custom(self._insert_svg, svg, before_prompt)
191
200
192 def _add_image(self, image):
201 def _add_image(self, image):
193 """ Adds the specified QImage to the document and returns a
202 """ Adds the specified QImage to the document and returns a
194 QTextImageFormat that references it.
203 QTextImageFormat that references it.
195 """
204 """
196 document = self._control.document()
205 document = self._control.document()
197 name = str(image.cacheKey())
206 name = str(image.cacheKey())
198 document.addResource(QtGui.QTextDocument.ImageResource,
207 document.addResource(QtGui.QTextDocument.ImageResource,
199 QtCore.QUrl(name), image)
208 QtCore.QUrl(name), image)
200 format = QtGui.QTextImageFormat()
209 format = QtGui.QTextImageFormat()
201 format.setName(name)
210 format.setName(name)
202 return format
211 return format
203
212
204 def _copy_image(self, name):
213 def _copy_image(self, name):
205 """ Copies the ImageResource with 'name' to the clipboard.
214 """ Copies the ImageResource with 'name' to the clipboard.
206 """
215 """
207 image = self._get_image(name)
216 image = self._get_image(name)
208 QtGui.QApplication.clipboard().setImage(image)
217 QtGui.QApplication.clipboard().setImage(image)
209
218
210 def _get_image(self, name):
219 def _get_image(self, name):
211 """ Returns the QImage stored as the ImageResource with 'name'.
220 """ Returns the QImage stored as the ImageResource with 'name'.
212 """
221 """
213 document = self._control.document()
222 document = self._control.document()
214 image = document.resource(QtGui.QTextDocument.ImageResource,
223 image = document.resource(QtGui.QTextDocument.ImageResource,
215 QtCore.QUrl(name))
224 QtCore.QUrl(name))
216 return image
225 return image
217
226
218 def _get_image_tag(self, match, path = None, format = "png"):
227 def _get_image_tag(self, match, path = None, format = "png"):
219 """ Return (X)HTML mark-up for the image-tag given by match.
228 """ Return (X)HTML mark-up for the image-tag given by match.
220
229
221 Parameters
230 Parameters
222 ----------
231 ----------
223 match : re.SRE_Match
232 match : re.SRE_Match
224 A match to an HTML image tag as exported by Qt, with
233 A match to an HTML image tag as exported by Qt, with
225 match.group("Name") containing the matched image ID.
234 match.group("Name") containing the matched image ID.
226
235
227 path : string|None, optional [default None]
236 path : string|None, optional [default None]
228 If not None, specifies a path to which supporting files may be
237 If not None, specifies a path to which supporting files may be
229 written (e.g., for linked images). If None, all images are to be
238 written (e.g., for linked images). If None, all images are to be
230 included inline.
239 included inline.
231
240
232 format : "png"|"svg"|"jpg", optional [default "png"]
241 format : "png"|"svg"|"jpg", optional [default "png"]
233 Format for returned or referenced images.
242 Format for returned or referenced images.
234 """
243 """
235 if format in ("png","jpg"):
244 if format in ("png","jpg"):
236 try:
245 try:
237 image = self._get_image(match.group("name"))
246 image = self._get_image(match.group("name"))
238 except KeyError:
247 except KeyError:
239 return "<b>Couldn't find image %s</b>" % match.group("name")
248 return "<b>Couldn't find image %s</b>" % match.group("name")
240
249
241 if path is not None:
250 if path is not None:
242 ensure_dir_exists(path)
251 ensure_dir_exists(path)
243 relpath = os.path.basename(path)
252 relpath = os.path.basename(path)
244 if image.save("%s/qt_img%s.%s" % (path, match.group("name"), format),
253 if image.save("%s/qt_img%s.%s" % (path, match.group("name"), format),
245 "PNG"):
254 "PNG"):
246 return '<img src="%s/qt_img%s.%s">' % (relpath,
255 return '<img src="%s/qt_img%s.%s">' % (relpath,
247 match.group("name"),format)
256 match.group("name"),format)
248 else:
257 else:
249 return "<b>Couldn't save image!</b>"
258 return "<b>Couldn't save image!</b>"
250 else:
259 else:
251 ba = QtCore.QByteArray()
260 ba = QtCore.QByteArray()
252 buffer_ = QtCore.QBuffer(ba)
261 buffer_ = QtCore.QBuffer(ba)
253 buffer_.open(QtCore.QIODevice.WriteOnly)
262 buffer_.open(QtCore.QIODevice.WriteOnly)
254 image.save(buffer_, format.upper())
263 image.save(buffer_, format.upper())
255 buffer_.close()
264 buffer_.close()
256 return '<img src="data:image/%s;base64,\n%s\n" />' % (
265 return '<img src="data:image/%s;base64,\n%s\n" />' % (
257 format,re.sub(r'(.{60})',r'\1\n',str(ba.toBase64())))
266 format,re.sub(r'(.{60})',r'\1\n',str(ba.toBase64())))
258
267
259 elif format == "svg":
268 elif format == "svg":
260 try:
269 try:
261 svg = str(self._name_to_svg_map[match.group("name")])
270 svg = str(self._name_to_svg_map[match.group("name")])
262 except KeyError:
271 except KeyError:
263 if not self._svg_warning_displayed:
272 if not self._svg_warning_displayed:
264 QtGui.QMessageBox.warning(self, 'Error converting PNG to SVG.',
273 QtGui.QMessageBox.warning(self, 'Error converting PNG to SVG.',
265 'Cannot convert PNG images to SVG, export with PNG figures instead. '
274 'Cannot convert PNG images to SVG, export with PNG figures instead. '
266 'If you want to export matplotlib figures as SVG, add '
275 'If you want to export matplotlib figures as SVG, add '
267 'to your ipython config:\n\n'
276 'to your ipython config:\n\n'
268 '\tc.InlineBackend.figure_format = \'svg\'\n\n'
277 '\tc.InlineBackend.figure_format = \'svg\'\n\n'
269 'And regenerate the figures.',
278 'And regenerate the figures.',
270 QtGui.QMessageBox.Ok)
279 QtGui.QMessageBox.Ok)
271 self._svg_warning_displayed = True
280 self._svg_warning_displayed = True
272 return ("<b>Cannot convert PNG images to SVG.</b> "
281 return ("<b>Cannot convert PNG images to SVG.</b> "
273 "You must export this session with PNG images. "
282 "You must export this session with PNG images. "
274 "If you want to export matplotlib figures as SVG, add to your config "
283 "If you want to export matplotlib figures as SVG, add to your config "
275 "<span>c.InlineBackend.figure_format = 'svg'</span> "
284 "<span>c.InlineBackend.figure_format = 'svg'</span> "
276 "and regenerate the figures.")
285 "and regenerate the figures.")
277
286
278 # Not currently checking path, because it's tricky to find a
287 # Not currently checking path, because it's tricky to find a
279 # cross-browser way to embed external SVG images (e.g., via
288 # cross-browser way to embed external SVG images (e.g., via
280 # object or embed tags).
289 # object or embed tags).
281
290
282 # Chop stand-alone header from matplotlib SVG
291 # Chop stand-alone header from matplotlib SVG
283 offset = svg.find("<svg")
292 offset = svg.find("<svg")
284 assert(offset > -1)
293 assert(offset > -1)
285
294
286 return svg[offset:]
295 return svg[offset:]
287
296
288 else:
297 else:
289 return '<b>Unrecognized image format</b>'
298 return '<b>Unrecognized image format</b>'
290
299
291 def _insert_jpg(self, cursor, jpg, metadata=None):
300 def _insert_jpg(self, cursor, jpg, metadata=None):
292 """ Insert raw PNG data into the widget."""
301 """ Insert raw PNG data into the widget."""
293 self._insert_img(cursor, jpg, 'jpg', metadata=metadata)
302 self._insert_img(cursor, jpg, 'jpg', metadata=metadata)
294
303
295 def _insert_png(self, cursor, png, metadata=None):
304 def _insert_png(self, cursor, png, metadata=None):
296 """ Insert raw PNG data into the widget.
305 """ Insert raw PNG data into the widget.
297 """
306 """
298 self._insert_img(cursor, png, 'png', metadata=metadata)
307 self._insert_img(cursor, png, 'png', metadata=metadata)
299
308
300 def _insert_img(self, cursor, img, fmt, metadata=None):
309 def _insert_img(self, cursor, img, fmt, metadata=None):
301 """ insert a raw image, jpg or png """
310 """ insert a raw image, jpg or png """
302 if metadata:
311 if metadata:
303 width = metadata.get('width', None)
312 width = metadata.get('width', None)
304 height = metadata.get('height', None)
313 height = metadata.get('height', None)
305 else:
314 else:
306 width = height = None
315 width = height = None
307 try:
316 try:
308 image = QtGui.QImage()
317 image = QtGui.QImage()
309 image.loadFromData(img, fmt.upper())
318 image.loadFromData(img, fmt.upper())
310 if width and height:
319 if width and height:
311 image = image.scaled(width, height, transformMode=QtCore.Qt.SmoothTransformation)
320 image = image.scaled(width, height, transformMode=QtCore.Qt.SmoothTransformation)
312 elif width and not height:
321 elif width and not height:
313 image = image.scaledToWidth(width, transformMode=QtCore.Qt.SmoothTransformation)
322 image = image.scaledToWidth(width, transformMode=QtCore.Qt.SmoothTransformation)
314 elif height and not width:
323 elif height and not width:
315 image = image.scaledToHeight(height, transformMode=QtCore.Qt.SmoothTransformation)
324 image = image.scaledToHeight(height, transformMode=QtCore.Qt.SmoothTransformation)
316 except ValueError:
325 except ValueError:
317 self._insert_plain_text(cursor, 'Received invalid %s data.'%fmt)
326 self._insert_plain_text(cursor, 'Received invalid %s data.'%fmt)
318 else:
327 else:
319 format = self._add_image(image)
328 format = self._add_image(image)
320 cursor.insertBlock()
329 cursor.insertBlock()
321 cursor.insertImage(format)
330 cursor.insertImage(format)
322 cursor.insertBlock()
331 cursor.insertBlock()
323
332
324 def _insert_svg(self, cursor, svg):
333 def _insert_svg(self, cursor, svg):
325 """ Insert raw SVG data into the widet.
334 """ Insert raw SVG data into the widet.
326 """
335 """
327 try:
336 try:
328 image = svg_to_image(svg)
337 image = svg_to_image(svg)
329 except ValueError:
338 except ValueError:
330 self._insert_plain_text(cursor, 'Received invalid SVG data.')
339 self._insert_plain_text(cursor, 'Received invalid SVG data.')
331 else:
340 else:
332 format = self._add_image(image)
341 format = self._add_image(image)
333 self._name_to_svg_map[format.name()] = svg
342 self._name_to_svg_map[format.name()] = svg
334 cursor.insertBlock()
343 cursor.insertBlock()
335 cursor.insertImage(format)
344 cursor.insertImage(format)
336 cursor.insertBlock()
345 cursor.insertBlock()
337
346
338 def _save_image(self, name, format='PNG'):
347 def _save_image(self, name, format='PNG'):
339 """ Shows a save dialog for the ImageResource with 'name'.
348 """ Shows a save dialog for the ImageResource with 'name'.
340 """
349 """
341 dialog = QtGui.QFileDialog(self._control, 'Save Image')
350 dialog = QtGui.QFileDialog(self._control, 'Save Image')
342 dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
351 dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
343 dialog.setDefaultSuffix(format.lower())
352 dialog.setDefaultSuffix(format.lower())
344 dialog.setNameFilter('%s file (*.%s)' % (format, format.lower()))
353 dialog.setNameFilter('%s file (*.%s)' % (format, format.lower()))
345 if dialog.exec_():
354 if dialog.exec_():
346 filename = dialog.selectedFiles()[0]
355 filename = dialog.selectedFiles()[0]
347 image = self._get_image(name)
356 image = self._get_image(name)
348 image.save(filename, format)
357 image.save(filename, format)
General Comments 0
You need to be logged in to leave comments. Login now