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