##// END OF EJS Templates
Add SVG output.
Stefan van der Walt -
Show More
@@ -36,6 +36,7 b' from shutil import rmtree'
36
36
37 import numpy as np
37 import numpy as np
38 import oct2py
38 import oct2py
39 from xml.dom import minidom
39
40
40 from IPython.core.displaypub import publish_display_data
41 from IPython.core.displaypub import publish_display_data
41 from IPython.core.magic import (Magics, magics_class, line_magic,
42 from IPython.core.magic import (Magics, magics_class, line_magic,
@@ -46,10 +47,13 b' from IPython.core.magic_arguments import ('
46 )
47 )
47 from IPython.utils.py3compat import unicode_to_str
48 from IPython.utils.py3compat import unicode_to_str
48
49
49
50 class OctaveMagicError(oct2py.Oct2PyError):
50 class OctaveMagicError(oct2py.Oct2PyError):
51 pass
51 pass
52
52
53 _mimetypes = {'png' : 'image/png',
54 'svg' : 'image/svg+xml',
55 'jpg' : 'image/jpeg',
56 'jpeg': 'image/jpeg'}
53
57
54 @magics_class
58 @magics_class
55 class OctaveMagics(Magics):
59 class OctaveMagics(Magics):
@@ -59,12 +63,39 b' class OctaveMagics(Magics):'
59 """
63 """
60 Parameters
64 Parameters
61 ----------
65 ----------
62
63 shell : IPython shell
66 shell : IPython shell
64
67
65 """
68 """
66 super(OctaveMagics, self).__init__(shell)
69 super(OctaveMagics, self).__init__(shell)
67 self.oct = oct2py.Oct2Py()
70 self._oct = oct2py.Oct2Py()
71 self._plot_format = 'png'
72
73
74 def _fix_gnuplot_svg_size(self, image, size=None):
75 """
76 GnuPlot SVGs do not have height/width attributes. Set
77 these to be the same as the viewBox, so that the browser
78 scales the image correctly.
79
80 Parameters
81 ----------
82 image : str
83 SVG data.
84 size : tuple of int
85 Image width, height.
86
87 """
88 (svg,) = minidom.parseString(image).getElementsByTagName('svg')
89 viewbox = svg.getAttribute('viewBox').split(' ')
90
91 if size is not None:
92 width, height = size
93 else:
94 width, height = viewbox[2:]
95
96 svg.setAttribute('width', '%dpx' % width)
97 svg.setAttribute('height', '%dpx' % height)
98 return svg.toxml()
68
99
69
100
70 @skip_doctest
101 @skip_doctest
@@ -92,7 +123,7 b' class OctaveMagics(Magics):'
92 inputs = line.split(' ')
123 inputs = line.split(' ')
93 for input in inputs:
124 for input in inputs:
94 input = unicode_to_str(input)
125 input = unicode_to_str(input)
95 self.oct.put(input, self.shell.user_ns[input])
126 self._oct.put(input, self.shell.user_ns[input])
96
127
97
128
98 @skip_doctest
129 @skip_doctest
@@ -117,7 +148,7 b' class OctaveMagics(Magics):'
117 outputs = line.split(' ')
148 outputs = line.split(' ')
118 for output in outputs:
149 for output in outputs:
119 output = unicode_to_str(output)
150 output = unicode_to_str(output)
120 self.shell.push({output: self.oct.get(output)})
151 self.shell.push({output: self._oct.get(output)})
121
152
122
153
123 @skip_doctest
154 @skip_doctest
@@ -135,8 +166,13 b' class OctaveMagics(Magics):'
135 )
166 )
136 @argument(
167 @argument(
137 '-s', '--size', action='append',
168 '-s', '--size', action='append',
138 help='Pixel size of plots. Default is "-s 400,250".'
169 help='Pixel size of plots, "width,height". Default is "-s 400,250".'
170 )
171 @argument(
172 '-f', '--format', action='append',
173 help='Plot format (png, svg or jpg).'
139 )
174 )
175
140 @argument(
176 @argument(
141 'code',
177 'code',
142 nargs='*',
178 nargs='*',
@@ -180,11 +216,16 b' class OctaveMagics(Magics):'
180 In [17]: W
216 In [17]: W
181 Out[17]: array([ 5., 20., 25., 50.])
217 Out[17]: array([ 5., 20., 25., 50.])
182
218
219 The size and format of output plots can be specified::
220
221 In [18]: %%octave -s 600,800 -f svg
222 ...: plot([1, 2, 3]);
223
183 '''
224 '''
184 args = parse_argstring(self.octave, line)
225 args = parse_argstring(self.octave, line)
185
226
186 # arguments 'code' in line are prepended to the cell lines
227 # arguments 'code' in line are prepended to the cell lines
187 if not cell:
228 if cell is None:
188 code = ''
229 code = ''
189 return_output = True
230 return_output = True
190 line_mode = True
231 line_mode = True
@@ -198,7 +239,7 b' class OctaveMagics(Magics):'
198 if args.input:
239 if args.input:
199 for input in ','.join(args.input).split(','):
240 for input in ','.join(args.input).split(','):
200 input = unicode_to_str(input)
241 input = unicode_to_str(input)
201 self.oct.put(input, self.shell.user_ns[input])
242 self._oct.put(input, self.shell.user_ns[input])
202
243
203 # generate plots in a temporary directory
244 # generate plots in a temporary directory
204 plot_dir = tempfile.mkdtemp()
245 plot_dir = tempfile.mkdtemp()
@@ -207,6 +248,11 b' class OctaveMagics(Magics):'
207 else:
248 else:
208 size = '400,240'
249 size = '400,240'
209
250
251 if args.format is not None:
252 plot_format = args.format[0]
253 else:
254 plot_format = 'png'
255
210 pre_call = '''
256 pre_call = '''
211 global __ipy_figures = [];
257 global __ipy_figures = [];
212 page_screen_output(0);
258 page_screen_output(0);
@@ -235,15 +281,16 b' class OctaveMagics(Magics):'
235 for f = __ipy_figures
281 for f = __ipy_figures
236 outfile = sprintf('%(plot_dir)s/__ipy_oct_fig_%%03d.png', f);
282 outfile = sprintf('%(plot_dir)s/__ipy_oct_fig_%%03d.png', f);
237 try
283 try
238 print(f, outfile, '-dpng', '-tight', '-S%(size)s');
284 print(f, outfile, '-d%(plot_format)s', '-tight', '-S%(size)s');
239 end
285 end
240 end
286 end
241
287
242 ''' % {'plot_dir': plot_dir, 'size': size}
288 ''' % {'plot_dir': plot_dir, 'size': size,
289 'plot_format': plot_format}
243
290
244 code = ' '.join((pre_call, code, post_call))
291 code = ' '.join((pre_call, code, post_call))
245 try:
292 try:
246 text_output = self.oct.run(code, verbose=False)
293 text_output = self._oct.run(code, verbose=False)
247 except (oct2py.Oct2PyError) as exception:
294 except (oct2py.Oct2PyError) as exception:
248 raise OctaveMagicError('Octave could not complete execution. '
295 raise OctaveMagicError('Octave could not complete execution. '
249 'Traceback (currently broken in oct2py): %s'
296 'Traceback (currently broken in oct2py): %s'
@@ -257,28 +304,27 b' class OctaveMagics(Magics):'
257 display_data.append((key, {'text/plain': text_output}))
304 display_data.append((key, {'text/plain': text_output}))
258
305
259 # Publish images
306 # Publish images
260 fmt = 'png'
261 mimetypes = {'png' : 'image/png',
262 'svg' : 'image/svg+xml'}
263 mime = mimetypes[fmt]
264
265 images = [open(imgfile, 'rb').read() for imgfile in \
307 images = [open(imgfile, 'rb').read() for imgfile in \
266 glob("%s/*.png" % plot_dir)]
308 glob("%s/*" % plot_dir)]
267 rmtree(plot_dir)
309 rmtree(plot_dir)
268
310
311 plot_mime_type = _mimetypes.get(plot_format, 'image/png')
312 width, height = [int(s) for s in size.split(',')]
269 for image in images:
313 for image in images:
270 display_data.append((key, {mime: image}))
314 if plot_format == 'svg':
315 image = self._fix_gnuplot_svg_size(image, size=(width, height))
316 display_data.append((key, {plot_mime_type: image}))
271
317
272 if args.output:
318 if args.output:
273 for output in ','.join(args.output).split(','):
319 for output in ','.join(args.output).split(','):
274 output = unicode_to_str(output)
320 output = unicode_to_str(output)
275 self.shell.push({output: self.oct.get(output)})
321 self.shell.push({output: self._oct.get(output)})
276
322
277 for tag, data in display_data:
323 for tag, data in display_data:
278 publish_display_data(tag, data)
324 publish_display_data(tag, data)
279
325
280 if return_output:
326 if return_output:
281 ans = self.oct.get('_')
327 ans = self._oct.get('_')
282
328
283 # Unfortunately, Octave doesn't have a "None" object,
329 # Unfortunately, Octave doesn't have a "None" object,
284 # so we can't return any NaN outputs
330 # so we can't return any NaN outputs
@@ -287,6 +333,7 b' class OctaveMagics(Magics):'
287
333
288 return ans
334 return ans
289
335
336
290 __doc__ = __doc__.format(
337 __doc__ = __doc__.format(
291 OCTAVE_DOC = ' '*8 + OctaveMagics.octave.__doc__,
338 OCTAVE_DOC = ' '*8 + OctaveMagics.octave.__doc__,
292 OCTAVE_PUSH_DOC = ' '*8 + OctaveMagics.octave_push.__doc__,
339 OCTAVE_PUSH_DOC = ' '*8 + OctaveMagics.octave_push.__doc__,
General Comments 0
You need to be logged in to leave comments. Login now