Ticket #1322: manipulate-take3_part2.patch

File manipulate-take3_part2.patch, 10.9 KB (added by was, 14 years ago)

part 2 of manipulate with decorators; this is usable again (probably) better docs; but there are several things to do soon and I'm too tired.

  • sage/server/notebook/all.py

    # HG changeset patch
    # User William Stein <wstein@gmail.com>
    # Date 1204447690 21600
    # Node ID 48f2d5de9355c29c0d5f477db1d137f8def9bca4
    # Parent  9ebe670b72be0ab93433932e9b70cb1b6b9faec1
    Trac #1322 -- manipulate -- new version (still first prototype)
    
    diff -r 9ebe670b72be -r 48f2d5de9355 sage/server/notebook/all.py
    a b from notebook_object import notebook, in 
    1414
    1515from sagetex import sagetex
    1616
    17 from manipulate import manipulate
     17from manipulate import manipulate, text_box, slider
  • new file sage/server/notebook/jquery.py

    diff -r 9ebe670b72be -r 48f2d5de9355 sage/server/notebook/jquery.py
    - +  
     1import manipulate
     2
     3def javascript(s):
     4    print '<html><script>%s</script></html>'%s
     5
     6def cell_id():
     7    return manipulate.SAGE_CELL_ID
     8
     9def draggable(option=''):
     10    """
     11    INPUT:
     12        option -- string
     13            "" (default) -- Creates new draggables on the current cell.
     14            "enable" -- Enable the draggable functionality.
     15            "disable" -- Temporarily disable the draggable functionality.
     16    """
     17    s = '$("#cell_outer_%s").draggable("%s")'%(cell_id(), option)
     18    javascript(s)
     19
     20def resizable():
     21    s = '$("#cell_outer_%s").resizable()'%cell_id()
     22    javascript(s)   
  • sage/server/notebook/js.py

    diff -r 9ebe670b72be -r 48f2d5de9355 sage/server/notebook/js.py
    a b function show_help_window(worksheet) { 
    24972497
    24982498
    24992499///////////////////////////////////////////////////////////////////
    2500 // Dynamic / Manipulate
     2500// Manipulate
    25012501///////////////////////////////////////////////////////////////////
    25022502
    2503 function dynamic(id, input) {
     2503function manipulate(id, input) {
    25042504    active_cell_list = active_cell_list.concat([id]);
    25052505    async_request(worksheet_command('eval'), evaluate_cell_callback,
    25062506            'newcell=0' + '&id=' + id + '&input='+escape0('%manipulate\n' + input));
  • sage/server/notebook/manipulate.py

    diff -r 9ebe670b72be -r 48f2d5de9355 sage/server/notebook/manipulate.py
    a b SAGE_CELL_ID=0 
    1 SAGE_CELL_ID=0
     1r"""
     2Manipulate Sage functions in the notebook
    23
    3 def _text_box(f, var):
    4     print """<html>
    5     <input type='text' value='<?%s>' onchange='dynamic(%s, "sage.server.notebook.manipulate.%s="+this.value+"\\n%s()");'></input>
    6     <table bgcolor=black cellpadding=3><tr><td bgcolor=white>
    7 <?TEXT>
    8     <table border=0 width=800px>
    9     <tr><td align=center>  <?HTML>  </td></tr></table>
    10     </td></tr></table>
    11     </html>
    12     """%(var, SAGE_CELL_ID, var, f.__name__)
     4This module implements a manipulate decorator for function in the Sage
     5notebook.
    136
     7The controls are:
     8\begin{itemize}
     9    \item TextBox -- a text box
     10    \item Slider -- a slider
     11\end{itemize}
     12
     13AUTHOR:
     14    -- William Stein (2008-03-02): initial version at Sage/Enthought
     15       Days 8 in Texas
     16    -- Jason Grout (2008-03): collaborators substantially on the
     17       design and prototypes.
     18
     19TODO/PLAN:
     20   [ ] sliders
     21   [ ] default value
     22   [ ] more widgets
     23   [ ] better widget layout controls
     24   [ ] 100% doctest coverage
     25"""
     26
     27import inspect
     28
     29SAGE_CELL_ID = 0
     30vars = {}
     31
     32def html(s):
     33    """
     34    Render the input string s in a form that tells the notebook
     35    to display it in the HTML portion of the output.
     36
     37    INPUT:
     38        s -- a string
     39
     40    OUTPUT:
     41        string -- html format
     42    """
     43    print "<html>%s</html>"%s
     44
     45class ManipulateControl:
     46    """
     47    Base class for manipulate controls.
     48    """
     49    def __init__(self, f, var):
     50        """
     51        Create a new manipulate control.
     52
     53        INPUT:
     54             f -- a Python function (that's being decorated)
     55             var -- name of variable that this control manipulates
     56             SAGE_CELL_ID -- uses this global variable
     57        """
     58        self.__var = var
     59        self.__cell_id = SAGE_CELL_ID
     60        self.__f = f
     61
     62    def __repr__(self):
     63        return "A ManipulateControl (abstract base class)"
     64       
     65    def manipulate(self):
     66        """
     67        Return a string that when evaluated in Javascript calls the
     68        javascript manipulate function with appropriate inputs for
     69        this control.
     70
     71        OUTPUT:
     72            string -- that is meant to be evaluated in Javascript
     73        """
     74        return 'manipulate(%s, "sage.server.notebook.manipulate.vars[%s][\\"%s\\"]=sage_eval(r\\"\\"\\""+%s+"\\"\\"\\", globals())\\n%s()");'%(
     75            self.cell_id(), self.cell_id(), self.var(), self.value(), self.function_name())
     76
     77    def function_name(self):
     78        """
     79        Returns the name of the function that this control manipulates.
     80
     81        OUTPUT:
     82            string -- name of a function as a string
     83        """
     84        return self.__f.__name__
     85
     86    def var(self):
     87        """
     88        Return the name of the variable that this control manipulates.
     89
     90        OUTPUT:
     91            string -- name of a variable as a string.
     92        """
     93        return self.__var
     94
     95    def cell_id(self):
     96        """
     97        Return the id of the cell that contains this manipulate control.
     98
     99        OUTPUT:
     100            integer -- id of cell that this control manipulates
     101        """
     102        return self.__cell_id
     103
     104class TextBox(ManipulateControl):
     105    """
     106    A text box manipulate control.
     107    """
     108    def __repr__(self):
     109        return "A TextBox manipulate control"
     110
     111    def value(self):
     112        """
     113        Return javascript string that will give the
     114        value of this control element.
     115
     116        OUTPUT:
     117             string -- javascript
     118        """
     119        return "this.value"
     120   
     121    def render(self):
     122        """
     123        Render this control as a string.
     124
     125        OUTPUT:
     126             string -- html format
     127        """
     128        return """
     129        %s: <input type='text' value='<?%s>' onchange='%s'></input>
     130        """%(self.var(), self.var(), self.manipulate())
     131
     132class Slider(ManipulateControl):
     133    """
     134    A slider manipulate control.
     135    """
     136    def __init__(self, f, var, values):
     137        """
     138        Create a slider manipulate control that takes on the given
     139        list of values.
     140        """
     141        ManipulateControl.__init__(self, f, var)
     142        self.__values = values
     143       
     144    def __repr__(self):
     145        return "A Slider manipulate control"
     146
     147    def value(self):
     148        """
     149        Return javascript string that will give the
     150        value of this control element.
     151
     152        OUTPUT:
     153             string -- javascript
     154        """
     155        return "this.value"
     156   
     157    def render(self):
     158        """
     159        Render this control as a string.
     160
     161        OUTPUT:
     162             string -- html format
     163        """
     164        return """
     165        SLIDER %s: <input type='text' value='<?%s>' onchange='%s'></input>
     166        """%(self.var(), self.var(), self.manipulate())
     167   
     168
     169class ManipulateCanvas:
     170    """
     171    Base class for manipulate canvases. This is where all the controls
     172    along with the output of the manipulated function are layed out
     173    and rendered.
     174    """
     175    def __init__(self, controls):
     176        """
     177        Create a manipulate canvas.
     178       
     179        INPUT:
     180            controls -- a list of ManipulateControl instances.
     181        """
     182        self.__controls = controls
     183
     184    def render_output(self):
     185        """
     186        Render in text (html) form the output portion of the manipulate canvas.
     187
     188        The output contains two special tags, <?TEXT> and <?HTML>,
     189        which get replaced at runtime by the text and html parts
     190        of the output of running the function.
     191
     192        OUTPUT:
     193            string -- html
     194        """
     195       
     196        return """
     197        <table bgcolor=black cellpadding=3><tr><td bgcolor=white>
     198        <?TEXT>
     199           <table border=0 width=800px>
     200           <tr><td align=center>  <?HTML>  </td></tr>
     201           </table>
     202        </td></tr></table>
     203        """
     204
     205    def render_controls(self):
     206        """
     207        Render in text (html) form all the input controls.
     208
     209        OUTPUT:
     210            string -- html
     211        """
     212        # This will need some sophisticated layout querying of the c's, maybe.
     213        return ''.join([c.render() for c in self.__controls])
     214       
     215    def render(self):
     216        """
     217        Render in text (html) the entire manipulate canvas.
     218
     219        OUTPUT:
     220            string -- html
     221        """
     222        return "%s%s"%(self.render_controls(), self.render_output())
     223   
     224       
    14225def manipulate(f):
    15226    """
    16     Decorate a function f to make a manipulatable version of f.
     227    Decorate a function f to make a manipulate version of f.
     228        @manipulate
     229        def foo(n,m):
     230            ...
    17231    """
    18     import inspect
    19     vars = inspect.getargspec(f)[0]
    20     print _text_box(f, vars[0])
     232   
     233    (args, varargs, varkw, defaults) = inspect.getargspec(f)
     234
     235    if defaults is None:
     236        defaults = []
     237       
     238    n = len(args) - len(defaults)
     239    controls = [TextBox(f, v) for v in args[:n]] + \
     240               [defaults[i].render(f, args[i+n]) for i in range(len(defaults))]
     241   
     242    C = ManipulateCanvas(controls)
     243   
     244    vars[SAGE_CELL_ID] = {}
     245    d = vars[SAGE_CELL_ID]
     246    for v in args:
     247         d[v] = ''
     248   
     249    html(C.render())
     250   
    21251    def g():
    22         return f(eval(vars[0]))
     252        return f(*[d[args[i]] for i in range(len(args))])
    23253    return g   
     254
     255
     256######################################################
     257# Actual control objects that the user passes in
     258######################################################
     259class control:
     260    pass
     261
     262class text_box(control):
     263    def __init__(self, default):
     264        """
     265        INPUT:
     266            default -- string (the default value)
     267        """
     268        self.__default = default
     269
     270    def __repr__(self):
     271        return "A manipulate text box control with default value '%s'"%self.__default
     272
     273    def render(self, f, var):
     274        """
     275        INPUT:
     276            f -- a Python function
     277            var -- a string (variable; one of the variable names input to f)
     278        """
     279        return TextBox(f, var)
     280   
     281class slider(control):
     282    def __init__(self, vmin, vmax=None, steps=30):
     283        if isinstance(vmin, list):
     284            self.__values = vmin
     285        else:
     286            if vmax is None:
     287                vmax = vmin
     288                vmin = 0
     289            steps = int(steps)
     290            if steps <= 0:
     291                self.__values = [vmin, vmax]
     292            else:
     293                step = (vmax-vmin)/steps  # hard coded
     294                self.__values = [vmin + i*step for i in range(steps)] + [vmax]
     295
     296    def __repr__(self):
     297        return "A manipulate slider control [%s - %s]."%(min(self.__values), max(self.__values))
     298
     299    def render(self, f, var):
     300        return Slider(f, var, self.__values)
     301