Skip to content

Commit

Permalink
Implement readonly and locations parameters to interacts
Browse files Browse the repository at this point in the history
readonly=True immediately disables the controls, so they can only be changed programmatically

locations is a dict that specifies, for each variable, a jQuery selector in which to put the control.  The special "_output" control can also be specified.  Any controls not named are automatically added to the default layout.  To work correctly, the jQuery selectors should be empty divs or spans that are children of an element with class "sagecell".  The locations will be emptied before the controls are created.  The idea of the locations parameter is to make it easy to have custom layouts for the controls and outputs, possibly separated all over a page.
  • Loading branch information
Jason Grout committed Aug 3, 2013
1 parent 56f508e commit 377b769
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 30 deletions.
2 changes: 1 addition & 1 deletion dynamic.py
Expand Up @@ -69,7 +69,7 @@ def dynamic_expression(v, vars):
sage: dynamic_expression('2*t','t')
"""
# control
@interact(output=False)
@interact(output=False, readonly=True)
def f(t=(0,2)):
pass

Expand Down
32 changes: 19 additions & 13 deletions interact_sagecell.py
Expand Up @@ -250,7 +250,7 @@ def __repr__(self):
pass

@decorator_defaults
def interact(f, controls=[], update=None, layout=None, output=True):
def interact(f, controls=[], update=None, layout=None, locations=None, output=True, readonly=False):
"""
A decorator that creates an interact.
Expand Down Expand Up @@ -327,9 +327,8 @@ def f(name3, name4=control4, name5=control5, **kwargs):
update = names
for n in update:
controls[n].update = True
if layout is None:
layout = [[(n, 1)] for n in names]
elif isinstance(layout, dict):

if isinstance(layout, dict):
rows = []
rows.extend(layout.get("top", []))
for pos, ctrls in layout.iteritems():
Expand All @@ -339,17 +338,22 @@ def f(name3, name4=control4, name5=control5, **kwargs):
rows.append([("_output",1)])
rows.extend(layout.get("bottom", []))
layout = rows
elif layout is None:
layout = []

placed = set()
for r in layout:
for i, c in enumerate(r):
if not isinstance(c, (list, tuple)):
c = (c, 1)
r[i] = c = (c[0], int(c[1]))
if c[0] is not None:
if c[0] in placed:
raise ValueError("duplicate item %s in layout" % (c[0],))
placed.add(c[0])
if locations:
placed.update(locations.keys())
if layout:
for r in layout:
for i, c in enumerate(r):
if not isinstance(c, (list, tuple)):
c = (c, 1)
r[i] = c = (c[0], int(c[1]))
if c[0] is not None:
if c[0] in placed:
raise ValueError("duplicate item %s in layout" % (c[0],))
placed.add(c[0])
layout.extend([(n, 1)] for n in names if n not in placed)
if output and "_output" not in placed:
layout.append([("_output", 1)])
Expand All @@ -364,6 +368,8 @@ def f(name3, name4=control4, name5=control5, **kwargs):
"new_interact_id": interact_id,
"controls": msgs,
"layout": layout,
"locations": locations,
"readonly": readonly,
},
"text/plain": "Sage Interact"
}
Expand Down
48 changes: 32 additions & 16 deletions static/compute_server.js
Expand Up @@ -619,6 +619,7 @@ sagecell.InteractCell = function (session, data, parent_block) {
this.controls = {};
this.session = session;
this.layout = data.layout;
this.locations = data.locations;
this.msg_id = data.msg_id;
this.changed = [];

Expand All @@ -630,6 +631,7 @@ sagecell.InteractCell = function (session, data, parent_block) {
}
this.renderCanvas(parent_block);
this.bindChange();
if (data.readonly) {this.disable();}
}

sagecell.InteractCell.prototype.newControl = function (data) {
Expand Down Expand Up @@ -722,32 +724,46 @@ sagecell.InteractCell.prototype.placeControl = function (name) {
sagecell.InteractCell.prototype.renderCanvas = function (parent_block) {
this.cells = {}
this.container = ce("div", {"class": "sagecell_interactContainer"});
for (var row = 0; row < this.layout.length; row++) {
var rdiv = ce("div");
var total = 0;
for (var col = 0; col < this.layout[row].length; col++) {
total += this.layout[row][col][1];
if (this.layout && this.layout.length>0) {
for (var row = 0; row < this.layout.length; row++) {
var rdiv = ce("div");
var total = 0;
for (var col = 0; col < this.layout[row].length; col++) {
total += this.layout[row][col][1];
}
for (var col = 0; col < this.layout[row].length; col++) {
var cdiv = ce("div", {"class": "sagecell_interactControlCell"});
cdiv.style.width = 100 * this.layout[row][col][1] / total + "%";
if (this.layout[row][col] !== undefined) {
this.cells[this.layout[row][col][0]] = cdiv;
if (this.layout[row][col][0] === "_output") {
this.output_block = ce("div", {"class": "sagecell_interactOutput"});
cdiv.appendChild(this.output_block);
}
}
rdiv.appendChild(cdiv);
}
this.container.appendChild(rdiv);
}
for (var col = 0; col < this.layout[row].length; col++) {
var cdiv = ce("div", {"class": "sagecell_interactControlCell"});
cdiv.style.width = 100 * this.layout[row][col][1] / total + "%";
if (this.layout[row][col] !== undefined) {
this.cells[this.layout[row][col][0]] = cdiv;
if (this.layout[row][col][0] === "_output") {
this.output_block = ce("div", {"class": "sagecell_interactOutput"});
cdiv.appendChild(this.output_block);
}
if (this.locations) {
for (var name in this.locations) {
if (this.locations.hasOwnProperty(name)) {
this.cells[name] = $("body").find(this.locations[name]).slice(0,1).empty()[0];
if (name==="_output") {
this.output_block = this.cells[name];
}
}
rdiv.appendChild(cdiv);
}
this.container.appendChild(rdiv);
}
for (var name in this.controls) {
if (this.controls.hasOwnProperty(name)) {
this.placeControl(name);
}
}
this.session.output(this.container, parent_block);
if (this.layout && this.layout.length>0) {
this.session.output(this.container, parent_block);
}
}

sagecell.InteractCell.prototype.updateControl = function (data) {
Expand Down

0 comments on commit 377b769

Please sign in to comment.