Source code for openalea.widgets.lpymagic

# -*- coding: utf-8 -*-
"""
===========
lpymagic
===========

Magics for interacting with LPy via openalea.lpy.

.. note::

  The ``openalea.lpy`` module needs to be installed separately and
  can be obtained using ``conda``.

Usage
=====

``%lpy``

{LPY_DOC}

``%lpy_rule``

{LPY_RULE_DOC}

``%lpy_axiom``

{LPY_AXIOM_DOC}


"""

#-----------------------------------------------------------------------------
#  Copyright (C) 2013-2019 Christophe Pradal
#
#  Distributed under the terms of the BSD License.  The full license is in
#  the file COPYING, distributed as part of this software.
#-----------------------------------------------------------------------------

from __future__ import absolute_import
from __future__ import print_function

import openalea.lpy as lpy
from IPython.core.displaypub import publish_display_data
from IPython.core.magic import (Magics, magics_class, line_magic,
                                line_cell_magic, needs_local_scope)
from IPython.core.magic_arguments import (
    argument, magic_arguments, parse_argstring
)
from IPython.display import display
from IPython.testing.skipdoctest import skip_doctest
from openalea.mtg import MTG
from openalea.mtg.io import mtg2lpy, lpy2mtg

from openalea.widgets import plantgl

_mimetypes = {'png' : 'image/png',
             'svg' : 'image/svg+xml',
             'jpg' : 'image/jpeg',
             'jpeg': 'image/jpeg'}

[docs] @magics_class class LpyMagics(Magics): """A set of magics useful for interactive work with Lpy """ def __init__(self, shell): """ Parameters ---------- shell : IPython shell """ super(LpyMagics, self).__init__(shell) self._lsys = lpy.Lsystem() self._plot_format = 'png' # Allow publish_display_data to be overridden for # testing purposes. self._publish_display_data = publish_display_data def _plot3d(self, scene, format='png'): """ Baptiste TODO: Replace by the k3d plantgl connection. """ # fn = tempfile.mktemp(suffix='.'+format) # Viewer.frameGL.setBgColor(255,255,255) # #Viewer.animation(True) # Viewer.display(scene) # Viewer.saveSnapshot(fn, format) # img = open(fn, 'rb').read() # os.unlink(fn) # return img data = plantgl.PlantGL(scene) display(data) return None
[docs] @skip_doctest @line_magic def lpy_axiom(self, line): """ Line-level magic that define the Lsystem Axiom to Lpy. `line` should be made up of a string or an AxialTree available in the IPython namespace:: In [1]: X = 'F(10)[(+30)F(1)]A' In [10]: %lpy_axiom X In [11]: %%lpy -n 10 Out[11]: 2.0 """ axiom = line axiom = str(axiom) self._lsys.axiom = axiom
[docs] @skip_doctest @line_magic def lpy_rule(self, line): """ TODO : Update the doc string Line-level magic that pulls a variable from Lpy. In [1]: %lpy_axiom 'A' In [2]: %lpy_rule 'A --> F A' In [3]: %%lpy -n 10 """ rule = str(line) self._lsys.addRule(rule)
[docs] @skip_doctest @magic_arguments() @argument( '-i', '--input', action='append', help='Names of input variable from shell.user_ns to be assigned to LPy variables of the same names. Multiple names can be passed separated only by commas with no whitespace.' ) @argument( '-o', '--output', action='append', help='Names of variables to be pushed from lpy to shell.user_ns after executing cell body. Multiple names can be passed separated only by commas with no whitespace.' ) @argument( '-w', '--workstring', action='append', help='Axial tree string or MTG to be set to Lpy before executing cell ' 'body. ' ) @argument( '-a', '--axialtree', action='append', help='Names of axial tree variable to be pulled from Lpy after executing cell ' 'body. ' ) @argument( '-g', '--mtg', action='append', help='Names of MTG variable to be pulled from Lpy after executing cell ' 'body. ' ) @argument( '-s', '--scene', action='append', help='Name of scene variable to be pulled from LPy after executing cell.' ) @argument( '-n', '--nbstep', action='append', help='Number of steps to be run by LPy..' ) @argument( '-f', '--format', action='store', help='Plot format (png, svg or jpg).' ) @needs_local_scope @argument( 'code', nargs='*', ) @line_cell_magic def lpy(self, line, cell=None, local_ns=None): """ .. todo:: Update the docstring Execute code in Lpy, and pull some of the results back into the Python namespace. In [9]: %lpy -w axiom -n 10 -g As a cell, this will run a block of Lpy code, without returning any value:: In [10]: %%lpy ....: p = [-2, -1, 0, 1, 2] ....: polyout(p, 'x') -2*x^4 - 1*x^3 + 0*x^2 + 1*x^1 + 2 In the notebook, plots are published as the output of the cell, e.g. %lpy plot([1 2 3], [4 5 6]) will create a line plot. Objects can be passed back and forth between Lpy and IPython via the -i and -o flags in line:: In [14]: Z = np.array([1, 4, 5, 10]) In [15]: %lpy -i Z mean(Z) Out[15]: array([ 5.]) In [16]: %lpy -o W W = Z * mean(Z) Out[16]: array([ 5., 20., 25., 50.]) In [17]: W Out[17]: array([ 5., 20., 25., 50.]) The size and format of output plots can be specified:: In [18]: %%lpy -s 600,800 -f svg ...: plot([1, 2, 3]); """ args = parse_argstring(self.lpy, line) # arguments 'code' in line are prepended to the cell lines if cell is None: code = '' return_output = True else: code = cell return_output = False code = ' '.join(args.code) + code code = str(code) # if there is no local namespace then default to an empty dict if local_ns is None: local_ns = {} parameters = {} if args.input: for input in ','.join(args.input).split(','): input = str(input) if input =='*': parameters.update(self.shell.user_ns) else: try: val = local_ns[input] except KeyError: val = self.shell.user_ns[input] parameters[input] = val if parameters: self._lsys.context().updateNamespace(parameters) if code: self._lsys.setCode(code, parameters) ################################################# # set, input workstring = '' if args.workstring: workstring = args.workstring[0] workstring = str(workstring) try: workstring = local_ns[workstring] except KeyError: workstring = self.shell.user_ns[workstring] except: pass if isinstance(workstring, str): self._lsys.makeCurrent() workstring = lpy.AxialTree(workstring) self._lsys.done() elif isinstance(workstring, MTG): workstring = mtg2lpy(workstring,self._lsys) else: pass # try: # ws = local_ns[workstring] # except KeyError: # ws = self.shell.user_ns[workstring] n = 1 if args.nbstep: n = int(args.nbstep[0]) if workstring: tree = self._lsys.iterate(workstring,n) else: n = self._lsys.derivationLength c_iter = self._lsys.getLastIterationNb() if not workstring: workstring = self._lsys.axiom if len(parameters) > 0: self._lsys.context().updateNamespace(parameters) print('DEBUG: ', workstring, c_iter, n) tree = self._lsys.iterate(workstring,c_iter,n) if args.axialtree: axial_name = str(args.axialtree[0]) self.shell.push({axial_name: tree}) scene = self._lsys.sceneInterpretation(tree) if args.scene and scene: self.shell.push({args.scene[0]: scene}) mtg = None if args.mtg: mtg_name = str(args.mtg[0]) mtg = lpy2mtg(tree, self._lsys, scene=scene) self.shell.push({mtg_name: mtg}) if args.format is not None: plot_format = args.format else: plot_format = 'png' key = 'LpyMagic.Lpy' display_data = {} # Publish text output """ if text_output: display_data.append((key, {'text/plain': text_output})) """ # Publish images image = self._plot3d(scene, format=plot_format) if image is not None: plot_mime_type = _mimetypes.get(plot_format, 'image/png') #width, height = [int(s) for s in size.split(',')] #for image in images: display_data[plot_mime_type]= image """ if args.output: for output in ','.join(args.output).split(','): output = str(output) self.shell.push({output: self._oct.get(output)}) """ #for source, data in display_data: # self._publish_display_data(source, data) self._publish_display_data(data=display_data) if return_output: return tree if not mtg else mtg return None
[docs] @skip_doctest @magic_arguments() @argument( '-w', '--workstring', action='append', help='Axial tree string or MTG to be set to Lpy before executing cell ' 'body. ' ) @argument( '-a', '--axialtree', action='append', help='Names of axial tree variable to be pulled from Lpy after executing cell ' 'body. ' ) @argument( '-s', '--scene', action='append', help='Name of scene variable to be pulled from LPy after executing cell.' ) @argument( '-g', '--mtg', action='append', help='Name of MTG variable to be pulled from LPy after executing cell.' ) @argument( '-n', '--nbstep', action='append', help='Number of steps to be run by LPy..' ) @argument( '-f', '--format', action='store', help='Plot format (png, svg or jpg).' ) @needs_local_scope @line_cell_magic def lpy_iter(self, line, cell=None, local_ns=None): """ Execute code in Lpy, and pull some of the results back into the Python namespace. In [9]: %lpy X = [1 2; 3 4]; mean(X) Out[9]: array([[ 2., 3.]]) As a cell, this will run a block of Lpy code, without returning any value:: In [10]: %%lpy ....: p = [-2, -1, 0, 1, 2] ....: polyout(p, 'x') -2*x^4 - 1*x^3 + 0*x^2 + 1*x^1 + 2 In the notebook, plots are published as the output of the cell, e.g. %lpy plot([1 2 3], [4 5 6]) will create a line plot. Objects can be passed back and forth between Lpy and IPython via the -i and -o flags in line:: In [14]: Z = np.array([1, 4, 5, 10]) In [15]: %lpy -i Z mean(Z) Out[15]: array([ 5.]) In [16]: %lpy -o W W = Z * mean(Z) Out[16]: array([ 5., 20., 25., 50.]) In [17]: W Out[17]: array([ 5., 20., 25., 50.]) The size and format of output plots can be specified:: In [18]: %%lpy -s 600,800 -f svg ...: plot([1, 2, 3]); """ args = parse_argstring(self.lpy, line) # arguments 'code' in line are prepended to the cell lines return_output = True # if there is no local namespace then default to an empty dict if local_ns is None: local_ns = {} workstring = self._lsys.axiom if args.workstring: workstring = ','.join(args.workstring).split(',')[0] workstring = str(workstring) try: ws = local_ns[workstring] except KeyError: ws = self.shell.user_ns[workstring] if isinstance(ws,MTG): workstring = mtg2lpy(ws,self._lsys) else: workstring = ws n0 = self._lsys.getLastIterationNb() n = self._lsys.derivationLength-n0 if args.nbstep: n = int(args.nbstep[0]) tree = self._lsys.iterate(workstring,n0,n) if args.axialtree: axial_name = str(args.axialtree[0]) self.shell.push({axial_name: tree}) scene = self._lsys.sceneInterpretation(tree) if args.scene: self.shell.push({args.scene[0]: scene}) g = None if args.mtg: mtg_name = str(args.mtg[0]) g = lpy2mtg(tree, self._lsys, scene=scene) self.shell.push({mtg_name: g}) if args.format is not None: plot_format = args.format else: plot_format = 'png' key = 'LpyMagic.Lpy' display_data = [] # Publish text output """ if text_output: display_data.append((key, {'text/plain': text_output})) """ # Publish images images = [self._plot3d(scene, format=plot_format)] plot_mime_type = _mimetypes.get(plot_format, 'image/png') #width, height = [int(s) for s in size.split(',')] for image in images: display_data.append((key, {plot_mime_type: image})) """ if args.output: for output in ','.join(args.output).split(','): output = str(output) self.shell.push({output: self._oct.get(output)}) """ for source, data in display_data: self._publish_display_data(source, data) if return_output: return tree if not args.mtg else g return None
__doc__ = __doc__.format( LPY_DOC = ' '*8 + LpyMagics.lpy.__doc__, LPY_AXIOM_DOC = ' '*8 + LpyMagics.lpy_axiom.__doc__, LPY_RULE_DOC = ' '*8 + LpyMagics.lpy_rule.__doc__, LPY_ITER_DOC = ' '*8 + LpyMagics.lpy_iter.__doc__, )
[docs] def load_ipython_extension(ip): """ Any module file that define a function named `load_ipython_extension` can be loaded via `%load_ext openalea.widgets.lpymagic` or be configured to be autoloaded by IPython at startup time. """ ip.register_magics(LpyMagics)