jp_proxy_widget
¶Can we use
jp_proxy_widget
to conveniently wrap the ev3devsim
components in a Jupyter ipywidget
?
html = '''
<div class="row">
<div>
<select id="map">
<option>Empty Map</tion>
<option>WRO 2020 Regular Junior</option>
<option>WRO 2019 Regular Junior</option>
<option>WRO 2018 Regular Junior</option>
<option>FLL 2019 - City Shaper</option>
<option>FLL 2018 - Into Orbit</option>
<option>Line Following Test</option>
<option>Junction Handling Test</option>
<option>Obstacles Test</option>
<option>Upload Image (2362x1143px)...</option>
<option hidden>Custom Image</option>
</select>
<button id="robotConfiguratorOpen">Configure Robot</button>
<button id="configureObstacles">Obstacles...</button>
</div>
<div class="right">
<div>X : <input type="number" id="xPos"></div>
<div>Y : <input type="number" id="yPos"></div>
<div>Angle : <input type="number" id="angle"></div>
<div><button id="move">Move</button></div>
</div>
</div>
<div class="row">
<div id="editor">
<div class="row buttons">
<div class="leftFlex">
<button id="runCode">Run</button>
<button id="stop">Stop</button>
</div>
<div class="center">
<input type="checkbox" id="largeFont">Large Font
<input type="checkbox" id="longWindow">Long Window
</div>
<div class="rightFlex">
<button id="upload">Load</button>
<button id="download">Save</button>
</div>
</div>
<div id="pythonCode"># Demo of a simple proportional line follower using two sensors
# It's deliberately flawed and will exit with errors in some circumstances;
# try fixing it!
from ev3dev2.motor import MoveSteering, OUTPUT_B, OUTPUT_C
from ev3dev2.sensor import INPUT_1, INPUT_2, INPUT_3, INPUT_4
from ev3dev2.sensor.lego import ColorSensor, GyroSensor, UltrasonicSensor
steering_drive = MoveSteering(OUTPUT_B, OUTPUT_C)
colorLeft = ColorSensor(INPUT_2)
colorRight = ColorSensor(INPUT_3)
gyro = GyroSensor(INPUT_4)
ultrasonic = UltrasonicSensor(INPUT_1)
GAIN = 0.5
while True:
print('Gyro: ' + str(gyro.angle_and_rate))
print('Ultrasonic: ' + str(ultrasonic.distance_centimeters))
error = colorLeft.reflected_light_intensity - colorRight.reflected_light_intensity
correction = error * GAIN
steering_drive.on(correction, 20)
</div>
</div>
<pre id="output"></pre>
</div>
<div id="robotConfigurator" class="closed">
<textarea id="robotConfiguratorEditor" spellcheck="false"></textarea>
<i>* Dimensions are in mm and degrees.</i>
<div class="buttons">
<div class="leftFlex">
<button id="robotConfiguratorApply">Apply</button>
<button id="robotConfiguratorCancel">Cancel</button>
</div>
<div class="rightFlex">
<button id="robotConfiguratorupload">Load</button>
<button id="robotConfiguratordownload">Save</button>
</div>
</div>
</div>
<div id="obstaclesConfigurator" class="closed">
<div class="content">
<div><input type="checkbox" id="showRays">Show ultrasonic rays</div>
<div><input type="checkbox" id="walls" checked>Walls around map</div>
<div><input type="checkbox" id="obstacles" checked>Obstacles</div>
<textarea id="obstaclesConfiguratorEditor" spellcheck="false"></textarea>
</div>
<div class="buttons">
<div class="leftFlex">
<button id="obstaclesConfiguratorApply">Apply</button>
<button id="obstaclesConfiguratorClose">Close</button>
</div>
<div class="rightFlex">
<button id="obstaclesConfiguratorupload">Upload File</button>
<button id="obstaclesConfiguratordownload">Download to File</button>
</div>
</div>
</div>
<div id="window" class="closed">
<div class="content"></div>
<div class="windowRow">
<button id="closeWindow">Close</button>
</div>
</div>
<div id="field"></div>
'''
script = '''
function setPos(x, y, angle) {
var x = parseFloat(x);
var y = parseFloat(y);
var angleRadian = parseFloat(angle) / 180 * Math.PI;
if (isNaN(x)) { x = 0; }
if (isNaN(y)) { y = 0; }
if (isNaN(angleRadian)) { angleRadian = 0; angle = 0; }
document.getElementById('xPos').value = x;
document.getElementById('yPos').value = y;
document.getElementById('angle').value = angle;
sim.setRobotPos(x, y, angleRadian);
sim.drawAll();
}
// Main
var sim = new EV3devSim('field');
setPos(1181, 571, 0);
document.getElementById('map').value = 'Empty Map';
document.getElementById('walls').checked = true;
document.getElementById('obstacles').checked = true;
document.getElementById('configureObstacles').addEventListener('click', function() {
document.getElementById('obstaclesConfigurator').classList.remove('closed');
var obstacles =
'// Obstacles should be configured in json using the following format...\n' +
'//\n' +
'// [\n' +
'// [x, y, width, height],\n' +
'// [x2, y2, width2, height2]\n' +
'// ]\n' +
'// \n' +
'// ...where x/y/width/height are the position and size of the obstacle.\n' +
'// See the "Obstacles Test" map for an example.\n\n';
obstacles += JSON.stringify(sim.obstacles, null, 2);
obstacles = obstacles.replace(/,\n /g, ',').replace(/\[\n /g,'[').replace(/\n \],/g, '],');
document.getElementById('obstaclesConfiguratorEditor').value = obstacles;
});
document.getElementById('obstaclesConfiguratorApply').addEventListener('click', function() {
var json = document.getElementById('obstaclesConfiguratorEditor').value;
json = json.replace(/^\/\/.*\n/gm,'');
var obstacles = JSON.parse(json);
sim.clearObstacles();
sim.clearObstaclesLayer();
sim.loadObstacles(obstacles);
document.getElementById('obstaclesConfigurator').classList.add('closed');
});
document.getElementById('obstaclesConfiguratorClose').addEventListener('click', function() {
document.getElementById('obstaclesConfigurator').classList.add('closed');
});
document.getElementById('obstaclesConfiguratordownload').addEventListener('click', function() {
var obstaclesSpecs = document.getElementById('obstaclesConfiguratorEditor').value
obstaclesSpecs = obstaclesSpecs.replace(/^\/\/.*\n/gm,'');
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:application/json;base64,' + btoa(obstaclesSpecs);
hiddenElement.target = '_blank';
hiddenElement.download = 'obstacles_config.json';
hiddenElement.dispatchEvent(new MouseEvent('click'));
});
document.getElementById('obstaclesConfiguratorupload').addEventListener('click', function() {
var hiddenElement = document.createElement('input');
hiddenElement.type = 'file';
hiddenElement.accept = 'application/json,.json,.js';
hiddenElement.dispatchEvent(new MouseEvent('click'));
hiddenElement.addEventListener('change', function(e){
var reader = new FileReader();
reader.onload = function() {
document.getElementById('obstaclesConfiguratorEditor').value = this.result;
};
reader.readAsText(e.target.files[0]);
});
});
document.getElementById('showRays').addEventListener('click', function() {
if (document.getElementById('showRays').checked) {
sim.drawUltrasonic = true;
} else {
sim.drawUltrasonic = false;
}
sim.drawAll();
});
document.getElementById('walls').addEventListener('click', function() {
if (document.getElementById('walls').checked) {
sim.setWallsPresent(true);
} else {
sim.setWallsPresent(false);
}
});
document.getElementById('obstacles').addEventListener('click', function() {
if (document.getElementById('obstacles').checked) {
sim.obstaclesPresent = true;
sim.clearObstaclesLayer();
sim.drawObstacles();
} else {
sim.obstaclesPresent = false;
sim.clearObstaclesLayer();
}
});
document.getElementById('move').addEventListener('click', function() {
setPos(
document.getElementById('xPos').value,
document.getElementById('yPos').value,
document.getElementById('angle').value
);
});
document.getElementById('stop').addEventListener('click', function() {
sim.stopAnimation();
Sk.hardInterrupt = true;
});
document.getElementById('largeFont').addEventListener('change', function(e) {
if (e.target.checked) {
document.getElementById('editor').classList.add('large');
} else {
document.getElementById('editor').classList.remove('large');
}
});
document.getElementById('longWindow').addEventListener('change', function(e) {
if (e.target.checked) {
document.getElementById('editor').classList.add('long');
} else {
document.getElementById('editor').classList.remove('long');
}
window.dispatchEvent(new Event('resize'));
});
document.getElementById('download').addEventListener('click', function() {
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:text/x-python;base64,' + btoa(editor.getValue());
hiddenElement.target = '_blank';
hiddenElement.download = 'robot.py';
hiddenElement.dispatchEvent(new MouseEvent('click'));
});
document.getElementById('upload').addEventListener('click', function() {
var hiddenElement = document.createElement('input');
hiddenElement.type = 'file';
hiddenElement.accept = 'text/x-python,.py';
hiddenElement.dispatchEvent(new MouseEvent('click'));
hiddenElement.addEventListener('change', function(e){
var reader = new FileReader();
reader.onload = function() {
editor.setValue(this.result);
};
reader.readAsText(e.target.files[0]);
});
});
document.getElementById('map').addEventListener('input', function() {
var map = document.getElementById('map').value;
if (map == 'WRO 2020 Regular Junior') {
sim.loadBackground('WRO-2020-Regular-Junior.png');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(130, 130, 0);
} else if (map == 'WRO 2019 Regular Junior') {
sim.loadBackground('WRO-2019-Regular-Junior.jpg');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(2215, 150, 90);
} else if (map == 'WRO 2018 Regular Junior') {
sim.loadBackground('WRO-2018-Regular-Junior.png');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(1181, 150, 90);
} else if (map == 'FLL 2019 - City Shaper') {
sim.loadBackground('FLL2019.jpg');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(500, 150, 90);
} else if (map == 'FLL 2018 - Into Orbit') {
sim.loadBackground('FLL2018.jpg');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(150, 150, 90);
} else if (map == 'Line Following Test') {
sim.loadBackground('Line_Following_Test.png');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(141, 125, 90);
} else if (map == 'Junction Handling Test') {
sim.loadBackground('Junction_Handling_Test.png');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(698, 130, 90);
} else if (map == 'Obstacles Test') {
sim.loadBackground('Obstacles_Test.png');
setPos(121, 125, 90);
sim.clearObstacles();
sim.clearObstaclesLayer();
sim.loadObstacles([
[46, 388, 150, 150],
[479, 704, 150, 150],
[852, 388, 150, 150],
[1374, 388, 150, 150],
[1758, 900, 150, 150],
[2126, 388, 150, 150]
]);
} else if (map == 'Upload Image (2362x1143px)...') {
console.log('upload');
var hiddenElement = document.createElement('input');
hiddenElement.type = 'file';
hiddenElement.accept = 'image/*';
console.log(hiddenElement);
hiddenElement.dispatchEvent(new MouseEvent('click'));
hiddenElement.addEventListener('change', function(e){
var reader = new FileReader();
reader.onload = function() {
sim.loadBackground(this.result);
sim.clearObstacles();
sim.clearObstaclesLayer();
};
reader.readAsDataURL(e.target.files[0]);
});
var select = document.getElementById('map');
select.selectedIndex = select.options.length - 1;
} else {
sim.clearBackground();
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(2362/2, 1143/2, 0);
}
});
document.getElementById('robotConfiguratorOpen').addEventListener('click', function() {
document.getElementById('robotConfiguratorEditor').value = JSON.stringify(sim.robotSpecs, null, 2);
document.getElementById('robotConfigurator').classList.remove('closed');
});
document.getElementById('robotConfiguratorCancel').addEventListener('click', function() {
document.getElementById('robotConfigurator').classList.add('closed');
});
document.getElementById('robotConfiguratorApply').addEventListener('click', function() {
var robotSpecs = JSON.parse(document.getElementById('robotConfiguratorEditor').value);
sim.loadRobot(robotSpecs);
sim.drawAll();
document.getElementById('robotConfigurator').classList.add('closed');
});
document.getElementById('robotConfiguratordownload').addEventListener('click', function() {
var robotSpecs = document.getElementById('robotConfiguratorEditor').value
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:application/json;base64,' + btoa(robotSpecs);
hiddenElement.target = '_blank';
hiddenElement.download = 'robot_config.json';
hiddenElement.dispatchEvent(new MouseEvent('click'));
});
document.getElementById('robotConfiguratorupload').addEventListener('click', function() {
var hiddenElement = document.createElement('input');
hiddenElement.type = 'file';
hiddenElement.accept = 'application/json,.json,.js';
hiddenElement.dispatchEvent(new MouseEvent('click'));
hiddenElement.addEventListener('change', function(e){
var reader = new FileReader();
reader.onload = function() {
document.getElementById('robotConfiguratorEditor').value = this.result;
};
reader.readAsText(e.target.files[0]);
});
});
function openWindow(url) {
var win = document.getElementById('window');
var content = win.getElementsByClassName('content')[0];
win.classList.remove('closed');
content.innerHTML = '';
fetch(url)
.then(function(response) {
response.text()
.then(function(text) {
content.innerHTML = text;
});
})
}
document.getElementById('guideWindow').addEventListener('click', function() {
openWindow('guide.html');
});
document.getElementById('aboutWindow').addEventListener('click', function() {
openWindow('about.html');
});
document.getElementById('closeWindow').addEventListener('click', function() {
document.getElementById('window').classList.add('closed');
});
// Load Ace editor
var editor = ace.edit("pythonCode");
editor.setTheme("ace/theme/monokai");
editor.session.setMode("ace/mode/python");
// Load Skulpt
function outf(text) {
var mypre = document.getElementById("output");
mypre.innerHTML = mypre.innerHTML + text;
mypre.scrollTop = mypre.scrollHeight - mypre.clientHeight;
}
function builtinRead(x) {
var externalLibs = {
'./ev3dev2/__init__.py': 'ev3dev2/__init__.py',
'./ev3dev2/motor.py': 'ev3dev2/motor.py',
'./ev3dev2/sensor/__init__.py': 'ev3dev2/sensor/__init__.py',
'./ev3dev2/sensor/lego.py': 'ev3dev2/sensor/lego.py',
'./ev3dev2_glue.js': 'ev3dev2_glue.js'
}
if (Sk.builtinFiles === undefined || Sk.builtinFiles["files"][x] === undefined) {
if (x in externalLibs) {
return Sk.misceval.promiseToSuspension(
fetch(externalLibs[x])
.then(r => r.text())
);
} else {
throw "File not found: '" + x + "'";
}
}
return Sk.builtinFiles["files"][x];
}
var interruptHandler = function (susp) {
if (Sk.hardInterrupt === true) {
delete Sk.hardInterrupt;
throw new Sk.builtin.ExternalError('aborted execution');
} else {
return null;
}
};
function runit() {
if (typeof Sk.hardInterrupt != 'undefined') {
delete Sk.hardInterrupt;
}
if (Sk.running == true) {
return;
}
Sk.running = true;
sim.reset();
sim.startAnimation();
var prog = editor.getValue();
var mypre = document.getElementById("output");
mypre.innerHTML = '';
Sk.pre = "output";
Sk.configure({output:outf, read:builtinRead});
var myPromise = Sk.misceval.asyncToPromise(
function() {
return Sk.importMainWithBody("<stdin>", false, prog, true);
},
{
'*': interruptHandler
}
);
myPromise.then(
function(mod) {
sim.stopAnimation();
Sk.running = false;
},
function(err) {
Sk.running = false;
sim.stopAnimation();
var mypre = document.getElementById("output");
mypre.innerHTML = mypre.innerHTML + '<span class="error">' + err.toString() + '</span>';
mypre.scrollTop = mypre.scrollHeight - mypre.clientHeight;
}
);
}
document.getElementById('runCode').addEventListener('click', runit);
'''
# regular expressions for stripping comment lines removed
script_base = '''
function setPos(x, y, angle) {
var x = parseFloat(x);
var y = parseFloat(y);
var angleRadian = parseFloat(angle) / 180 * Math.PI;
if (isNaN(x)) { x = 0; }
if (isNaN(y)) { y = 0; }
if (isNaN(angleRadian)) { angleRadian = 0; angle = 0; }
document.getElementById('xPos').value = x;
document.getElementById('yPos').value = y;
document.getElementById('angle').value = angle;
sim.setRobotPos(x, y, angleRadian);
sim.drawAll();
}
var sim = new EV3devSim('field');
setPos(1181, 571, 0);
document.getElementById('map').value = 'Empty Map';
document.getElementById('walls').checked = true;
document.getElementById('obstacles').checked = true;
document.getElementById('configureObstacles').addEventListener('click', function() {
document.getElementById('obstaclesConfigurator').classList.remove('closed');
var obstacles = JSON.stringify(sim.obstacles, null, 2);
document.getElementById('obstaclesConfiguratorEditor').value = obstacles;
});
document.getElementById('obstaclesConfiguratorApply').addEventListener('click', function() {
var json = document.getElementById('obstaclesConfiguratorEditor').value;
var obstacles = JSON.parse(json);
sim.clearObstacles();
sim.clearObstaclesLayer();
sim.loadObstacles(obstacles);
document.getElementById('obstaclesConfigurator').classList.add('closed');
});
document.getElementById('obstaclesConfiguratorClose').addEventListener('click', function() {
document.getElementById('obstaclesConfigurator').classList.add('closed');
});
document.getElementById('obstaclesConfiguratordownload').addEventListener('click', function() {
var obstaclesSpecs = document.getElementById('obstaclesConfiguratorEditor').value
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:application/json;base64,' + btoa(obstaclesSpecs);
hiddenElement.target = '_blank';
hiddenElement.download = 'obstacles_config.json';
hiddenElement.dispatchEvent(new MouseEvent('click'));
});
document.getElementById('obstaclesConfiguratorupload').addEventListener('click', function() {
var hiddenElement = document.createElement('input');
hiddenElement.type = 'file';
hiddenElement.accept = 'application/json,.json,.js';
hiddenElement.dispatchEvent(new MouseEvent('click'));
hiddenElement.addEventListener('change', function(e){
var reader = new FileReader();
reader.onload = function() {
document.getElementById('obstaclesConfiguratorEditor').value = this.result;
};
reader.readAsText(e.target.files[0]);
});
});
document.getElementById('showRays').addEventListener('click', function() {
if (document.getElementById('showRays').checked) {
sim.drawUltrasonic = true;
} else {
sim.drawUltrasonic = false;
}
sim.drawAll();
});
document.getElementById('walls').addEventListener('click', function() {
if (document.getElementById('walls').checked) {
sim.setWallsPresent(true);
} else {
sim.setWallsPresent(false);
}
});
document.getElementById('obstacles').addEventListener('click', function() {
if (document.getElementById('obstacles').checked) {
sim.obstaclesPresent = true;
sim.clearObstaclesLayer();
sim.drawObstacles();
} else {
sim.obstaclesPresent = false;
sim.clearObstaclesLayer();
}
});
document.getElementById('move').addEventListener('click', function() {
setPos(
document.getElementById('xPos').value,
document.getElementById('yPos').value,
document.getElementById('angle').value
);
});
document.getElementById('stop').addEventListener('click', function() {
sim.stopAnimation();
Sk.hardInterrupt = true;
});
document.getElementById('largeFont').addEventListener('change', function(e) {
if (e.target.checked) {
document.getElementById('editor').classList.add('large');
} else {
document.getElementById('editor').classList.remove('large');
}
});
document.getElementById('longWindow').addEventListener('change', function(e) {
if (e.target.checked) {
document.getElementById('editor').classList.add('long');
} else {
document.getElementById('editor').classList.remove('long');
}
window.dispatchEvent(new Event('resize'));
});
document.getElementById('download').addEventListener('click', function() {
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:text/x-python;base64,' + btoa(editor.getValue());
hiddenElement.target = '_blank';
hiddenElement.download = 'robot.py';
hiddenElement.dispatchEvent(new MouseEvent('click'));
});
document.getElementById('upload').addEventListener('click', function() {
var hiddenElement = document.createElement('input');
hiddenElement.type = 'file';
hiddenElement.accept = 'text/x-python,.py';
hiddenElement.dispatchEvent(new MouseEvent('click'));
hiddenElement.addEventListener('change', function(e){
var reader = new FileReader();
reader.onload = function() {
editor.setValue(this.result);
};
reader.readAsText(e.target.files[0]);
});
});
sim.loadBackground('WRO-2020-Regular-Junior.png');
document.getElementById('map').addEventListener('input', function() {
var map = document.getElementById('map').value;
if (map == 'WRO 2020 Regular Junior') {
sim.loadBackground('WRO-2020-Regular-Junior.png');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(130, 130, 0);
} else if (map == 'WRO 2019 Regular Junior') {
sim.loadBackground('WRO-2019-Regular-Junior.jpg');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(2215, 150, 90);
} else if (map == 'WRO 2018 Regular Junior') {
sim.loadBackground('WRO-2018-Regular-Junior.png');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(1181, 150, 90);
} else if (map == 'FLL 2019 - City Shaper') {
sim.loadBackground('FLL2019.jpg');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(500, 150, 90);
} else if (map == 'FLL 2018 - Into Orbit') {
sim.loadBackground('FLL2018.jpg');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(150, 150, 90);
} else if (map == 'Line Following Test') {
sim.loadBackground('Line_Following_Test.png');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(141, 125, 90);
} else if (map == 'Junction Handling Test') {
sim.loadBackground('Junction_Handling_Test.png');
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(698, 130, 90);
} else if (map == 'Obstacles Test') {
sim.loadBackground('Obstacles_Test.png');
setPos(121, 125, 90);
sim.clearObstacles();
sim.clearObstaclesLayer();
sim.loadObstacles([
[46, 388, 150, 150],
[479, 704, 150, 150],
[852, 388, 150, 150],
[1374, 388, 150, 150],
[1758, 900, 150, 150],
[2126, 388, 150, 150]
]);
} else if (map == 'Upload Image (2362x1143px)...') {
console.log('upload');
var hiddenElement = document.createElement('input');
hiddenElement.type = 'file';
hiddenElement.accept = 'image/*';
console.log(hiddenElement);
hiddenElement.dispatchEvent(new MouseEvent('click'));
hiddenElement.addEventListener('change', function(e){
var reader = new FileReader();
reader.onload = function() {
sim.loadBackground(this.result);
sim.clearObstacles();
sim.clearObstaclesLayer();
};
reader.readAsDataURL(e.target.files[0]);
});
var select = document.getElementById('map');
select.selectedIndex = select.options.length - 1;
} else {
sim.clearBackground();
sim.clearObstacles();
sim.clearObstaclesLayer();
setPos(2362/2, 1143/2, 0);
}
});
document.getElementById('robotConfiguratorOpen').addEventListener('click', function() {
document.getElementById('robotConfiguratorEditor').value = JSON.stringify(sim.robotSpecs, null, 2);
document.getElementById('robotConfigurator').classList.remove('closed');
});
document.getElementById('robotConfiguratorCancel').addEventListener('click', function() {
document.getElementById('robotConfigurator').classList.add('closed');
});
document.getElementById('robotConfiguratorApply').addEventListener('click', function() {
var robotSpecs = JSON.parse(document.getElementById('robotConfiguratorEditor').value);
sim.loadRobot(robotSpecs);
sim.drawAll();
document.getElementById('robotConfigurator').classList.add('closed');
});
document.getElementById('robotConfiguratordownload').addEventListener('click', function() {
var robotSpecs = document.getElementById('robotConfiguratorEditor').value
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:application/json;base64,' + btoa(robotSpecs);
hiddenElement.target = '_blank';
hiddenElement.download = 'robot_config.json';
hiddenElement.dispatchEvent(new MouseEvent('click'));
});
document.getElementById('robotConfiguratorupload').addEventListener('click', function() {
var hiddenElement = document.createElement('input');
hiddenElement.type = 'file';
hiddenElement.accept = 'application/json,.json,.js';
hiddenElement.dispatchEvent(new MouseEvent('click'));
hiddenElement.addEventListener('change', function(e){
var reader = new FileReader();
reader.onload = function() {
document.getElementById('robotConfiguratorEditor').value = this.result;
};
reader.readAsText(e.target.files[0]);
});
});
function openWindow(url) {
var win = document.getElementById('window');
var content = win.getElementsByClassName('content')[0];
win.classList.remove('closed');
content.innerHTML = '';
fetch(url)
.then(function(response) {
response.text()
.then(function(text) {
content.innerHTML = text;
});
})
}
// Load Ace editor
var editor = ace.edit("pythonCode");
editor.setTheme("ace/theme/monokai");
editor.session.setMode("ace/mode/python");
// Load Skulpt
function outf(text) {
var mypre = document.getElementById("output");
mypre.innerHTML = mypre.innerHTML + text;
mypre.scrollTop = mypre.scrollHeight - mypre.clientHeight;
}
function builtinRead(x) {
var externalLibs = {
'./ev3dev2/__init__.py': 'ev3dev2/__init__.py',
'./ev3dev2/motor.py': 'ev3dev2/motor.py',
'./ev3dev2/sensor/__init__.py': 'ev3dev2/sensor/__init__.py',
'./ev3dev2/sensor/lego.py': 'ev3dev2/sensor/lego.py',
'./ev3dev2_glue.js': 'ev3dev2_glue.js'
}
if (Sk.builtinFiles === undefined || Sk.builtinFiles["files"][x] === undefined) {
if (x in externalLibs) {
alert("load "+x)
return Sk.misceval.promiseToSuspension(
fetch(externalLibs[x])
.then(r => r.text())
);
} else {
throw "File not found: '" + x + "'";
}
}
return Sk.builtinFiles["files"][x];
}
var interruptHandler = function (susp) {
if (Sk.hardInterrupt === true) {
delete Sk.hardInterrupt;
throw new Sk.builtin.ExternalError('aborted execution');
} else {
return null;
}
};
function runit() {
if (typeof Sk.hardInterrupt != 'undefined') {
delete Sk.hardInterrupt;
}
if (Sk.running == true) {
return;
}
Sk.running = true;
sim.reset();
sim.startAnimation();
var prog = editor.getValue();
var mypre = document.getElementById("output");
mypre.innerHTML = '';
Sk.pre = "output";
Sk.configure({output:outf, read:builtinRead});
var myPromise = Sk.misceval.asyncToPromise(
function() {
return Sk.importMainWithBody("<stdin>", false, prog, true);
},
{
'*': interruptHandler
}
);
myPromise.then(
function(mod) {
sim.stopAnimation();
Sk.running = false;
},
function(err) {
Sk.running = false;
sim.stopAnimation();
var mypre = document.getElementById("output");
mypre.innerHTML = mypre.innerHTML + '<span class="error">' + err.toString() + '</span>';
mypre.scrollTop = mypre.scrollHeight - mypre.clientHeight;
}
);
}
document.getElementById('runCode').addEventListener('click', runit);
'''
The following doesnlt work, I think because Skulpt is confused trying to load in the external Python packages. Not sure how that can be addressed? Or whether the ev3dev packages and the glue can all be preloaded into Skulpt prior to each terminal programme execution.
This might actually be more useful for 101 use case because then there will be no requirement to load in ev3dev packages explicitly?
import jp_proxy_widget
from jp_proxy_widget import js_context
from IPython.display import HTML, display
test1 = jp_proxy_widget.JSProxyWidget()
e = test1.element
# Call some standard jQuery method on the widget element:
e.empty()
e.width("1000px")
e.height("1000px")
test1.load_js_files(["skulpt.min.js", "skulpt-stdlib.js",
"EV3devSim.js"])
test1.require_js("ace/ace", "ace-src-min/ace.js")
test1.load_css("main.css")
e.html(html)
test1.js_init(script_base)
#test1.js_init(script_everything_else)
#test1.js_init(script)
display(test1)
JSProxyWidget(status='Not yet rendered')