Opened 15 years ago
Closed 15 years ago
#1322 closed enhancement (duplicate)
interact -- interactive functions in the notebook (moved to #2449)
Reported by: | Jason Grout | Owned by: | William Stein |
---|---|---|---|
Priority: | major | Milestone: | sage-2.10.3 |
Component: | notebook | Keywords: | |
Cc: | jason-sage@…, Mike Hansen, Timothy Clemans | Merged in: | |
Authors: | Reviewers: | ||
Report Upstream: | N/A | Work issues: | |
Branch: | Commit: | ||
Dependencies: | Stopgaps: |
Description (last modified by )
This ticket is a total mess, so I've made a new clean #2449.
See mailing list discussions at
Also, from Robert Miller (to Jason Grout):
> I was brainstorming about something like widgets a while ago, before > the notebook underwent its sea change. We (>= you and I) should make > this a coding sprint at SD7.
Attachments (8)
Change History (33)
comment:1 Changed 15 years ago by
Cc: | jason-sage@… added |
---|
comment:2 Changed 15 years ago by
comment:3 Changed 15 years ago by
Changed 15 years ago by
Attachment: | menu-widget.patch added |
---|
Extremely rough cut of initial functionality for interactive widgets in the notebook.
comment:4 Changed 15 years ago by
Summary: | interactive widgets in the notebook → [with alpha patch] interactive widgets in the notebook |
---|
There are several problems with the way things are done in menu-widget.patch:
- We rely on a complete hack of sending an AJAX request with an invalid cell id (-1). The webserver complains ("Warning -- cell -1 no longer exists"), but the code is still executed and everything seems fine. So it'd be great if we could remove the warning and call it a feature :).
- The variable is not set initially. You have to select a value before using the variable. The variable is not set initially because I couldn't figure out how to set a global variable from within the menu.init function. We could probably look into the polynomial ring stuff and figure out how to inject variables into the global namespace. I have a rough cython file written that attempts to define an "inject_global" function that injects a variable into the global namespace, but when this function is called from menu.init, it doesn't inject into the global namespace.
- When a value is updated via the menu, it doesn't change or reevaluate any cells in the notebook. It would be very nice to automatically evaluate other cells in the notebook to get real-time dynamic feedback from picking new values from the menu.
- When manually reevaluating cells, it seems to display the old value first and then update to the new value. This is very annoying and takes time and looks bad.
- There should be some sort of caching eventually implemented to make things faster. Maybe something like a directive "%depends_on(x)" to say that a cell's output only depends on x. In other words, if x is the same, then we can spit out a cached copy of the cell's output instead of having to reevaluate the cell.
The menu select box is the tip of the iceberg here. It'd be nice to have fancy sliders and things like that using javascript.
comment:5 Changed 15 years ago by
Oh, and one more thing: we currently have to import the function:
sage: from sage.server.notebook.widgets.menu import menu sage: menu('f',[sin,cos,tan]).show() (a menu is shown. pick a value for f) sage: plot(f(x),0,2*pi).show() (the plot of f(x) (with the selected f) is shown.)
comment:6 Changed 15 years ago by
Component: | combinatorics → notebook |
---|---|
Keywords: | graphs removed |
Milestone: | sage-wishlist → sage-2.9 |
Owner: | changed from Mike Hansen to Kelly Boothby |
comment:7 Changed 15 years ago by
It appears that the warning about a nonexistant cell comes from the line
s = encode_list([cell.next_id(), 'no_new_cell', str(id)])
in sage/server/notebook/twisted.py
comment:8 Changed 15 years ago by
In addition to applying the first patch above, you need to create an empty init.py file in sage/server/notebook/widgets/
comment:10 Changed 15 years ago by
Here is a trivial example. You *must* change 155 to an actual existing blank output cell.
def manipulate(): print r""" <html> <script> function manip(id, cmd) { var cell_input = get_cell(id); cell_input.value = cmd; cell_input.style.display = "none"; evaluate_cell(id, 0); } </script> <input type="button" value="sin(x)" onclick="manip(155, 'plot(sin,-1,1)')"> <input type="button" value="cos(x)" onclick="manip(155, 'plot(cos,-1,1)')"> <input type="button" value="tan(x)" onclick="manip(155, 'plot(tan,-1,1)')"> <input type="button" value="sin(x^2)" onclick="manip(155, 'plot(sin(x^2),-1,1)')"> </html> """
comment:11 Changed 15 years ago by
More stuff:
print r""" <html> <script> function manip(id,cmd, variable, value) { var cell_input = get_cell(id); cell_input.value = variable+"="+value+"\n"+cmd; cell_input.style.display = "none"; evaluate_cell(id, 0); } </script> </html> """
and
def menu(cmd, variable, options): ret = """<select name='' onchange='manip(%d,"%s", "%s", this.options[this.selectedIndex].value)'>"""%(__SAGE_NOTEBOOK_CELL_ID__+1,cmd,variable) for option in options: ret += "<option value='%s'>%s</option>"%(repr(option), repr(option)) ret += "</select>" return ret def manipulate(cmd, variables): controls='' for key,val in variables.items(): controls += menu(cmd, key, val) print "<html>"+controls+"</html>"
and
manipulate('plot(f(x),(x,0,3))', {'f':[sin,cos,tan]})
comment:12 Changed 15 years ago by
with "manipulate.patch", here is a trivial example. You *must* change the CELL_ID to the id of an existing cell. That cell will be overwritten.
manipulate("factor(y)", {'y': TextBox?(100)}, CELL_ID)
manipulate("factor(y)", {'y': Menu([2, 4, 6, 8])}, CELL_ID)
manipulate("factor(y)", {'y': ButtonGroup?([2, 4, 6, 8])}, CELL_ID)
manipulate("factor(y)", {'y': CheckBox?(checked=10, unchecked=20)}, CELL_ID)
manipulate("factor(p*y)", {'p': TextBox?(100), 'y': Menu([(x-1),x^{2-2*x+1])}, CELL_ID) }
(Note to self: fix the bug of printing the menu using str instead of repr, so x^{2 looks like 2x) }
Changed 15 years ago by
Attachment: | manipulate.patch added |
---|
A different very rough cut of functionality.
comment:13 Changed 15 years ago by
Wow, this is a lot of fun!
Here's another, more interesting example. (This uses a CELL_ID of zero, so the first cell in your notebook will be overwritten.)
x = polygen(ZZ) coeff_range = ButtonGroup([-4,-3,-2,-1,0,1,2,3,4]) manipulate("p = a*x^4 + b*x^3 + c*x^2 + d*x + e; show(plot(p, -4, 4)); show(p); p.roots(ring=RR)", {'a': coeff_range, 'b': coeff_range, 'c': coeff_range, 'd': coeff_range, 'e': coeff_range}, 0)
Just judging from the functionality, I'd be willing to give a positive review once these two issues were addressed. (I haven't read the code yet, though.)
- Having to specify a CELL_ID is lame.
- The API of specifying a dictionary is bad. In the above example, the controls don't appear in the expected order (a,b,c,d,e); but that can't be fixed in manipulate, because it doesn't know what order you used when you typed the dictionary literal. Instead, manipulate should take a list of pairs.
Wishlist items:
- Slider bars would be nice.
- Widgets should be labeled with the variable name they control.
- The syntax is pretty ugly. I haven't thought of a way to fix that without some sort of preparser; here's a first suggestion for a preparser-based syntax:
%manipulate x : Menu([2,4,6,8]) y : Menu([1,10,100,1000]) -- x^y
For extra bonus points, this could work in conjunction with %pari/%magma/etc.
Changed 15 years ago by
Attachment: | manipulate-take3.patch added |
---|
this is independent of the patches above -- it's standalone -- see comments below for how to use.
comment:14 Changed 15 years ago by
This patch manipulate-take3.patch is a completely totally different approach to the whole manipulate thing. It is just a prototype!!! It's not supposed to work perfectly, in particular, you must enter a number first and press return to see anything. Anyway, here are some example inputs (each should be entered in its own cell):
@manipulate def myfactor(n): print jsmath(factor(n)) @manipulate def polys(m): R = QQ['x'] f = R.random_element(m) print "f = ", jsmath(f) print "real roots = ", f.real_roots() show(plot(f)) @manipulate def ellcurve(label): E = EllipticCurve(label) show(E) show("E conductor = %s"%E.conductor()) show(E.q_eigenform(7)) show(plot(E)) @manipulate def pl(n): var('x,y') show(x^n-y^n) show(plot3d(x^n-y^n, (x,-2,2), (y,-2,2)))
comment:15 Changed 15 years ago by
I love the new code by William.
Here is my first example using it:
@manipulate def gcd_steps(numbers): a, b = numbers w = a y = b while True: x, z = divmod(w, y) print '%d = %d * %d + %d' % (w, x, y, z) if not z: break w = y y = z
Changed 15 years ago by
Attachment: | manipulate-take3_part2.patch added |
---|
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.
comment:16 Changed 15 years ago by
Here is the patch that adds full jquery and uijquery support to sage. apply it against the extcode repo.
http://sage.math.washington.edu/home/was/patches/extcode-add_jquery_support.patch
comment:17 Changed 15 years ago by
Examples of how to use the version as of now:
@manipulate def foo(f=text_box("sin(x)"), L=slider(-5,0), U=slider(0,5)): return plot(f,L,U)
@manipulate def bar(a,b=slider([1..10])): return a+b
@manipulate def ec(a,b): E = EllipticCurve([a,b]) html("<h1>Data about an Elliptic Curve</h1>") print E.cremona_label() show(E) show(plot(E)) show(E.q_eigenform(30)) show(E.torsion_order())
Changed 15 years ago by
Attachment: | manipulate_take3_part3.patch added |
---|
this requires that you also install the extcode patch that gives jquery support.
comment:18 Changed 15 years ago by
Examples as of manipulate_take3_part3.patch
@manipulate def factor_example(n=range(2,1000)): F = factor(n) html("<h1 align=center><font color='darkblue' size=+4>factor(%s) = %s</font></h1>"%(n, F)) @manipulate def foo(f, xmax=[0,0.1,..20]): show(f) show(plot(f, -1, xmax)) @manipulate def pl(n=[0..30], xmin=[-5..0], xmax=[0..10]): print n, xmin, xmax f = sin(n*x); g = cos(n*x) show(plot(f,xmin,xmax) + plot(g,xmin,xmax,color='red'), figsize=[5,2], xmin=xmin, xmax=xmax) @manipulate def pl(n=[100,200,..,10^4]): html("<h1 align=center>Primes up to %s</h1>"%n) show(plot(prime_pi, 1, n) + plot(x/(log(x)-1), 0.1, n, color='red')) @manipulate def a3dplot(a=[1..10], b=[1..10]): var('x,y') f = x^a + y^b show(f) show(plot3d(f, (x,-2,2), (y,-2,2)))
comment:19 Changed 15 years ago by
HI: I've put a bundle at
http://sage.math.washington.edu/home/was/patches/manipulate.hg
since people were having trouble applying the plain text patches.
William
comment:20 Changed 15 years ago by
To get this to work, install jquery as follows:
Get
http://sage.math.washington.edu/home/was/patches/jquery.tar.bz2
and unpack it in SAGE_ROOT
comment:21 Changed 15 years ago by
Oops, actually extract
http://sage.math.washington.edu/home/was/patches/jquery.tar.bz2
in SAGE_ROOT/data/
Sorry for all the trouble.
comment:22 Changed 15 years ago by
Cc: | Mike Hansen added |
---|
Changed 15 years ago by
Attachment: | manipulate_take3_part4.patch added |
---|
Changed 15 years ago by
Attachment: | manipulate_take3_part5.patch added |
---|
comment:23 Changed 15 years ago by
Cc: | Timothy Clemans added |
---|
comment:24 Changed 15 years ago by
Owner: | changed from Kelly Boothby to William Stein |
---|---|
Status: | new → assigned |
Summary: | [with alpha patch] interactive widgets in the notebook → interact -- interactive functions in the notebook |
I am now posting the hg bundle for people who want to try this out here:
http://sage.math.washington.edu/home/was/patches/interact.hg
comment:25 Changed 15 years ago by
Description: | modified (diff) |
---|---|
Resolution: | → duplicate |
Status: | assigned → closed |
Summary: | interact -- interactive functions in the notebook → interact -- interactive functions in the notebook (moved to #2449) |
Test to see if CC feature is working.